Merge mozilla-central and mozilla-inbound
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 10 Nov 2011 11:40:00 +0100
changeset 80128 9ce43912891b1ac14933d2a2c2285d43b989cfa1
parent 80065 c60535115ea1078a28317e9621fa8f3b5b268a32 (current diff)
parent 80127 46b40e2c1953fc7bae594382f53e334e7b31abbd (diff)
child 80129 a41fa578cfc111f8d63a2ca38d0293f8cbde54c7
child 80136 0fb81504b0aa150982e9cfe762e2f4eb1f926bd5
push id21462
push usermak77@bonardo.net
push dateThu, 10 Nov 2011 10:40:54 +0000
treeherdermozilla-central@9ce43912891b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone11.0a1
first release with
nightly win64
9ce43912891b / 11.0a1 / 20111110031403 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Merge mozilla-central and mozilla-inbound
layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
layout/reftests/svg/as-image/img-foreignObject-embed-1.html
layout/reftests/svg/as-image/img-foreignObject-embed-2-helper.svg
layout/reftests/svg/as-image/img-foreignObject-embed-2.html
--- a/browser/components/safebrowsing/content/malware-warden.js
+++ b/browser/components/safebrowsing/content/malware-warden.js
@@ -52,24 +52,24 @@ function PROT_MalwareWarden() {
     this.prefs_.getPref(kMalwareWardenEnabledPref, null);
 
   // Get notifications when the malware warden enabled pref changes
   var malwareWardenPrefObserver =
     BindToObject(this.onMalwareWardenEnabledPrefChanged, this);
   this.prefs_.addObserver(kMalwareWardenEnabledPref, malwareWardenPrefObserver);
 
   // Add a test chunk to the database
-  var testData = "mozilla.com/firefox/its-an-attack.html";
+  var testData = "mozilla.org/firefox/its-an-attack.html";
 
   var testUpdate =
     "n:1000\ni:test-malware-simple\nad:1\n" +
     "a:1:32:" + testData.length + "\n" +
     testData;
     
-  testData = "mozilla.com/firefox/its-a-trap.html";
+  testData = "mozilla.org/firefox/its-a-trap.html";
   testUpdate +=
     "n:1000\ni:test-phish-simple\nad:1\n" +
     "a:1:32:" + testData.length + "\n" +
     testData;
 
   var dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
                    .getService(Ci.nsIUrlClassifierDBService);
 
--- a/browser/components/safebrowsing/content/test/browser_bug400731.js
+++ b/browser/components/safebrowsing/content/test/browser_bug400731.js
@@ -3,32 +3,32 @@
 function test() {
   waitForExplicitFinish();
   
   gBrowser.selectedTab = gBrowser.addTab();
   
   // Navigate to malware site.  Can't use an onload listener here since
   // error pages don't fire onload
   window.addEventListener("DOMContentLoaded", testMalware, true);
-  content.location = "http://www.mozilla.com/firefox/its-an-attack.html";
+  content.location = "http://www.mozilla.org/firefox/its-an-attack.html";
 }
 
 function testMalware() {
   window.removeEventListener("DOMContentLoaded", testMalware, true);
 
   // Confirm that "Ignore this warning" is visible - bug 422410
   var el = content.document.getElementById("ignoreWarningButton");
   ok(el, "Ignore warning button should be present for malware");
   
   var style = content.getComputedStyle(el, null);
   is(style.display, "inline", "Ignore Warning button should be display:inline for malware");
   
   // Now launch the phishing test
   window.addEventListener("DOMContentLoaded", testPhishing, true);
-  content.location = "http://www.mozilla.com/firefox/its-a-trap.html";
+  content.location = "http://www.mozilla.org/firefox/its-a-trap.html";
 }
 
 function testPhishing() {
   window.removeEventListener("DOMContentLoaded", testPhishing, true);
   
   var el = content.document.getElementById("ignoreWarningButton");
   ok(el, "Ignore warning button should be present for phishing");
   
--- a/browser/components/safebrowsing/content/test/browser_bug415846.js
+++ b/browser/components/safebrowsing/content/test/browser_bug415846.js
@@ -32,17 +32,17 @@ function testNormal_PopupListener() {
   var reportMenu = document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
   var errorMenu = document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
   is(reportMenu.hidden, false, "Report phishing menu should be visible on normal sites");
   is(errorMenu.hidden, true, "Report error menu item should be hidden on normal sites");
   menu.hidePopup();
   
   // Now launch the phishing test.  Can't use onload here because error pages don't
   // fire normal load events.
-  content.location = "http://www.mozilla.com/firefox/its-a-trap.html";
+  content.location = "http://www.mozilla.org/firefox/its-a-trap.html";
   setTimeout(testPhishing, 2000);
 }
 
 function testPhishing() {
   menu.addEventListener("popupshown", testPhishing_PopupListener, false);
   menu.openPopup(null, "", 0, 0, false, null);
 }
 
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -413,16 +413,17 @@ user_pref("security.warn_submit_insecure
 user_pref("browser.shell.checkDefaultBrowser", false);
 user_pref("shell.checkDefaultClient", false);
 user_pref("browser.warnOnQuit", false);
 user_pref("accessibility.typeaheadfind.autostart", false);
 user_pref("javascript.options.showInConsole", true);
 user_pref("devtools.errorconsole.enabled", true);
 user_pref("layout.debug.enable_data_xbl", true);
 user_pref("browser.EULA.override", true);
+user_pref("javascript.options.jit_hardening", true);
 user_pref("gfx.color_management.force_srgb", true);
 user_pref("network.manage-offline-status", false);
 user_pref("test.mousescroll", true);
 user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs
 user_pref("network.http.prompt-temp-redirect", false);
 user_pref("media.cache_size", 100);
 user_pref("security.warn_viewing_mixed", false);
 user_pref("app.update.enabled", false);
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -251,21 +251,21 @@ class DeviceManagerADB(DeviceManager):
     return outputFile;
 
   # external function
   # returns:
   #  success: output from testagent
   #  failure: None
   def killProcess(self, appname):
     procs = self.getProcessList()
-    for proc in procs:
-      if (proc[1] == appname):
-        p = self.runCmd(["shell", "ps"])
+    for (pid, name, user) in procs:
+      if name == appname:
+        p = self.runCmdAs(["shell", "kill", pid])
         return p.stdout.read()
-      return None
+    return None
 
   # external function
   # returns:
   #  success: filecontents
   #  failure: None
   def catFile(self, remoteFile):
     #p = self.runCmd(["shell", "cat", remoteFile])
     #return p.stdout.read()
@@ -501,16 +501,22 @@ class DeviceManagerADB(DeviceManager):
       ret["systime"] = self.runCmd(["shell", "date"]).stdout.read()
     print ret
     return ret
 
   def runCmd(self, args):
     args.insert(0, "adb")
     return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
+  def runCmdAs(self, args):
+    if self.useRunAs:
+      args.insert(1, "run-as")
+      args.insert(2, self.packageName)
+    return self.runCmd(args)
+
   def checkCmd(self, args):
     args.insert(0, "adb")
     return subprocess.check_call(args)
 
   def checkCmdAs(self, args):
     if (self.useRunAs):
       args.insert(1, "run-as")
       args.insert(2, self.packageName)
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -130,17 +130,17 @@ https://requestclientcert.example.com:44
 https://requireclientcert.example.com:443         privileged,clientauth=require
 
 # This is here so that we don't load the default live bookmark over
 # the network in every test suite.
 http://fxfeeds.mozilla.com:80
 
 # Prevent safebrowsing tests from hitting the network for its-a-trap.html and
 # its-an-attack.html.
-http://www.mozilla.com:80
+http://www.mozilla.org:80
 
 #
 # These are subdomains of <ält.example.org>.
 #
 http://sub1.xn--lt-uia.example.org:8000   privileged
 http://sub2.xn--lt-uia.example.org:80     privileged
 http://xn--exmple-cua.test:80             privileged
 http://sub1.xn--exmple-cua.test:80        privileged
--- a/configure.in
+++ b/configure.in
@@ -1839,16 +1839,26 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align"
            ;;
        esac
     fi
 
     _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/mozilla-config.h'
     _USE_CPP_INCLUDE_FLAG=1
 
+    # Recent clang and gcc support C++11 deleted functions without warnings if
+    # compiling with -std=c++0x or -std=gnu++0x (or c++11 or gnu++11 in very new
+    # versions).  We can't use -std=c++0x yet, so gcc's support must remain
+    # unused.  But clang's warning can be disabled, so when compiling with clang
+    # we use it to opt out of the warning, enabling (macro-encapsulated) use of
+    # deleted function syntax.
+    if test "$CLANG_CXX"; then
+        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-c++0x-extensions"
+    fi
+
     AC_CACHE_CHECK(whether the compiler supports -Wno-invalid-offsetof,
                    ac_has_wno_invalid_offsetof,
         [
             AC_LANG_SAVE
             AC_LANG_CPLUSPLUS
             _SAVE_CXXFLAGS="$CXXFLAGS"
             CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-invalid-offsetof"
             AC_TRY_COMPILE([],
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/700090-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var mo = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mo");
+  var t1 = document.createTextNode("123456 ");
+  mo.appendChild(t1);
+  document.body.appendChild(mo);
+  var t2 = document.createTextNode("x");
+  document.body.appendChild(t2);
+
+  var r1 = document.createRange();
+  r1.setEnd(t1, 7);
+  var r3 = document.createRange();
+  r3.setStart(t1, 7);
+
+  document.documentElement.offsetHeight;
+
+  var r2 = document.createRange();
+  r2.setStart(t1, 0);
+  r2.setEnd(t2, 0);
+  r2.deleteContents();
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/700090-2.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var mo = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mo");
+  var t1 = document.createTextNode("123456 ");
+  mo.appendChild(t1);
+  document.body.appendChild(mo);
+  var t2 = document.createTextNode("x");
+  document.body.appendChild(t2);
+
+  var r1 = document.createRange();
+  r1.setEnd(t1, 7);
+  var r3 = document.createRange();
+  r3.setStart(t1, 7);
+
+  document.documentElement.offsetHeight;
+
+  t1.splitText(t1.length);
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -93,8 +93,10 @@ load 642022-1.html
 load 646184.html
 load 658845-1.svg
 load 667336-1.html
 load 679459.html
 load 679689-1.html
 load 682463.html
 load 693212.xhtml
 load 698974-1.html
+load 700090-1.html
+load 700090-2.html
--- a/content/base/public/nsIImageLoadingContent.idl
+++ b/content/base/public/nsIImageLoadingContent.idl
@@ -37,16 +37,17 @@
 
 #include "imgIDecoderObserver.idl"
 
 interface imgIRequest;
 interface nsIChannel;
 interface nsIStreamListener;
 interface nsIURI;
 interface nsIDocument;
+interface nsIFrame;
 
 /**
  * This interface represents a content node that loads images.  The interface
  * exists to allow getting information on the images that the content node
  * loads and to allow registration of observers for the image loads.
  *
  * Implementors of this interface should handle all the mechanics of actually
  * loading an image -- getting the URI, checking with content policies and
@@ -60,17 +61,17 @@ interface nsIDocument;
  * observers to check which request they are getting notifications for.
  *
  * Observers added in mid-load will not get any notifications they
  * missed.  We should NOT freeze this interface without considering
  * this issue.  (It could be that the image status on imgIRequest is
  * sufficient, when combined with the imageBlockingStatus information.)
  */
 
-[scriptable, uuid(95c74255-df9a-4060-b5a0-0d111fcafe08)]
+[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)]
 interface nsIImageLoadingContent : imgIDecoderObserver
 {
   /**
    * Request types.  Image loading content nodes attempt to do atomic
    * image changes when the image url is changed.  This means that
    * when the url changes the new image load will start, but the old
    * image will remain the "current" request until the new image is
    * fully loaded.  At that point, the old "current" request will be
@@ -124,16 +125,28 @@ interface nsIImageLoadingContent : imgID
    * is thrown)
    *
    * @throws NS_ERROR_UNEXPECTED if the request type requested is not
    * known
    */
   imgIRequest getRequest(in long aRequestType);
 
   /**
+   * Used to notify the image loading content node that a frame has been
+   * created.
+   */
+  [notxpcom] void frameCreated(in nsIFrame aFrame);
+
+  /**
+   * Used to notify the image loading content node that a frame has been
+   * destroyed.
+   */
+  [notxpcom] void frameDestroyed(in nsIFrame aFrame);
+
+  /**
    * Used to find out what type of request one is dealing with (eg
    * which request got passed through to the imgIDecoderObserver
    * interface of an observer)
    *
    * @param aRequest the request whose type we want to know
    *
    * @return an enum value saying what type this request is
    *
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3217,16 +3217,17 @@ nsIDocument::TakeAnimationFrameListeners
 
 void
 nsDocument::DeleteShell()
 {
   mExternalResourceMap.HideViewers();
   if (IsEventHandlingEnabled()) {
     RevokeAnimationFrameNotifications();
   }
+
   mPresShell = nsnull;
 }
 
 void
 nsDocument::RevokeAnimationFrameNotifications()
 {
   if (mHavePendingPaint) {
     mPresShell->GetPresContext()->RefreshDriver()->RevokeBeforePaintEvent(this);
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1376,18 +1376,18 @@ nsFrameLoader::MaybeCreateDocShell()
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->OwnerDoc();
   if (!(doc->IsStaticDocument() || mOwnerContent->IsInDoc())) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (doc->GetDisplayDocument() || !doc->IsActive()) {
-    // Don't allow subframe loads in external reference documents, nor
+  if (doc->IsResourceDoc() || !doc->IsActive()) {
+    // Don't allow subframe loads in resource documents, nor
     // in non-active documents.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsISupports> container =
     doc->GetContainer();
   nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(container);
   NS_ENSURE_STATE(parentAsWebNav);
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -47,16 +47,20 @@
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIProtocolHandler.h"
 
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
 static bool
 IsChromeProcess()
 {
   nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
   if (!rt)
     return true;
 
   PRUint32 type;
@@ -274,16 +278,19 @@ nsFrameMessageManager::SendAsyncMessage(
     GetParamsForMessage(aObject, aCx, json);
   }
   return SendAsyncMessageInternal(aMessageName, json);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
+#ifdef ANDROID
+  __android_log_print(ANDROID_LOG_INFO, "Gecko", NS_ConvertUTF16toUTF8(aStr).get());
+#endif
   fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
   fflush(stdout);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::PrivateNoteIntentionalCrash()
 {
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -67,16 +67,17 @@
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
 
 #include "nsIFrame.h"
 #include "nsIDOMNode.h"
 
 #include "nsContentUtils.h"
+#include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsSVGEffects.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/dom/Element.h"
 
@@ -110,17 +111,19 @@ nsImageLoadingContent::nsImageLoadingCon
     // mBroken starts out true, since an image without a URI is broken....
     mBroken(true),
     mUserDisabled(false),
     mSuppressed(false),
     mBlockingOnload(false),
     mNewRequestsWillNeedAnimationReset(false),
     mPendingRequestNeedsResetAnimation(false),
     mCurrentRequestNeedsResetAnimation(false),
-    mStateChangerDepth(0)
+    mStateChangerDepth(0),
+    mCurrentRequestRegistered(false),
+    mPendingRequestRegistered(false)
 {
   if (!nsContentUtils::GetImgLoader()) {
     mLoadingEnabled = false;
   }
 }
 
 void
 nsImageLoadingContent::DestroyImageLoadingContent()
@@ -323,17 +326,16 @@ nsImageLoadingContent::OnStopDecode(imgI
   // (ie, before the initial paint delay is finished), we fire onload without
   // doing a paint first. This means that decode-on-draw images don't start
   // decoding, so we can't wait for them to finish. See bug 512435.
 
   // We can only do this if we have a presshell
   nsIDocument* doc = GetOurDocument();
   nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
   if (shell) {
-
     // We need to figure out whether to kick off decoding
     bool doRequestDecode = false;
 
     // If we haven't got the initial reflow yet, IsPaintingSuppressed actually
     // returns false
     if (!shell->DidInitialReflow())
       doRequestDecode = true;
 
@@ -368,16 +370,28 @@ nsImageLoadingContent::OnStopRequest(img
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
   LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
+  if (requestFlag) {
+    nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
+                                        aRequest, requestFlag);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsImageLoadingContent::OnDiscard(imgIRequest *aRequest)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
   LOOP_OVER_OBSERVERS(OnDiscard(aRequest));
 
   return NS_OK;
 }
@@ -496,16 +510,54 @@ nsImageLoadingContent::GetRequest(PRInt3
     *aRequest = nsnull;
     return NS_ERROR_UNEXPECTED;
   }
   
   NS_IF_ADDREF(*aRequest);
   return NS_OK;
 }
 
+NS_IMETHODIMP_(void)
+nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
+{
+  NS_ASSERTION(aFrame, "aFrame is null");
+
+  // We need to make sure that our image request is registered, if it should
+  // be registered.
+  nsPresContext* presContext = aFrame->PresContext();
+
+  if (mCurrentRequest) {
+    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
+                                                  &mCurrentRequestRegistered);
+  }
+
+  if (mPendingRequest) {
+    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest,
+                                                  &mPendingRequestRegistered);
+  }
+}
+
+NS_IMETHODIMP_(void)
+nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
+{
+  NS_ASSERTION(aFrame, "aFrame is null");
+
+  // We need to make sure that our image request is deregistered.
+  if (mCurrentRequest) {
+    nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
+                                          mCurrentRequest,
+                                          &mCurrentRequestRegistered);
+  }
+
+  if (mPendingRequest) {
+    nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
+                                          mPendingRequest,
+                                          &mPendingRequestRegistered);
+  }
+}
 
 NS_IMETHODIMP
 nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
                                       PRInt32* aRequestType)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
   NS_PRECONDITION(aRequestType, "Null out param");
@@ -862,16 +914,33 @@ nsIDocument*
 nsImageLoadingContent::GetOurDocument()
 {
   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   NS_ENSURE_TRUE(thisContent, nsnull);
 
   return thisContent->OwnerDoc();
 }
 
+nsIFrame*
+nsImageLoadingContent::GetOurPrimaryFrame()
+{
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
+  return thisContent->GetPrimaryFrame();
+}
+
+nsPresContext* nsImageLoadingContent::GetFramePresContext()
+{
+  nsIFrame* frame = GetOurPrimaryFrame();
+  if (!frame) {
+    return nsnull;
+  }
+
+  return frame->PresContext();
+}
+
 nsresult
 nsImageLoadingContent::StringToURI(const nsAString& aSpec,
                                    nsIDocument* aDocument,
                                    nsIURI** aURI)
 {
   NS_PRECONDITION(aDocument, "Must have a document");
   NS_PRECONDITION(aURI, "Null out param");
 
@@ -981,16 +1050,21 @@ nsImageLoadingContent::ClearCurrentReque
     // Even if we didn't have a current request, we might have been keeping
     // a URI as a placeholder for a failed load. Clear that now.
     mCurrentURI = nsnull;
     return;
   }
   NS_ABORT_IF_FALSE(!mCurrentURI,
                     "Shouldn't have both mCurrentRequest and mCurrentURI!");
 
+  // Deregister this image from the refresh driver so it no longer receives
+  // notifications.
+  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
+                                        &mCurrentRequestRegistered);
+
   // Clean up the request.
   UntrackImage(mCurrentRequest);
   mCurrentRequest->CancelAndForgetObserver(aReason);
   mCurrentRequest = nsnull;
   mCurrentRequestNeedsResetAnimation = false;
 
   // We only block onload during the decoding of "current" images. This one is
   // going away, so we should unblock unconditionally here.
@@ -1004,22 +1078,39 @@ nsImageLoadingContent::ClearPendingReque
     return;
 
   // Push a null JSContext on the stack so that code that runs within
   // the below code doesn't think it's being called by JS. See bug
   // 604262.
   nsCxPusher pusher;
   pusher.PushNull();
 
+  // Deregister this image from the refresh driver so it no longer receives
+  // notifications.
+  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
+                                        &mPendingRequestRegistered);
+
   UntrackImage(mPendingRequest);
   mPendingRequest->CancelAndForgetObserver(aReason);
   mPendingRequest = nsnull;
   mPendingRequestNeedsResetAnimation = false;
 }
 
+bool*
+nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
+{
+  if (aRequest == mCurrentRequest) {
+    return &mCurrentRequestRegistered;
+  } else if (aRequest == mPendingRequest) {
+    return &mPendingRequestRegistered;
+  } else {
+    return nsnull;
+  }
+}
+
 bool
 nsImageLoadingContent::HaveSize(imgIRequest *aImage)
 {
   // Handle the null case
   if (!aImage)
     return false;
 
   // Query the image
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -142,16 +142,34 @@ protected:
    * and such).  Not named GetDocument to prevent ambiguous method
    * names in subclasses
    *
    * @return the document we belong to
    */
   nsIDocument* GetOurDocument();
 
   /**
+   * Helper function to get the frame associated with this content. Not named
+   * GetPrimaryFrame to prevent ambiguous method names in subclasses.
+   *
+   * @return The frame which we belong to, or nsnull if it doesn't exist.
+   */
+  nsIFrame* GetOurPrimaryFrame();
+
+  /**
+   * Helper function to get the PresContext associated with this content's
+   * frame. Not named GetPresContext to prevent ambiguous method names in
+   * subclasses.
+   *
+   * @return The nsPresContext associated with our frame, or nsnull if either
+   *         the frame doesn't exist, or the frame's prescontext doesn't exist.
+   */
+  nsPresContext* GetFramePresContext();
+
+  /**
    * CancelImageRequests is called by subclasses when they want to
    * cancel all image requests (for example when the subclass is
    * somehow not an image anymore).
    */
   void CancelImageRequests(bool aNotify);
 
   /**
    * UseAsPrimaryRequest is called by subclasses when they have an existing
@@ -298,16 +316,26 @@ protected:
 
   /**
    * Cancels and nulls-out the "current" and "pending" requests if they exist.
    */
   void ClearCurrentRequest(nsresult aReason);
   void ClearPendingRequest(nsresult aReason);
 
   /**
+   * Retrieve a pointer to the 'registered with the refresh driver' flag for
+   * which a particular image request corresponds.
+   *
+   * @returns A pointer to the boolean flag for a given image request, or
+   *          |nsnull| if the request is not either |mPendingRequest| or
+   *          |mCurrentRequest|.
+   */
+  bool* GetRegisteredFlagForRequest(imgIRequest* aRequest);
+
+  /**
    * Static helper method to tell us if we have the size of a request. The
    * image may be null.
    */
   static bool HaveSize(imgIRequest *aImage);
 
   /**
    * Adds/Removes a given imgIRequest from our document's tracker.
    *
@@ -377,11 +405,16 @@ protected:
   bool mNewRequestsWillNeedAnimationReset : 1;
 
 private:
   bool mPendingRequestNeedsResetAnimation : 1;
   bool mCurrentRequestNeedsResetAnimation : 1;
 
   /* The number of nested AutoStateChangers currently tracking our state. */
   PRUint8 mStateChangerDepth;
+
+  // Flags to indicate whether each of the current and pending requests are
+  // registered with the refresh driver.
+  bool mCurrentRequestRegistered;
+  bool mPendingRequestRegistered;
 };
 
 #endif // nsImageLoadingContent_h__
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -281,17 +281,17 @@ nsRange::CharacterDataChanged(nsIDocumen
   // before the boundary we'll need to adjust the offset.
   if (aContent == mStartParent &&
       aInfo->mChangeStart < static_cast<PRUint32>(mStartOffset)) {
     if (aInfo->mDetails) {
       // splitText(), aInfo->mDetails->mNextSibling is the new text node
       NS_ASSERTION(aInfo->mDetails->mType ==
                    CharacterDataChangeInfo::Details::eSplit,
                    "only a split can start before the end");
-      NS_ASSERTION(static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd,
+      NS_ASSERTION(static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd + 1,
                    "mStartOffset is beyond the end of this node");
       newStartOffset = static_cast<PRUint32>(mStartOffset) - aInfo->mChangeStart;
       newStartNode = aInfo->mDetails->mNextSibling;
       if (NS_UNLIKELY(aContent == mRoot)) {
         newRoot = IsValidBoundary(newStartNode);
       }
     } else {
       // If boundary is inside changed text, position it before change
@@ -308,17 +308,17 @@ nsRange::CharacterDataChanged(nsIDocumen
   // did so too (otherwise the range would end up with disconnected nodes).
   if (aContent == mEndParent &&
       aInfo->mChangeStart < static_cast<PRUint32>(mEndOffset)) {
     if (aInfo->mDetails && (aContent->GetParent() || newStartNode)) {
       // splitText(), aInfo->mDetails->mNextSibling is the new text node
       NS_ASSERTION(aInfo->mDetails->mType ==
                    CharacterDataChangeInfo::Details::eSplit,
                    "only a split can start before the end");
-      NS_ASSERTION(static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd,
+      NS_ASSERTION(static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd + 1,
                    "mEndOffset is beyond the end of this node");
       newEndOffset = static_cast<PRUint32>(mEndOffset) - aInfo->mChangeStart;
       newEndNode = aInfo->mDetails->mNextSibling;
     } else {
       mEndOffset = static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd ?
         aInfo->mChangeStart :
         mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
           aInfo->mReplaceLength;
--- a/content/base/src/nsStubImageDecoderObserver.cpp
+++ b/content/base/src/nsStubImageDecoderObserver.cpp
@@ -102,13 +102,19 @@ nsStubImageDecoderObserver::OnStopReques
 
 NS_IMETHODIMP 
 nsStubImageDecoderObserver::OnDiscard(imgIRequest *aRequest)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsStubImageDecoderObserver::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsStubImageDecoderObserver::FrameChanged(imgIContainer *aContainer,
                                          const nsIntRect *aDirtyRect)
 {
     return NS_OK;
 }
--- a/content/svg/content/src/SVGLengthList.cpp
+++ b/content/svg/content/src/SVGLengthList.cpp
@@ -37,16 +37,17 @@
 #include "SVGLengthList.h"
 #include "SVGAnimatedLengthList.h"
 #include "SVGLength.h"
 #include "nsSVGElement.h"
 #include "nsDOMError.h"
 #include "nsContentUtils.h"
 #include "nsString.h"
 #include "nsSVGUtils.h"
+#include "nsCharSeparatedTokenizer.h"
 #include "string.h"
 
 namespace mozilla {
 
 nsresult
 SVGLengthList::CopyFrom(const SVGLengthList& rhs)
 {
   if (!mLengths.SetCapacity(rhs.Length())) {
@@ -80,42 +81,33 @@ static inline char* SkipWhitespace(char*
   return str;
 }
 
 nsresult
 SVGLengthList::SetValueFromString(const nsAString& aValue)
 {
   SVGLengthList temp;
 
-  NS_ConvertUTF16toUTF8 value(aValue);
-  char* start = SkipWhitespace(value.BeginWriting());
-
-  // We can't use strtok with SVG_COMMA_WSP_DELIM because to correctly handle
-  // invalid input in the form of two commas without a value between them, we
-  // would need to know if strtok overwrote a comma or not.
+  nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
+    tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
-  while (*start != '\0') {
-    int end = strcspn(start, SVG_COMMA_WSP_DELIM);
-    if (end == 0) {
-      // found comma in an invalid location
-      return NS_ERROR_DOM_SYNTAX_ERR;
-    }
+  nsCAutoString str;  // outside loop to minimize memory churn
+
+  while (tokenizer.hasMoreTokens()) {
     SVGLength length;
-    if (!length.SetValueFromString(NS_ConvertUTF8toUTF16(start, PRUint32(end)))) {
+    if (!length.SetValueFromString(tokenizer.nextToken())) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
     if (!temp.AppendItem(length)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    start = SkipWhitespace(start + end);
-    if (*start == ',') {
-      start = SkipWhitespace(start + 1);
-    }
   }
-
+  if (tokenizer.lastTokenEndedWithSeparator()) {
+    return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
+  }
   return CopyFrom(temp);
 }
 
 bool
 SVGLengthList::operator==(const SVGLengthList& rhs) const
 {
   if (Length() != rhs.Length()) {
     return false;
--- a/content/svg/content/src/SVGNumberList.cpp
+++ b/content/svg/content/src/SVGNumberList.cpp
@@ -97,17 +97,19 @@ SVGNumberList::SetValueFromString(const 
     if (token == '\0') {
       return NS_ERROR_DOM_SYNTAX_ERR; // nothing between commas
     }
     char *end;
     float num = float(PR_strtod(token, &end));
     if (*end != '\0' || !NS_finite(num)) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
-    temp.AppendItem(num);
+    if (!temp.AppendItem(num)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
   }
   if (tokenizer.lastTokenEndedWithSeparator()) {
     return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
   }
   return CopyFrom(temp);
 }
 
 } // namespace mozilla
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -229,19 +229,20 @@ nsDefaultURIFixup::CreateFixupURI(const 
             }
         }
 #endif
     }
 
     // For these protocols, use system charset instead of the default UTF-8,
     // if the URI is non ASCII.
     bool bAsciiURI = IsASCII(uriString);
+    bool useUTF8 = (aFixupFlags & FIXUP_FLAG_USE_UTF8) ||
+                   Preferences::GetBool("browser.fixup.use-utf8", false);
     bool bUseNonDefaultCharsetForURI =
-                        !bAsciiURI &&
-                        !(aFixupFlags & FIXUP_FLAG_USE_UTF8) &&
+                        !bAsciiURI && !useUTF8 &&
                         (scheme.IsEmpty() ||
                          scheme.LowerCaseEqualsLiteral("http") ||
                          scheme.LowerCaseEqualsLiteral("https") ||
                          scheme.LowerCaseEqualsLiteral("ftp") ||
                          scheme.LowerCaseEqualsLiteral("file"));
 
     // Now we need to check whether "scheme" is something we don't
     // really know about.
@@ -318,17 +319,17 @@ nsDefaultURIFixup::CreateFixupURI(const 
 
         // insert url spec corresponding to host name
         if (IsLikelyFTP(hostSpec))
             uriString.Assign(NS_LITERAL_CSTRING("ftp://") + uriString);
         else 
             uriString.Assign(NS_LITERAL_CSTRING("http://") + uriString);
 
         // For ftp & http, we want to use system charset.
-        if (!bAsciiURI && !(aFixupFlags & FIXUP_FLAG_USE_UTF8))
+        if (!bAsciiURI && !useUTF8)
           bUseNonDefaultCharsetForURI = true;
     } // end if checkprotocol
 
     rv = NS_NewURI(aURI, uriString, bUseNonDefaultCharsetForURI ? GetCharsetForUrlBar() : nsnull);
 
     // Did the caller want us to try an alternative URI?
     // If so, attempt to fixup http://foo into http://www.foo.com
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -248,16 +248,20 @@
 
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsWrapperCacheInlines.h"
 
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -4566,16 +4570,19 @@ nsGlobalWindow::Dump(const nsAString& aS
   while (c < cEnd) {
     if (*c == '\r')
       *c = '\n';
     c++;
   }
 #endif
 
   if (cstr) {
+#ifdef ANDROID
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", cstr);
+#endif
     FILE *fp = gDumpFile ? gDumpFile : stdout;
     fputs(cstr, fp);
     fflush(fp);
     nsMemory::Free(cstr);
   }
 
   return NS_OK;
 }
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -157,17 +157,17 @@ static bool sPostGCEventsToConsole;
 nsScriptNameSpaceManager *gNameSpaceManager;
 
 static nsIJSRuntimeService *sRuntimeService;
 JSRuntime *nsJSRuntime::sRuntime;
 
 static const char kJSRuntimeServiceContractID[] =
   "@mozilla.org/js/xpc/RuntimeService;1";
 
-static JSGCCallback gOldJSGCCallback;
+static PRTime sFirstCollectionTime;
 
 static bool sIsInitialized;
 static bool sDidShutdown;
 
 static PRInt32 sContextCount;
 
 static PRTime sMaxScriptRunTime;
 static PRTime sMaxChromeScriptRunTime;
@@ -932,16 +932,17 @@ static const char js_tracejit_chrome_str
 static const char js_methodjit_content_str[]  = JS_OPTIONS_DOT_STR "methodjit.content";
 static const char js_methodjit_chrome_str[]   = JS_OPTIONS_DOT_STR "methodjit.chrome";
 static const char js_profiling_content_str[]  = JS_OPTIONS_DOT_STR "jitprofiling.content";
 static const char js_profiling_chrome_str[]   = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
 static const char js_methodjit_always_str[]   = JS_OPTIONS_DOT_STR "methodjit_always";
 static const char js_typeinfer_str[]          = JS_OPTIONS_DOT_STR "typeinference";
 static const char js_pccounts_content_str[]   = JS_OPTIONS_DOT_STR "pccounts.content";
 static const char js_pccounts_chrome_str[]    = JS_OPTIONS_DOT_STR "pccounts.chrome";
+static const char js_jit_hardening_str[]      = JS_OPTIONS_DOT_STR "jit_hardening";
 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
 
 int
 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
 {
   nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
   PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
   PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
@@ -968,27 +969,29 @@ nsJSContext::JSOptionChangedCallback(con
   bool useProfiling = Preferences::GetBool(chromeWindow ?
                                                js_profiling_chrome_str :
                                                js_profiling_content_str);
   bool usePCCounts = Preferences::GetBool(chromeWindow ?
                                             js_pccounts_chrome_str :
                                             js_pccounts_content_str);
   bool useMethodJITAlways = Preferences::GetBool(js_methodjit_always_str);
   bool useTypeInference = !chromeWindow && Preferences::GetBool(js_typeinfer_str);
+  bool useHardening = Preferences::GetBool(js_jit_hardening_str);
   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   if (xr) {
     bool safeMode = false;
     xr->GetInSafeMode(&safeMode);
     if (safeMode) {
       useTraceJIT = false;
       useMethodJIT = false;
       useProfiling = false;
       usePCCounts = false;
       useTypeInference = false;
       useMethodJITAlways = true;
+      useHardening = false;
     }
   }    
 
   if (useTraceJIT)
     newDefaultJSOptions |= JSOPTION_JIT;
   else
     newDefaultJSOptions &= ~JSOPTION_JIT;
 
@@ -1007,16 +1010,21 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_PCCOUNT;
 
   if (useMethodJITAlways)
     newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
 
+  if (useHardening)
+    newDefaultJSOptions &= ~JSOPTION_SOFTEN;
+  else
+    newDefaultJSOptions |= JSOPTION_SOFTEN;
+
   if (useTypeInference)
     newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
   else
     newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
 
 #ifdef DEBUG
   // In debug builds, warnings are enabled in chrome context if
   // javascript.options.strict.debug is true
@@ -3239,20 +3247,27 @@ nsJSContext::CycleCollectNow(nsICycleCol
   // If we collected a substantial amount of cycles, poke the GC since more objects
   // might be unreachable now.
   if (sCCollectedWaitingForGC > 250) {
     PokeGC();
   }
 
   if (sPostGCEventsToConsole) {
     PRTime now = PR_Now();
+    PRTime delta = 0;
+    if (sFirstCollectionTime) {
+      delta = now - sFirstCollectionTime;
+    } else {
+      sFirstCollectionTime = now;
+    }
+
     NS_NAMED_LITERAL_STRING(kFmt,
-                            "CC timestamp: %lld, collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.");
+                            "CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.");
     nsString msg;
-    msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now,
+    msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         collected, sCCollectedWaitingForGC, suspected,
                                         (now - start) / PR_USEC_PER_MSEC));
     nsCOMPtr<nsIConsoleService> cs =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
@@ -3383,77 +3398,69 @@ nsJSContext::KillCCTimer()
 }
 
 void
 nsJSContext::GC()
 {
   PokeGC();
 }
 
-static JSBool
-DOMGCCallback(JSContext *cx, JSGCStatus status)
+static void
+DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
 {
-  static PRTime start;
-
-  if (sPostGCEventsToConsole && NS_IsMainThread()) {
-    if (status == JSGC_BEGIN) {
-      start = PR_Now();
-    } else if (status == JSGC_END) {
-      PRTime now = PR_Now();
-      NS_NAMED_LITERAL_STRING(kFmt, "GC mode: %s, timestamp: %lld, duration: %llu ms.");
-      nsString msg;
-      msg.Adopt(nsTextFormatter::smprintf(kFmt.get(),
-                cx->runtime->gcTriggerCompartment ? "compartment" : "full",
-                now,
-                (now - start) / PR_USEC_PER_MSEC));
-      nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-      if (cs) {
-        cs->LogStringMessage(msg.get());
-      }
+  NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
+
+  if (sPostGCEventsToConsole) {
+    PRTime now = PR_Now();
+    PRTime delta = 0;
+    if (sFirstCollectionTime) {
+      delta = now - sFirstCollectionTime;
+    } else {
+      sFirstCollectionTime = now;
+    }
+
+    NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) %s");
+    nsString msg;
+    msg.Adopt(nsTextFormatter::smprintf(kFmt.get(),
+                                        double(delta) / PR_USEC_PER_SEC, status));
+    nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    if (cs) {
+      cs->LogStringMessage(msg.get());
     }
   }
 
-  if (status == JSGC_END) {
-    sCCollectedWaitingForGC = 0;
-    if (sGCTimer) {
-      // If we were waiting for a GC to happen, kill the timer.
-      nsJSContext::KillGCTimer();
-
-      // If this is a compartment GC, restart it. We still want
-      // a full GC to happen. Compartment GCs usually happen as a
-      // result of last-ditch or MaybeGC. In both cases its
-      // probably a time of heavy activity and we want to delay
-      // the full GC, but we do want it to happen eventually.
-      if (cx->runtime->gcTriggerCompartment) {
-        nsJSContext::PokeGC();
-
-        // We poked the GC, so we can kill any pending CC here.
-        nsJSContext::KillCCTimer();
-      }
-    } else {
-      // If this was a full GC, poke the CC to run soon.
-      if (!cx->runtime->gcTriggerCompartment) {
-        sGCHasRun = true;
-        nsJSContext::PokeCC();
-      }
+  sCCollectedWaitingForGC = 0;
+  if (sGCTimer) {
+    // If we were waiting for a GC to happen, kill the timer.
+    nsJSContext::KillGCTimer();
+
+    // If this is a compartment GC, restart it. We still want
+    // a full GC to happen. Compartment GCs usually happen as a
+    // result of last-ditch or MaybeGC. In both cases its
+    // probably a time of heavy activity and we want to delay
+    // the full GC, but we do want it to happen eventually.
+    if (comp) {
+      nsJSContext::PokeGC();
+
+      // We poked the GC, so we can kill any pending CC here.
+      nsJSContext::KillCCTimer();
     }
-
-    // If we didn't end up scheduling a GC, and there are unused
-    // chunks waiting to expire, make sure we will GC again soon.
-    if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) {
-      nsJSContext::PokeGC();
+  } else {
+    // If this was a full GC, poke the CC to run soon.
+    if (!comp) {
+      sGCHasRun = true;
+      nsJSContext::PokeCC();
     }
   }
 
-  JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
-
-  if (status == JSGC_BEGIN && !NS_IsMainThread())
-    return JS_FALSE;
-
-  return result;
+  // If we didn't end up scheduling a GC, and there are unused
+  // chunks waiting to expire, make sure we will GC again soon.
+  if (!sGCTimer && JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS) > 0) {
+    nsJSContext::PokeGC();
+  }
 }
 
 // Script object mananagement - note duplicate implementation
 // in nsJSRuntime below...
 nsresult
 nsJSContext::HoldScriptObject(void* aScriptObject)
 {
     NS_ASSERTION(sIsInitialized, "runtime not initialized");
@@ -3548,17 +3555,16 @@ nsJSRuntime::Startup()
   sGCHasRun = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sPostGCEventsToConsole = false;
   gNameSpaceManager = nsnull;
   sRuntimeService = nsnull;
   sRuntime = nsnull;
-  gOldJSGCCallback = nsnull;
   sIsInitialized = false;
   sDidShutdown = false;
   sContextCount = 0;
   sSecurityManager = nsnull;
 }
 
 static int
 MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
@@ -3702,21 +3708,17 @@ nsJSRuntime::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = sRuntimeService->GetRuntime(&sRuntime);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Let's make sure that our main thread is the same as the xpcom main thread.
   NS_ASSERTION(NS_IsMainThread(), "bad");
 
-  NS_ASSERTION(!gOldJSGCCallback,
-               "nsJSRuntime initialized more than once");
-
-  // Save the old GC callback to chain to it, for GC-observing generality.
-  gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
+  ::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback);
 
   JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
   NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
 
   callbacks->findObjectPrincipals = ObjectPrincipalFinder;
 
   // Set up the structured clone callbacks.
   static JSStructuredCloneCallbacks cloneCallbacks = {
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -30,61 +30,67 @@
  * 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 <limits>
 #include "mozilla/Hal.h"
 #include "BatteryManager.h"
 #include "nsIDOMClassInfo.h"
 #include "Constants.h"
 #include "nsDOMEvent.h"
 #include "mozilla/Preferences.h"
 
 /**
  * We have to use macros here because our leak analysis tool things we are
  * leaking strings when we have |static const nsString|. Sad :(
  */
-#define LEVELCHANGE_EVENT_NAME    NS_LITERAL_STRING("levelchange")
-#define CHARGINGCHANGE_EVENT_NAME NS_LITERAL_STRING("chargingchange")
+#define LEVELCHANGE_EVENT_NAME           NS_LITERAL_STRING("levelchange")
+#define CHARGINGCHANGE_EVENT_NAME        NS_LITERAL_STRING("chargingchange")
+#define DISCHARGINGTIMECHANGE_EVENT_NAME NS_LITERAL_STRING("dischargingtimechange")
+#define CHARGINGTIMECHANGE_EVENT_NAME    NS_LITERAL_STRING("chargingtimechange")
 
 DOMCI_DATA(BatteryManager, mozilla::dom::battery::BatteryManager)
 
 namespace mozilla {
 namespace dom {
 namespace battery {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BatteryManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BatteryManager,
                                                   nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLevelChangeListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnChargingChangeListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnDischargingTimeChangeListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BatteryManager,
                                                 nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLevelChangeListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnChargingChangeListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnDischargingTimeChangeListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BatteryManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBatteryManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BatteryManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BatteryManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BatteryManager, nsDOMEventTargetHelper)
 
 BatteryManager::BatteryManager()
   : mLevel(kDefaultLevel)
   , mCharging(kDefaultCharging)
+  , mRemainingTime(kUnknownRemainingTime)
 {
 }
 
 BatteryManager::~BatteryManager()
 {
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
@@ -113,24 +119,50 @@ NS_IMETHODIMP
 BatteryManager::GetCharging(bool* aCharging)
 {
   *aCharging = mCharging;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-BatteryManager::GetLevel(float* aLevel)
+BatteryManager::GetLevel(double* aLevel)
 {
   *aLevel = mLevel;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+BatteryManager::GetDischargingTime(double* aDischargingTime)
+{
+  if (mCharging || mRemainingTime == kUnknownRemainingTime) {
+    *aDischargingTime = std::numeric_limits<double>::infinity();
+    return NS_OK;
+  }
+
+  *aDischargingTime = mRemainingTime;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BatteryManager::GetChargingTime(double* aChargingTime)
+{
+  if (!mCharging || mRemainingTime == kUnknownRemainingTime) {
+    *aChargingTime = std::numeric_limits<double>::infinity();
+    return NS_OK;
+  }
+
+  *aChargingTime = mRemainingTime;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 BatteryManager::GetOnlevelchange(nsIDOMEventListener** aOnlevelchange)
 {
   return GetInnerEventListener(mOnLevelChangeListener, aOnlevelchange);
 }
 
 NS_IMETHODIMP
 BatteryManager::SetOnlevelchange(nsIDOMEventListener* aOnlevelchange)
 {
@@ -146,16 +178,46 @@ BatteryManager::GetOnchargingchange(nsID
 
 NS_IMETHODIMP
 BatteryManager::SetOnchargingchange(nsIDOMEventListener* aOnchargingchange)
 {
   return RemoveAddEventListener(CHARGINGCHANGE_EVENT_NAME,
                                 mOnChargingChangeListener, aOnchargingchange);
 }
 
+NS_IMETHODIMP
+BatteryManager::GetOndischargingtimechange(nsIDOMEventListener** aOndischargingtimechange)
+{
+  return GetInnerEventListener(mOnDischargingTimeChangeListener,
+                               aOndischargingtimechange);
+}
+
+NS_IMETHODIMP
+BatteryManager::SetOndischargingtimechange(nsIDOMEventListener* aOndischargingtimechange)
+{
+  return RemoveAddEventListener(DISCHARGINGTIMECHANGE_EVENT_NAME,
+                                mOnDischargingTimeChangeListener,
+                                aOndischargingtimechange);
+}
+
+NS_IMETHODIMP
+BatteryManager::GetOnchargingtimechange(nsIDOMEventListener** aOnchargingtimechange)
+{
+  return GetInnerEventListener(mOnChargingTimeChangeListener,
+                               aOnchargingtimechange);
+}
+
+NS_IMETHODIMP
+BatteryManager::SetOnchargingtimechange(nsIDOMEventListener* aOnchargingtimechange)
+{
+  return RemoveAddEventListener(CHARGINGTIMECHANGE_EVENT_NAME,
+                                mOnChargingTimeChangeListener,
+                                aOnchargingtimechange);
+}
+
 nsresult
 BatteryManager::DispatchTrustedEventToSelf(const nsAString& aEventName)
 {
   nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
   nsresult rv = event->InitEvent(aEventName, false, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = event->SetTrusted(PR_TRUE);
@@ -168,33 +230,58 @@ BatteryManager::DispatchTrustedEventToSe
   return NS_OK;
 }
 
 void
 BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo)
 {
   mLevel = aBatteryInfo.level();
   mCharging = aBatteryInfo.charging();
+  mRemainingTime = aBatteryInfo.remainingTime();
 }
 
 void
 BatteryManager::Notify(const hal::BatteryInformation& aBatteryInfo)
 {
-  float previousLevel = mLevel;
+  double previousLevel = mLevel;
   bool previousCharging = mCharging;
+  double previousRemainingTime = mRemainingTime;
 
   UpdateFromBatteryInfo(aBatteryInfo);
 
   if (previousCharging != mCharging) {
     DispatchTrustedEventToSelf(CHARGINGCHANGE_EVENT_NAME);
   }
 
   if (previousLevel != mLevel) {
     DispatchTrustedEventToSelf(LEVELCHANGE_EVENT_NAME);
   }
+
+  /*
+   * There are a few situations that could happen here:
+   * 1. Charging state changed:
+   *   a. Previous remaining time wasn't unkwonw, we have to fire an event for
+   *      the change.
+   *   b. New remaining time isn't unkwonw, we have to fire an event for it.
+   * 2. Charging state didn't change but remainingTime did, we have to fire
+   *    the event that correspond to the current charging state.
+   */
+  if (mCharging != previousCharging) {
+    if (previousRemainingTime != kUnknownRemainingTime) {
+      DispatchTrustedEventToSelf(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                                  : DISCHARGINGTIMECHANGE_EVENT_NAME);
+    }
+    if (mRemainingTime != kUnknownRemainingTime) {
+      DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                           : DISCHARGINGTIMECHANGE_EVENT_NAME);
+    }
+  } else if (previousRemainingTime != mRemainingTime) {
+    DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                         : DISCHARGINGTIMECHANGE_EVENT_NAME);
+  }
 }
 
 /* static */ bool
 BatteryManager::HasSupport()
 {
   return Preferences::GetBool("dom.battery.enabled", true);
 }
 
--- a/dom/battery/BatteryManager.h
+++ b/dom/battery/BatteryManager.h
@@ -88,20 +88,27 @@ private:
   nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
 
   /**
    * Update the battery information stored in the battery manager object using
    * a battery information object.
    */
   void UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo);
 
-  float mLevel;
-  bool  mCharging;
+  double mLevel;
+  bool   mCharging;
+  /**
+   * Represents the discharging time or the charging time, dpending on the
+   * current battery status (charging or not).
+   */
+  double mRemainingTime;
 
   nsRefPtr<nsDOMEventListenerWrapper> mOnLevelChangeListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnChargingChangeListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnDischargingTimeChangeListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnChargingTimeChangeListener;
 };
 
 } // namespace battery
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_battery_BatteryManager_h
--- a/dom/battery/Constants.h
+++ b/dom/battery/Constants.h
@@ -41,16 +41,17 @@
 /**
  * A set of constants that might need to be used by battery backends.
  * It's not part of BatteryManager.h to prevent those backends to include it.
  */
 namespace mozilla {
 namespace dom {
 namespace battery {
 
-  static const float kDefaultLevel    = 1.0f;
-  static const bool  kDefaultCharging = true;
+  static const double kDefaultLevel         = 1.0;
+  static const bool   kDefaultCharging      = true;
+  static const double kUnknownRemainingTime = -1;
 
 } // namespace battery
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_battery_Constants_h__
--- a/dom/battery/nsIDOMBatteryManager.idl
+++ b/dom/battery/nsIDOMBatteryManager.idl
@@ -33,17 +33,21 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
 
-[scriptable, function, uuid(6ac8bdb2-f005-469b-a55e-398e23ef3c95)]
+[scriptable, function, uuid(6dcb803b-e968-4c02-88f5-049a3f2a2efb)]
 interface nsIDOMBatteryManager : nsIDOMEventTarget
 {
-  readonly attribute float      level;
+  readonly attribute double     level;
   readonly attribute boolean    charging;
+  readonly attribute double     dischargingTime;
+  readonly attribute double     chargingTime;
 
   attribute nsIDOMEventListener onlevelchange;
   attribute nsIDOMEventListener onchargingchange;
+  attribute nsIDOMEventListener ondischargingtimechange;
+  attribute nsIDOMEventListener onchargingtimechange;
 };
--- a/dom/battery/test/test_battery_basics.html
+++ b/dom/battery/test/test_battery_basics.html
@@ -14,13 +14,15 @@
 
 /** Test for Battery API **/
 
 ok('mozBattery' in navigator, "navigator.mozBattery should exist");
 
 var battery = navigator.mozBattery;
 is(battery.level, 1.0, "Default battery level should be 1.0");
 is(battery.charging, true, "Default charging value should be true");
+is(battery.dischargingTime, Infinity, "Default dischargingTime should be Inifinity");
+is(battery.chargingTime, Infinity, "Default chargingTime should be Inifinity");
 
 </script>
 </pre>
 </body>
 </html>
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -109,17 +109,17 @@ public class GeckoAppShell
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadLibs(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
     public static native void reportJavaCrash(String stack);
 
     public static native void processNextNativeEvent();
 
-    public static native void notifyBatteryChange(float aLevel, boolean aCharging);
+    public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
     // A looper thread, accessed by GeckoAppShell.getHandler
     private static class LooperThread extends Thread {
         public SynchronousQueue<Handler> mHandlerQueue =
             new SynchronousQueue<Handler>();
         
         public void run() {
             Looper.prepare();
@@ -1623,12 +1623,12 @@ public class GeckoAppShell
     public static void enableBatteryNotifications() {
         GeckoBatteryManager.enableNotifications();
     }
 
     public static void disableBatteryNotifications() {
         GeckoBatteryManager.disableNotifications();
     }
 
-    public static float[] getCurrentBatteryInformation() {
+    public static double[] getCurrentBatteryInformation() {
         return GeckoBatteryManager.getCurrentInformation();
     }
 }
--- a/embedding/android/GeckoBatteryManager.java
+++ b/embedding/android/GeckoBatteryManager.java
@@ -33,97 +33,149 @@
  * 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 ***** */
 
 package org.mozilla.gecko;
 
 import java.lang.Math;
+import java.util.Date;
 
 import android.util.Log;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
 import android.os.BatteryManager;
 
 public class GeckoBatteryManager
   extends BroadcastReceiver
 {
-  private final static float   kDefaultLevel       = 1.0f;
-  private final static boolean kDefaultCharging    = true;
-  private final static float   kMinimumLevelChange = 0.01f;
+  // Those constants should be keep in sync with the ones in:
+  // dom/battery/Constants.h
+  private final static double  kDefaultLevel         = 1.0;
+  private final static boolean kDefaultCharging      = true;
+  private final static double  kUnknownRemainingTime = -1.0;
 
-  private static boolean sNotificationsEnabled     = false;
-  private static float   sLevel                    = kDefaultLevel;
-  private static boolean sCharging                 = kDefaultCharging;
+  private static Date    sLastLevelChange            = new Date(0);
+  private static boolean sNotificationsEnabled       = false;
+  private static double  sLevel                      = kDefaultLevel;
+  private static boolean sCharging                   = kDefaultCharging;
+  private static double  sRemainingTime              = kUnknownRemainingTime;;
 
   @Override
   public void onReceive(Context context, Intent intent) {
     if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
       Log.e("GeckoBatteryManager", "Got an unexpected intent!");
       return;
     }
 
     boolean previousCharging = isCharging();
-    float previousLevel = getLevel();
+    double previousLevel = getLevel();
 
     if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)) {
       int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
       if (plugged == -1) {
         sCharging = kDefaultCharging;
         Log.e("GeckoBatteryManager", "Failed to get the plugged status!");
       } else {
         // Likely, if plugged > 0, it's likely plugged and charging but the doc
         // isn't clear about that.
         sCharging = plugged != 0;
       }
 
-      // We need two floats because sLevel is a float.
-      float current =  (float)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
-      float max = (float)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+      if (sCharging != previousCharging) {
+        sRemainingTime = kUnknownRemainingTime;
+        // The new remaining time is going to take some time to show up but
+        // it's the best way to show a not too wrong value.
+        sLastLevelChange = new Date(0);
+      }
+
+      // We need two doubles because sLevel is a double.
+      double current =  (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+      double max = (double)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
       if (current == -1 || max == -1) {
         Log.e("GeckoBatteryManager", "Failed to get battery level!");
         sLevel = kDefaultLevel;
       } else {
         sLevel = current / max;
       }
+
+      if (sLevel == 1.0 && sCharging) {
+        sRemainingTime = 0.0;
+      } else if (sLevel != previousLevel) {
+        // Estimate remaining time.
+        if (sLastLevelChange.getTime() != 0) {
+          Date currentTime = new Date();
+          long dt = (currentTime.getTime() - sLastLevelChange.getTime()) / 1000;
+          double dLevel = sLevel - previousLevel;
+
+          if (sCharging) {
+            if (dLevel < 0) {
+              Log.w("GeckoBatteryManager", "When charging, level should increase!");
+              sRemainingTime = kUnknownRemainingTime;
+            } else {
+              sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel));
+            }
+          } else {
+            if (dLevel > 0) {
+              Log.w("GeckoBatteryManager", "When discharging, level should decrease!");
+              sRemainingTime = kUnknownRemainingTime;
+            } else {
+              sRemainingTime = Math.round(dt / -dLevel * sLevel);
+            }
+          }
+
+          sLastLevelChange = currentTime;
+        } else {
+          // That's the first time we got an update, we can't do anything.
+          sLastLevelChange = new Date();
+        }
+      }
     } else {
       sLevel = kDefaultLevel;
       sCharging = kDefaultCharging;
+      sRemainingTime = kUnknownRemainingTime;
     }
 
     /*
      * We want to inform listeners if the following conditions are fulfilled:
      *  - we have at least one observer;
-     *  - the charging state has changed
-     *    OR
-     *    the level has changed of at least kMinimumLevelChange
+     *  - the charging state or the level has changed.
+     *
+     * Note: no need to check for a remaining time change given that it's only
+     * updated if there is a level change or a charging change.
+     *
+     * The idea is to prevent doing all the way to the DOM code in the child
+     * process to finally not send an event.
      */
     if (sNotificationsEnabled &&
-        (previousCharging != isCharging() ||
-         Math.abs(previousLevel - getLevel()) >= kMinimumLevelChange)) {
-      GeckoAppShell.notifyBatteryChange(sLevel, sCharging);
+        (previousCharging != isCharging() || previousLevel != getLevel())) {
+      GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime());
     }
   }
 
   public static boolean isCharging() {
     return sCharging;
   }
 
-  public static float getLevel() {
+  public static double getLevel() {
     return sLevel;
   }
 
+  public static double getRemainingTime() {
+    return sRemainingTime;
+  }
+
   public static void enableNotifications() {
     sNotificationsEnabled = true;
   }
 
   public static void disableNotifications() {
     sNotificationsEnabled = false;
   }
 
-  public static float[] getCurrentInformation() {
-    return new float[] { getLevel(), isCharging() ? 1.0f : 0.0f };
+  public static double[] getCurrentInformation() {
+    return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() };
   }
 }
--- a/extensions/auth/nsAuthSSPI.cpp
+++ b/extensions/auth/nsAuthSSPI.cpp
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is IBM Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2004
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
  *   Jim Mathies <jmathies@mozilla.com>
+ *   Guillermo Robla Vicario <groblavicario@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -47,16 +48,17 @@
 //
 
 #include "nsAuthSSPI.h"
 #include "nsIServiceManager.h"
 #include "nsIDNSService.h"
 #include "nsIDNSRecord.h"
 #include "nsNetCID.h"
 #include "nsCOMPtr.h"
+#include "nsICryptoHash.h"
 
 #include <windows.h>
 
 #define SEC_SUCCESS(Status) ((Status) >= 0)
 
 #ifndef KERB_WRAP_NO_ENCRYPT
 #define KERB_WRAP_NO_ENCRYPT 0x80000001
 #endif
@@ -185,16 +187,18 @@ MakeSN(const char *principal, nsCString 
 }
 
 //-----------------------------------------------------------------------------
 
 nsAuthSSPI::nsAuthSSPI(pType package)
     : mServiceFlags(REQ_DEFAULT)
     , mMaxTokenLen(0)
     , mPackage(package)
+    , mCertDERData(nsnull)
+    , mCertDERLength(0)
 {
     memset(&mCred, 0, sizeof(mCred));
     memset(&mCtxt, 0, sizeof(mCtxt));
 }
 
 nsAuthSSPI::~nsAuthSSPI()
 {
     Reset();
@@ -207,16 +211,24 @@ nsAuthSSPI::~nsAuthSSPI()
 #endif
         memset(&mCred, 0, sizeof(mCred));
     }
 }
 
 void
 nsAuthSSPI::Reset()
 {
+    mIsFirst = true;
+
+    if (mCertDERData){
+        nsMemory::Free(mCertDERData);
+        mCertDERData = nsnull;
+        mCertDERLength = 0;   
+    }
+
     if (mCtxt.dwLower || mCtxt.dwUpper) {
         (sspi->DeleteSecurityContext)(&mCtxt);
         memset(&mCtxt, 0, sizeof(mCtxt));
     }
 }
 
 NS_IMPL_ISUPPORTS1(nsAuthSSPI, nsIAuthModule)
 
@@ -224,16 +236,20 @@ NS_IMETHODIMP
 nsAuthSSPI::Init(const char *serviceName,
                  PRUint32    serviceFlags,
                  const PRUnichar *domain,
                  const PRUnichar *username,
                  const PRUnichar *password)
 {
     LOG(("  nsAuthSSPI::Init\n"));
 
+    mIsFirst = true;
+    mCertDERLength = 0;
+    mCertDERData = nsnull;
+
     // The caller must supply a service name to be used. (For why we now require
     // a service name for NTLM, see bug 487872.)
     NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
 
     nsresult rv;
 
     // XXX lazy initialization like this assumes that we are single threaded
     if (!sspi) {
@@ -309,73 +325,186 @@ nsAuthSSPI::Init(const char *serviceName
                                            &mCred,
                                            &useBefore);
     if (rc != SEC_E_OK)
         return NS_ERROR_UNEXPECTED;
     LOG(("AcquireCredentialsHandle() succeeded.\n"));
     return NS_OK;
 }
 
+// The arguments inToken and inTokenLen are used to pass in the server
+// certificate (when available) in the first call of the function. The
+// second time these arguments hold an input token. 
 NS_IMETHODIMP
 nsAuthSSPI::GetNextToken(const void *inToken,
                          PRUint32    inTokenLen,
                          void      **outToken,
                          PRUint32   *outTokenLen)
 {
+    // String for end-point bindings.
+    const char end_point[] = "tls-server-end-point:"; 
+    const int end_point_length = sizeof(end_point) - 1;
+    const int hash_size = 32;  // Size of a SHA256 hash.
+    const int cbt_size = hash_size + end_point_length;
+	
     SECURITY_STATUS rc;
     TimeStamp ignored;
 
     DWORD ctxAttr, ctxReq = 0;
     CtxtHandle *ctxIn;
     SecBufferDesc ibd, obd;
-    SecBuffer ib, ob;
+    // Optional second input buffer for the CBT (Channel Binding Token)
+    SecBuffer ib[2], ob;
+    // Pointer to the block of memory that stores the CBT
+    char* sspi_cbt = nsnull;
+    SEC_CHANNEL_BINDINGS pendpoint_binding;
 
     LOG(("entering nsAuthSSPI::GetNextToken()\n"));
 
     if (!mCred.dwLower && !mCred.dwUpper) {
         LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
         return NS_ERROR_NOT_INITIALIZED;
     }
 
     if (mServiceFlags & REQ_DELEGATE)
         ctxReq |= ISC_REQ_DELEGATE;
     if (mServiceFlags & REQ_MUTUAL_AUTH)
         ctxReq |= ISC_REQ_MUTUAL_AUTH;
 
     if (inToken) {
-        ib.BufferType = SECBUFFER_TOKEN;
-        ib.cbBuffer = inTokenLen;
-        ib.pvBuffer = (void *) inToken;
-        ibd.ulVersion = SECBUFFER_VERSION;
-        ibd.cBuffers = 1;
-        ibd.pBuffers = &ib;
-        ctxIn = &mCtxt;
-    }
-    else {
-        // If there is no input token, then we are starting a new
-        // authentication sequence.  If we have already initialized our
-        // security context, then we're in trouble because it means that the
-        // first sequence failed.  We need to bail or else we might end up in
-        // an infinite loop.
-        if (mCtxt.dwLower || mCtxt.dwUpper) {
+        if (mIsFirst) {
+            // First time if it comes with a token,
+            // the token represents the server certificate.
+            mIsFirst = false;
+            mCertDERLength = inTokenLen;
+            mCertDERData = nsMemory::Alloc(inTokenLen);
+            if (!mCertDERData)
+                return NS_ERROR_OUT_OF_MEMORY;
+            memcpy(mCertDERData, inToken, inTokenLen);
+
+            // We are starting a new authentication sequence.  
+            // If we have already initialized our
+            // security context, then we're in trouble because it means that the
+            // first sequence failed.  We need to bail or else we might end up in
+            // an infinite loop.
+            if (mCtxt.dwLower || mCtxt.dwUpper) {
+                LOG(("Cannot restart authentication sequence!"));
+                return NS_ERROR_UNEXPECTED;
+            }
+            ctxIn = nsnull;
+            // The certificate needs to be erased before being passed 
+            // to InitializeSecurityContextW().
+            inToken = nsnull;
+            inTokenLen = 0;
+        } else {
+            ibd.ulVersion = SECBUFFER_VERSION;
+            ibd.cBuffers = 0;
+            ibd.pBuffers = ib;
+            
+            // If we have stored a certificate, the Channel Binding Token
+            // needs to be generated and sent in the first input buffer.
+            if (mCertDERLength > 0) {
+                // First we create a proper Endpoint Binding structure. 
+                pendpoint_binding.dwInitiatorAddrType = 0;
+                pendpoint_binding.cbInitiatorLength = 0;
+                pendpoint_binding.dwInitiatorOffset = 0;
+                pendpoint_binding.dwAcceptorAddrType = 0;
+                pendpoint_binding.cbAcceptorLength = 0;
+                pendpoint_binding.dwAcceptorOffset = 0;
+                pendpoint_binding.cbApplicationDataLength = cbt_size;
+                pendpoint_binding.dwApplicationDataOffset = 
+                                            sizeof(SEC_CHANNEL_BINDINGS);
+
+                // Then add it to the array of sec buffers accordingly.
+                ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+                ib[ibd.cBuffers].cbBuffer =
+                        pendpoint_binding.cbApplicationDataLength
+                        + pendpoint_binding.dwApplicationDataOffset;
+          
+                sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer);
+                if (!sspi_cbt){
+                    return NS_ERROR_OUT_OF_MEMORY;
+                }
+
+                // Helper to write in the memory block that stores the CBT
+                char* sspi_cbt_ptr = sspi_cbt;
+          
+                ib[ibd.cBuffers].pvBuffer = sspi_cbt;
+                ibd.cBuffers++;
+
+                memcpy(sspi_cbt_ptr, &pendpoint_binding,
+                       pendpoint_binding.dwApplicationDataOffset);
+                sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
+
+                memcpy(sspi_cbt_ptr, end_point, end_point_length);
+                sspi_cbt_ptr += end_point_length;
+          
+                // Start hashing. We are always doing SHA256, but depending
+                // on the certificate, a different alogirthm might be needed.
+                nsCAutoString hashString;
+
+                nsresult rv;
+                nsCOMPtr<nsICryptoHash> crypto;
+                crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Init(nsICryptoHash::SHA256);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Finish(false, hashString);
+                if (NS_FAILED(rv)) {
+                    nsMemory::Free(mCertDERData);
+                    mCertDERData = nsnull;
+                    mCertDERLength = 0;
+                    nsMemory::Free(sspi_cbt);
+                    return rv;
+                }
+          
+                // Once the hash has been computed, we store it in memory right
+                // after the Endpoint structure and the "tls-server-end-point:"
+                // char array.
+                memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
+          
+                // Free memory used to store the server certificate
+                nsMemory::Free(mCertDERData);
+                mCertDERData = nsnull;
+                mCertDERLength = 0;
+            } // End of CBT computation.
+
+            // We always need this SECBUFFER.
+            ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
+            ib[ibd.cBuffers].cbBuffer = inTokenLen;
+            ib[ibd.cBuffers].pvBuffer = (void *) inToken;
+            ibd.cBuffers++;
+            ctxIn = &mCtxt;
+        }
+    } else { // First time and without a token (no server certificate)
+        // We are starting a new authentication sequence.  If we have already 
+        // initialized our security context, then we're in trouble because it 
+        // means that the first sequence failed.  We need to bail or else we 
+        // might end up in an infinite loop.
+        if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
             LOG(("Cannot restart authentication sequence!"));
             return NS_ERROR_UNEXPECTED;
         }
-
         ctxIn = NULL;
+        mIsFirst = false;
     }
 
     obd.ulVersion = SECBUFFER_VERSION;
     obd.cBuffers = 1;
     obd.pBuffers = &ob;
     ob.BufferType = SECBUFFER_TOKEN;
     ob.cbBuffer = mMaxTokenLen;
     ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer);
-    if (!ob.pvBuffer)
+    if (!ob.pvBuffer){
+        if (sspi_cbt)
+            nsMemory::Free(sspi_cbt);
         return NS_ERROR_OUT_OF_MEMORY;
+    }
     memset(ob.pvBuffer, 0, ob.cbBuffer);
 
     NS_ConvertUTF8toUTF16 wSN(mServiceName);
     SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
 
     rc = (sspi->InitializeSecurityContextW)(&mCred,
                                             ctxIn,
                                             sn,
@@ -391,17 +520,19 @@ nsAuthSSPI::GetNextToken(const void *inT
     if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
 
 #ifdef PR_LOGGING
         if (rc == SEC_E_OK)
             LOG(("InitializeSecurityContext: succeeded.\n"));
         else
             LOG(("InitializeSecurityContext: continue.\n"));
 #endif
-
+        if (sspi_cbt)
+            nsMemory::Free(sspi_cbt);
+            
         if (!ob.cbBuffer) {
             nsMemory::Free(ob.pvBuffer);
             ob.pvBuffer = NULL;
         }
         *outToken = ob.pvBuffer;
         *outTokenLen = ob.cbBuffer;
 
         if (rc == SEC_E_OK)
--- a/extensions/auth/nsAuthSSPI.h
+++ b/extensions/auth/nsAuthSSPI.h
@@ -15,16 +15,17 @@
  * The Original Code is the SSPI NegotiateAuth Module.
  *
  * The Initial Developer of the Original Code is IBM Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2004
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
+ *   Guillermo Robla Vicario <groblavicario@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -76,11 +77,14 @@ private:
     CtxtHandle   mCtxt;
     nsCString    mServiceName;
     PRUint32     mServiceFlags;
     PRUint32     mMaxTokenLen;
     pType        mPackage;
     nsString     mDomain;
     nsString     mUsername;
     nsString     mPassword;
+    bool         mIsFirst;	
+    void*        mCertDERData; 
+    PRUint32     mCertDERLength;
 };
 
 #endif /* nsAuthSSPI_h__ */
--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -2201,77 +2201,16 @@ static cairo_status_t
 
     }
     if (status != CAIRO_STATUS_SUCCESS)
 	return _cairo_span_renderer_set_error (abstract_renderer,
 					       status);
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_bool_t
-_cairo_win32_surface_check_span_renderer (cairo_operator_t	  op,
-					  const cairo_pattern_t  *pattern,
-					  void			 *abstract_dst,
-					  cairo_antialias_t	  antialias)
-{
-    (void) op;
-    (void) pattern;
-    (void) abstract_dst;
-    (void) antialias;
-    return TRUE;
-}
-
-static cairo_span_renderer_t *
-_cairo_win32_surface_create_span_renderer (cairo_operator_t	 op,
-					   const cairo_pattern_t  *pattern,
-					   void			*abstract_dst,
-					   cairo_antialias_t	 antialias,
-					   const cairo_composite_rectangles_t *rects,
-					   cairo_region_t	*clip_region)
-{
-    cairo_win32_surface_t *dst = abstract_dst;
-    cairo_win32_surface_span_renderer_t *renderer;
-    cairo_status_t status;
-    int width = rects->bounded.width;
-    int height = rects->bounded.height;
-
-    renderer = calloc(1, sizeof(*renderer));
-    if (renderer == NULL)
-	return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
-
-    renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy;
-    renderer->base.finish = _cairo_win32_surface_span_renderer_finish;
-    renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows;
-    renderer->op = op;
-    renderer->pattern = pattern;
-    renderer->antialias = antialias;
-    renderer->dst = dst;
-    renderer->clip_region = clip_region;
-
-    renderer->composite_rectangles = *rects;
-
-    /* TODO: support rendering to A1 surfaces (or: go add span
-     * compositing to pixman.) */
-    renderer->mask = (cairo_image_surface_t *)
-	cairo_image_surface_create (CAIRO_FORMAT_A8,
-				    width, height);
-
-    status = cairo_surface_status (&renderer->mask->base);
-
-    if (status != CAIRO_STATUS_SUCCESS) {
-	_cairo_win32_surface_span_renderer_destroy (renderer);
-	return _cairo_span_renderer_create_in_error (status);
-    }
-
-    renderer->mask_data = renderer->mask->data - rects->bounded.x - rects->bounded.y * renderer->mask->stride;
-    renderer->mask_stride = renderer->mask->stride;
-    return &renderer->base;
-}
-
-
 static cairo_int_status_t
 _cairo_win32_surface_paint (void			*abstract_surface,
 			    cairo_operator_t		 op,
 			    const cairo_pattern_t	*source,
 			    cairo_clip_t		*clip)
 {
     cairo_win32_surface_t *surface = abstract_surface;
 
@@ -3263,64 +3202,16 @@ static cairo_status_t
                     extents->width, extents->height,
 		    clip_region);
 	}
 	cairo_pattern_destroy (mask_pattern);
 
     }
     return status;
 }
-#if 0
-static cairo_span_renderer_t *
-_cairo_win32_surface_create_span_renderer (cairo_operator_t	 op,
-					   const cairo_pattern_t  *pattern,
-					   void			*abstract_dst,
-					   cairo_antialias_t	 antialias,
-					   const cairo_composite_rectangles_t *rects,
-					   cairo_region_t	*clip_region)
-{
-    cairo_win32_surface_t *dst = abstract_dst;
-    cairo_win32_surface_span_renderer_t *renderer;
-    cairo_status_t status;
-    int width = rects->bounded.width;
-    int height = rects->bounded.height;
-
-    renderer = calloc(1, sizeof(*renderer));
-    if (renderer == NULL)
-	return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
-
-    renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy;
-    renderer->base.finish = _cairo_win32_surface_span_renderer_finish;
-    renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows;
-    renderer->op = op;
-    renderer->pattern = pattern;
-    renderer->antialias = antialias;
-    renderer->dst = dst;
-    renderer->clip_region = clip_region;
-
-    renderer->composite_rectangles = *rects;
-
-    /* TODO: support rendering to A1 surfaces (or: go add span
-     * compositing to pixman.) */
-    renderer->mask = (cairo_image_surface_t *)
-	cairo_image_surface_create (CAIRO_FORMAT_A8,
-				    width, height);
-
-    status = cairo_surface_status (&renderer->mask->base);
-
-    if (status != CAIRO_STATUS_SUCCESS) {
-	_cairo_win32_surface_span_renderer_destroy (renderer);
-	return _cairo_span_renderer_create_in_error (status);
-    }
-
-    renderer->mask_data = renderer->mask->data - rects->bounded.x - rects->bounded.y * renderer->mask->stride;
-    renderer->mask_stride = renderer->mask->stride;
-    return &renderer->base;
-}
-#endif
 
 typedef struct _cairo_image_surface_span_renderer {
     cairo_span_renderer_t base;
 
     uint8_t *mask_data;
     uint32_t mask_stride;
 } cairo_image_surface_span_renderer_t;
 
@@ -3633,17 +3524,17 @@ cairo_status_t
 	goto DO_TRAPS;
 
     if (_cairo_operator_bounded_by_mask (op)) {
 	_cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
 	if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
 	    goto CLEANUP;
     }
 
-    if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
 	cairo_composite_spans_info_t info;
 
 	info.polygon = &polygon;
 	info.fill_rule = CAIRO_FILL_RULE_WINDING;
 	info.antialias = antialias;
 
 	status = _clip_and_composite (clip, op, source,
 				      _composite_spans,
@@ -3750,17 +3641,17 @@ cairo_status_t
 	if (likely (status == CAIRO_STATUS_SUCCESS))
 	    goto DO_TRAPS;
 
 	if (unlikely (_cairo_status_is_error (status)))
 	    goto CLEANUP;
     }
 
 
-    if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
 	cairo_composite_spans_info_t info;
 
 	info.polygon = &polygon;
 	info.fill_rule = fill_rule;
 	info.antialias = antialias;
 
 	status = _clip_and_composite (clip, op, source,
 				      _composite_spans,
@@ -3785,443 +3676,30 @@ cairo_status_t
     _cairo_traps_fini (&traps);
     _cairo_polygon_fini (&polygon);
     if (clip_boxes != boxes_stack)
 	free (clip_boxes);
 
     return status;
 }
 
-typedef struct {
-    cairo_scaled_font_t *font;
-    cairo_glyph_t *glyphs;
-    int num_glyphs;
-} cairo_show_glyphs_info_t;
-
-static cairo_status_t
-_cairo_surface_old_show_glyphs_draw_func (void                          *closure,
-					  cairo_operator_t               op,
-					  const cairo_pattern_t         *src,
-					  cairo_surface_t               *dst,
-					  int                            dst_x,
-					  int                            dst_y,
-					  const cairo_rectangle_int_t	*extents,
-					  cairo_region_t		*clip_region)
-{
-    cairo_show_glyphs_info_t *glyph_info = closure;
-    cairo_status_t status;
-    cairo_region_t *extents_region = NULL;
-
-    if (clip_region == NULL &&
-        !_cairo_operator_bounded_by_source (op)) {
-        extents_region = cairo_region_create_rectangle (extents);
-        if (unlikely (extents_region->status))
-            return extents_region->status;
-        cairo_region_translate (extents_region, -dst_x, -dst_y);
-        clip_region = extents_region;
-    }
-
-    /* Modifying the glyph array is fine because we know that this function
-     * will be called only once, and we've already made a copy of the
-     * glyphs in the wrapper.
-     */
-    if (dst_x != 0 || dst_y != 0) {
-	int i;
-
-	for (i = 0; i < glyph_info->num_glyphs; ++i) {
-	    ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x;
-	    ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y;
-	}
-    }
-
-    status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
-					     dst,
-					     extents->x, extents->y,
-					     extents->x - dst_x,
-					     extents->y - dst_y,
-					     extents->width,
-					     extents->height,
-					     glyph_info->glyphs,
-					     glyph_info->num_glyphs,
-					     clip_region);
-
-    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	status = _cairo_scaled_font_show_glyphs (glyph_info->font,
-                                                 op,
-                                                 src, dst,
-                                                 extents->x,         extents->y,
-                                                 extents->x - dst_x,
-                                                 extents->y - dst_y,
-                                                 extents->width,     extents->height,
-                                                 glyph_info->glyphs,
-                                                 glyph_info->num_glyphs,
-                                                 clip_region);
-    }
-
-    if (extents_region)
-        cairo_region_destroy (extents_region);
-
-    return status;
-}
-
-cairo_status_t
-_cairo_win32_surface_fallback_show_glyphs (cairo_surface_t		*surface,
-				     cairo_operator_t		 op,
-				     const cairo_pattern_t	*source,
-				     cairo_glyph_t		*glyphs,
-				     int			 num_glyphs,
-				     cairo_scaled_font_t	*scaled_font,
-				     cairo_clip_t		*clip)
-{
-    cairo_show_glyphs_info_t glyph_info;
-    cairo_composite_rectangles_t extents;
-    cairo_rectangle_int_t rect;
-    cairo_status_t status;
-
-    if (!_cairo_surface_get_extents (surface, &rect))
-        ASSERT_NOT_REACHED;
-
-    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
-							  &rect,
-							  op, source,
-							  scaled_font,
-							  glyphs, num_glyphs,
-							  clip,
-							  NULL);
-    if (unlikely (status))
-	return status;
-
-    if (_cairo_clip_contains_rectangle (clip, &extents.mask))
-	clip = NULL;
-
-    if (clip != NULL && extents.is_bounded) {
-	status = _cairo_clip_rectangle (clip, &extents.bounded);
-	if (unlikely (status))
-	    return status;
-    }
-
-    glyph_info.font = scaled_font;
-    glyph_info.glyphs = glyphs;
-    glyph_info.num_glyphs = num_glyphs;
-
-    return _clip_and_composite (clip, op, source,
-				_cairo_surface_old_show_glyphs_draw_func,
-				&glyph_info,
-				surface,
-                                extents.is_bounded ? &extents.bounded : &extents.unbounded);
-}
-
-cairo_surface_t *
-_cairo_win32_surface_fallback_snapshot (cairo_surface_t *surface)
-{
-    cairo_surface_t *snapshot;
-    cairo_status_t status;
-    cairo_format_t format;
-    cairo_surface_pattern_t pattern;
-    cairo_image_surface_t *image;
-    void *image_extra;
-
-    status = _cairo_surface_acquire_source_image (surface,
-						  &image, &image_extra);
-    if (unlikely (status))
-	return _cairo_surface_create_in_error (status);
-
-    format = image->format;
-    if (format == CAIRO_FORMAT_INVALID) {
-	/* Non-standard images formats can be generated when retrieving
-	 * images from unusual xservers, for example.
-	 */
-	format = _cairo_format_from_content (image->base.content);
-    }
-    snapshot = cairo_image_surface_create (format,
-					   image->width,
-					   image->height);
-    if (cairo_surface_status (snapshot)) {
-	_cairo_surface_release_source_image (surface, image, image_extra);
-	return snapshot;
-    }
-
-    _cairo_pattern_init_for_surface (&pattern, &image->base);
-    status = _cairo_surface_paint (snapshot,
-				   CAIRO_OPERATOR_SOURCE,
-				   &pattern.base,
-				   NULL);
-    _cairo_pattern_fini (&pattern.base);
-    _cairo_surface_release_source_image (surface, image, image_extra);
-    if (unlikely (status)) {
-	cairo_surface_destroy (snapshot);
-	return _cairo_surface_create_in_error (status);
-    }
-
-    return snapshot;
-}
-
-cairo_status_t
-_cairo_win32_surface_fallback_composite (cairo_operator_t		 op,
-				   const cairo_pattern_t	*src,
-				   const cairo_pattern_t	*mask,
-				   cairo_surface_t		*dst,
-				   int				 src_x,
-				   int				 src_y,
-				   int				 mask_x,
-				   int				 mask_y,
-				   int				 dst_x,
-				   int				 dst_y,
-				   unsigned int			 width,
-				   unsigned int			 height,
-				   cairo_region_t		*clip_region)
-{
-    fallback_state_t state;
-    cairo_region_t *fallback_region = NULL;
-    cairo_status_t status;
-
-    status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
-    if (unlikely (status))
-	return status;
-
-    /* We know this will never fail with the image backend; but
-     * instead of calling into it directly, we call
-     * _cairo_surface_composite so that we get the correct device
-     * offset handling.
-     */
-
-    if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) {
-	fallback_region = cairo_region_copy (clip_region);
-	status = fallback_region->status;
-	if (unlikely (status))
-	    goto FAIL;
-
-	cairo_region_translate (fallback_region,
-				-state.image_rect.x,
-				-state.image_rect.y);
-	clip_region = fallback_region;
-    }
-
-    status = _cairo_surface_composite (op, src, mask,
-				       &state.image->base,
-				       src_x, src_y, mask_x, mask_y,
-				       dst_x - state.image_rect.x,
-				       dst_y - state.image_rect.y,
-				       width, height,
-				       clip_region);
-  FAIL:
-    if (fallback_region != NULL)
-	cairo_region_destroy (fallback_region);
-    _fallback_fini (&state);
-
-    return status;
-}
-
-cairo_status_t
-_cairo_win32_surface_fallback_fill_rectangles (cairo_surface_t         *surface,
-					 cairo_operator_t	  op,
-					 const cairo_color_t	 *color,
-					 cairo_rectangle_int_t   *rects,
-					 int			  num_rects)
-{
-    fallback_state_t state;
-    cairo_rectangle_int_t *offset_rects = NULL;
-    cairo_status_t status;
-    int x1, y1, x2, y2;
-    int i;
-
-    assert (surface->snapshot_of == NULL);
-
-    if (num_rects <= 0)
-	return CAIRO_STATUS_SUCCESS;
-
-    /* Compute the bounds of the rectangles, so that we know what area of the
-     * destination surface to fetch
-     */
-    x1 = rects[0].x;
-    y1 = rects[0].y;
-    x2 = rects[0].x + rects[0].width;
-    y2 = rects[0].y + rects[0].height;
-
-    for (i = 1; i < num_rects; i++) {
-	if (rects[i].x < x1)
-	    x1 = rects[i].x;
-	if (rects[i].y < y1)
-	    y1 = rects[i].y;
-
-	if ((int) (rects[i].x + rects[i].width) > x2)
-	    x2 = rects[i].x + rects[i].width;
-	if ((int) (rects[i].y + rects[i].height) > y2)
-	    y2 = rects[i].y + rects[i].height;
-    }
-
-    status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1);
-    if (unlikely (status))
-	return status;
-
-    /* If the fetched image isn't at 0,0, we need to offset the rectangles */
-
-    if (state.image_rect.x != 0 || state.image_rect.y != 0) {
-	offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t));
-	if (unlikely (offset_rects == NULL)) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto DONE;
-	}
-
-	for (i = 0; i < num_rects; i++) {
-	    offset_rects[i].x = rects[i].x - state.image_rect.x;
-	    offset_rects[i].y = rects[i].y - state.image_rect.y;
-	    offset_rects[i].width = rects[i].width;
-	    offset_rects[i].height = rects[i].height;
-	}
-
-	rects = offset_rects;
-    }
-
-    status = _cairo_surface_fill_rectangles (&state.image->base,
-					     op, color,
-					     rects, num_rects);
-
-    free (offset_rects);
-
- DONE:
-    _fallback_fini (&state);
-
-    return status;
-}
-
-cairo_status_t
-_cairo_win32_surface_fallback_composite_trapezoids (cairo_operator_t		op,
-					      const cairo_pattern_t    *pattern,
-					      cairo_surface_t	       *dst,
-					      cairo_antialias_t		antialias,
-					      int			src_x,
-					      int			src_y,
-					      int			dst_x,
-					      int			dst_y,
-					      unsigned int		width,
-					      unsigned int		height,
-					      cairo_trapezoid_t	       *traps,
-					      int			num_traps,
-					      cairo_region_t		*clip_region)
-{
-    fallback_state_t state;
-    cairo_region_t *fallback_region = NULL;
-    cairo_trapezoid_t *offset_traps = NULL;
-    cairo_status_t status;
-
-    status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
-    if (unlikely (status))
-	return status;
-
-    /* If the destination image isn't at 0,0, we need to offset the trapezoids */
-
-    if (state.image_rect.x != 0 || state.image_rect.y != 0) {
-	offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t));
-	if (offset_traps == NULL) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto FAIL;
-	}
-
-	_cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps,
-                                                    - state.image_rect.x, - state.image_rect.y,
-                                                    1.0, 1.0);
-	traps = offset_traps;
-
-	/* similarly we need to adjust the region */
-	if (clip_region != NULL) {
-	    fallback_region = cairo_region_copy (clip_region);
-	    status = fallback_region->status;
-	    if (unlikely (status))
-		goto FAIL;
-
-	    cairo_region_translate (fallback_region,
-				    -state.image_rect.x,
-				    -state.image_rect.y);
-	    clip_region = fallback_region;
-	}
-    }
-
-    status = _cairo_surface_composite_trapezoids (op, pattern,
-					          &state.image->base,
-						  antialias,
-						  src_x, src_y,
-						  dst_x - state.image_rect.x,
-						  dst_y - state.image_rect.y,
-						  width, height,
-						  traps, num_traps,
-						  clip_region);
- FAIL:
-    if (offset_traps != NULL)
-	free (offset_traps);
-
-    if (fallback_region != NULL)
-	cairo_region_destroy (fallback_region);
-
-    _fallback_fini (&state);
-
-    return status;
-}
-
-cairo_status_t
-_cairo_win32_surface_fallback_clone_similar (cairo_surface_t	*surface,
-				       cairo_surface_t	*src,
-				       int		 src_x,
-				       int		 src_y,
-				       int		 width,
-				       int		 height,
-				       int		*clone_offset_x,
-				       int		*clone_offset_y,
-				       cairo_surface_t **clone_out)
-{
-    cairo_surface_t *new_surface;
-    cairo_surface_pattern_t pattern;
-    cairo_status_t status;
-
-    new_surface = _cairo_surface_create_similar_scratch (surface,
-							 src->content,
-							 width, height);
-    if (new_surface == NULL)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    if (unlikely (new_surface->status))
-	return new_surface->status;
-
-    /* We have to copy these here, so that the coordinate spaces are correct */
-    new_surface->device_transform = src->device_transform;
-    new_surface->device_transform_inverse = src->device_transform_inverse;
-
-    _cairo_pattern_init_for_surface (&pattern, src);
-    cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y);
-    pattern.base.filter = CAIRO_FILTER_NEAREST;
-
-    status = _cairo_surface_paint (new_surface,
-				   CAIRO_OPERATOR_SOURCE,
-				   &pattern.base,
-				   NULL);
-    _cairo_pattern_fini (&pattern.base);
-
-    if (unlikely (status)) {
-	cairo_surface_destroy (new_surface);
-	return status;
-    }
-
-    *clone_offset_x = src_x;
-    *clone_offset_y = src_y;
-    *clone_out = new_surface;
-    return CAIRO_STATUS_SUCCESS;
-}
 static const cairo_surface_backend_t cairo_win32_surface_backend = {
     CAIRO_SURFACE_TYPE_WIN32,
     _cairo_win32_surface_create_similar,
     _cairo_win32_surface_finish,
     _cairo_win32_surface_acquire_source_image,
     _cairo_win32_surface_release_source_image,
     _cairo_win32_surface_acquire_dest_image,
     _cairo_win32_surface_release_dest_image,
     NULL, /* clone similar */
     _cairo_win32_surface_composite,
     _cairo_win32_surface_fill_rectangles,
     NULL, /* composite_trapezoids */
-    _cairo_win32_surface_create_span_renderer,
-    _cairo_win32_surface_check_span_renderer,
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
     NULL, /* copy_page */
     NULL, /* show_page */
     _cairo_win32_surface_get_extents,
     NULL, /* old_show_glyphs */
     NULL, /* get_font_options */
     _cairo_win32_surface_flush,
     NULL, /* mark_dirty_rectangle */
     NULL, /* scaled_font_fini */
--- a/hal/fallback/FallbackHal.cpp
+++ b/hal/fallback/FallbackHal.cpp
@@ -61,12 +61,13 @@ void
 DisableBatteryNotifications()
 {}
 
 void
 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 {
   aBatteryInfo->level() = dom::battery::kDefaultLevel;
   aBatteryInfo->charging() = dom::battery::kDefaultCharging;
+  aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
 }
 
 } // hal_impl
 } // namespace mozilla
--- a/hal/linux/LinuxHal.cpp
+++ b/hal/linux/LinuxHal.cpp
@@ -61,14 +61,15 @@ void
 DisableBatteryNotifications()
 {}
 
 void
 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 {
   aBatteryInfo->level() = dom::battery::kDefaultLevel;
   aBatteryInfo->charging() = dom::battery::kDefaultCharging;
+  aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
 }
 #endif // !MOZ_ENABLE_DBUS
 
 } // hal_impl
 } // mozilla
 
--- a/hal/linux/UPowerClient.cpp
+++ b/hal/linux/UPowerClient.cpp
@@ -75,18 +75,19 @@ namespace hal_impl {
 class UPowerClient
 {
 public:
   static UPowerClient* GetInstance();
 
   void BeginListening();
   void StopListening();
 
-  float GetLevel();
-  bool IsCharging();
+  double GetLevel();
+  bool   IsCharging();
+  double GetRemainingTime();
 
   ~UPowerClient();
 
 private:
   UPowerClient();
 
   enum States {
     eState_Unknown = 0,
@@ -133,22 +134,24 @@ private:
   DBusGConnection* mDBusConnection;
 
   // The DBus proxy object to upower.
   DBusGProxy* mUPowerProxy;
 
   // The path of the tracked device.
   gchar* mTrackedDevice;
 
-  float mLevel;
+  double mLevel;
   bool mCharging;
+  double mRemainingTime;
 
   static UPowerClient* sInstance;
 
   static const guint sDeviceTypeBattery = 2;
+  static const guint64 kUPowerUnknownRemainingTime = 0;
 };
 
 /*
  * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
  *                   mozilla::hal_impl::DisableBatteryNotifications,
  *               and mozilla::hal_impl::GetCurrentBatteryInformation.
  */
 
@@ -166,16 +169,17 @@ DisableBatteryNotifications()
 
 void
 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 {
   UPowerClient* upowerClient = UPowerClient::GetInstance();
 
   aBatteryInfo->level() = upowerClient->GetLevel();
   aBatteryInfo->charging() = upowerClient->IsCharging();
+  aBatteryInfo->remainingTime() = upowerClient->GetRemainingTime();
 }
 
 /*
  * Following is the implementation of UPowerClient.
  */
 
 UPowerClient* UPowerClient::sInstance = nsnull;
 
@@ -190,16 +194,17 @@ UPowerClient::GetInstance()
 }
 
 UPowerClient::UPowerClient()
   : mDBusConnection(nsnull)
   , mUPowerProxy(nsnull)
   , mTrackedDevice(nsnull)
   , mLevel(kDefaultLevel)
   , mCharging(kDefaultCharging)
+  , mRemainingTime(kUnknownRemainingTime)
 {
 }
 
 UPowerClient::~UPowerClient()
 {
   NS_ASSERTION(!mDBusConnection && !mUPowerProxy && !mTrackedDevice,
                "The observers have not been correctly removed! "
                "(StopListening should have been called)");
@@ -269,16 +274,17 @@ UPowerClient::StopListening()
   mUPowerProxy = nsnull;
 
   dbus_g_connection_unref(mDBusConnection);
   mDBusConnection = nsnull;
 
   // We should now show the default values, not the latest we got.
   mLevel = kDefaultLevel;
   mCharging = kDefaultCharging;
+  mRemainingTime = kUnknownRemainingTime;
 }
 
 void
 UPowerClient::UpdateTrackedDevice()
 {
   GType typeGPtrArray = dbus_g_type_get_collection("GPtrArray",
                                                    DBUS_TYPE_G_OBJECT_PATH);
   GPtrArray* devices = nsnull;
@@ -327,17 +333,19 @@ UPowerClient::DeviceChanged(DBusGProxy* 
   if (g_ascii_strcasecmp(aObjectPath, aListener->mTrackedDevice)) {
 #endif
     return;
   }
 
   nsAutoRef<GHashTable> hashTable(aListener->GetDeviceProperties(aObjectPath));
   aListener->UpdateSavedInfo(hashTable);
 
-  hal::NotifyBatteryChange(hal::BatteryInformation(aListener->mLevel, aListener->mCharging));
+  hal::NotifyBatteryChange(hal::BatteryInformation(aListener->mLevel,
+                                                   aListener->mCharging,
+                                                   aListener->mRemainingTime));
 }
 
 /* static */ DBusHandlerResult
 UPowerClient::ConnectionSignalFilter(DBusConnection* aConnection,
                                      DBusMessage* aMessage, void* aData)
 {
   if (dbus_message_is_signal(aMessage, DBUS_INTERFACE_LOCAL, "Disconnected")) {
     static_cast<UPowerClient*>(aData)->StopListening();
@@ -369,41 +377,78 @@ UPowerClient::GetDeviceProperties(const 
   }
 
   return hashTable;
 }
 
 void
 UPowerClient::UpdateSavedInfo(GHashTable* aHashTable)
 {
-  mLevel = g_value_get_double(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "Percentage")))/100.f;
+  bool isFull = false;
+
+  mLevel = g_value_get_double(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "Percentage")))*0.01;
 
+  /*
+   * State values are confusing...
+   * First of all, after looking at upower sources (0.9.13), it seems that
+   * PendingDischarge and PendingCharge are not used.
+   * In addition, FullyCharged and Empty states are not clear because we do not
+   * know if the battery is actually charging or not. Those values come directly
+   * from sysfs (in the Linux kernel) which have four states: "Empty", "Full",
+   * "Charging" and "Discharging". In sysfs, "Empty" and "Full" are also only
+   * related to the level, not to the charging state.
+   * In this code, we are going to assume that Full means charging and Empty
+   * means discharging because if that is not the case, the state should not
+   * last a long time (actually, it should disappear at the following update).
+   * It might be even very hard to see real cases where the state is Empty and
+   * the battery is charging or the state is Full and the battery is discharging
+   * given that plugging/unplugging the battery should have an impact on the
+   * level.
+   */
   switch (g_value_get_uint(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "State")))) {
     case eState_Unknown:
       mCharging = kDefaultCharging;
       break;
+    case eState_FullyCharged:
+      isFull = true;
     case eState_Charging:
-    case eState_FullyCharged:
     case eState_PendingCharge:
       mCharging = true;
       break;
     case eState_Discharging:
     case eState_Empty:
     case eState_PendingDischarge:
       mCharging = false;
       break;
   }
+
+  if (isFull) {
+    mRemainingTime = 0;
+  } else {
+    mRemainingTime = mCharging ? g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToFull")))
+                               : g_value_get_int64(static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "TimeToEmpty")));
+
+    if (mRemainingTime == kUPowerUnknownRemainingTime) {
+      mRemainingTime = kUnknownRemainingTime;
+    }
+  }
 }
 
-float
+double
 UPowerClient::GetLevel()
 {
   return mLevel;
 }
 
 bool
 UPowerClient::IsCharging()
 {
   return mCharging;
 }
 
+double
+UPowerClient::GetRemainingTime()
+{
+  return mRemainingTime;
+}
+
 } // namespace hal_impl
 } // namespace mozilla
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -39,18 +39,19 @@
 
 include protocol PContent;
 include protocol PBrowser;
 
 namespace mozilla {
 
 namespace hal {
   struct BatteryInformation {
-    float level;
-    bool  charging;
+    double level;
+    bool   charging;
+    double remainingTime;
   };
 }
 
 namespace hal_sandbox {
 
 sync protocol PHal {
     manager PContent;
 
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -58,40 +58,47 @@ interface imgIDecoderObserver;
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
 }
 }
 
 class nsIFrame;
+
+namespace mozilla {
+class TimeStamp;
+}
+
 %}
 
 [ptr] native gfxImageSurface(gfxImageSurface);
 [ptr] native gfxASurface(gfxASurface);
 native gfxImageFormat(gfxASurface::gfxImageFormat);
 [ptr] native gfxContext(gfxContext);
 [ref] native gfxMatrix(gfxMatrix);
 [ref] native gfxRect(gfxRect);
 native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
 [ref] native nsIntRect(nsIntRect);
 [ref] native nsIntSize(nsIntSize);
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native ImageContainer(mozilla::layers::ImageContainer);
 [ptr] native LayerManager(mozilla::layers::LayerManager);
+[ref] native TimeStamp(mozilla::TimeStamp);
+
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces, and permits users to extract subregions
  * as other imgIContainers. It also allows drawing of images on to Thebes
  * contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, uuid(239dfa70-2285-4d63-99cd-e9b7ff9555c7)]
+[scriptable, uuid(8c82b89f-f90c-4a31-a544-6e1f759673d4)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute PRInt32 width;
 
@@ -274,16 +281,23 @@ interface imgIContainer : nsISupports
     *
     * Upon instantiation images have a lock count of zero. It is an error to
     * call this method without first having made a matching lockImage() call.
     * In other words, the lock count is not allowed to be negative.
     */
   void unlockImage();
 
   /**
+    * Indicates that this imgIContainer has been triggered to update
+    * its internal animation state. Likely this should only be called
+    * from within nsImageFrame or objects of similar type.
+    */
+  [notxpcom] void requestRefresh([const] in TimeStamp aTime);
+
+  /**
    * Animation mode Constants
    *   0 = normal
    *   1 = don't animate
    *   2 = loop once
    */
   const short kNormalAnimMode   = 0;
   const short kDontAnimMode     = 1;
   const short kLoopOnceAnimMode = 2;
--- a/image/public/imgIDecoderObserver.idl
+++ b/image/public/imgIDecoderObserver.idl
@@ -54,17 +54,17 @@ interface imgIContainer;
  * observing imgIRequest objects.  In the former case, aRequest is
  * always null.
  *
  * We make the distinction here between "load" and "decode" notifications. Load
  * notifications are fired as the image is loaded from the network or
  * filesystem. Decode notifications are fired as the image is decoded. If an
  * image is decoded on load and not visibly discarded, decode notifications are
  * nested logically inside load notifications as one might expect. However, with
- * decode-on-draw, the set of decode notifications can come completely _after_
+ * decode-on-draw, the set of decode notifications can imgRcome completely _after_
  * the load notifications, and can come multiple times if the image is
  * discardable. Moreover, they can be interleaved in various ways. In general,
  * any presumed ordering between load and decode notifications should not be
  * relied upon.
  *
  * Decode notifications may or may not be synchronous, depending on the
  * situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
  * triggers a decode, all notifications that can be generated from the currently
@@ -72,17 +72,17 @@ interface imgIContainer;
  * all, some, or none of the notifications may fire before the call returns.
  *
  * This interface will be cleaned up in bug 505385.
  *
  * @author Stuart Parmenter <pavlov@netscape.com>
  * @version 0.1
  * @see imagelib2
  */
-[scriptable, uuid(9f6bfbee-9e04-43a0-b8f6-2159973efec8)]
+[scriptable, uuid(2e5fa0c4-57f8-4d16-bda3-1daeba9caa34)]
 interface imgIDecoderObserver : imgIContainerObserver
 {
   /**
    * Load notification.
    *
    * called at the same time that nsIRequestObserver::onStartRequest would be
    * (used only for observers of imgIRequest objects, which are nsIRequests,
    * not imgIDecoder objects)
@@ -130,16 +130,22 @@ interface imgIDecoderObserver : imgICont
   void onStopFrame(in imgIRequest aRequest, in unsigned long aFrame);
 
   /**
    * Do not implement this. It is useless and going away.
    */
   void onStopContainer(in imgIRequest aRequest, in imgIContainer aContainer);
 
   /**
+   * Notification for when an image is known to be animated. This should be
+   * fired at the earliest possible time.
+   */
+  void onImageIsAnimated(in imgIRequest aRequest);
+
+  /**
    * In theory a decode notification, but currently a load notification.
    *
    * Ideally this would be called when the decode is complete. Unfortunately,
    * this is currently the only way to signal decoding errors to consumers,
    * and the only decoding errors that consumers care about (indeed, the only
    * ones that they're prepared to hear about) are failures to instantiate the
    * decoder (<img src="foo.html"> for example). Thus, currently this is just
    * a companion to onStopDecode to signal success or failure. This will be
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -50,16 +50,17 @@ Decoder::Decoder(RasterImage &aImage, im
   , mDecodeFlags(0)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mInitialized(false)
   , mSizeDecode(false)
   , mInFrame(false)
   , mDecodeDone(false)
   , mDataError(false)
+  , mIsAnimated(false)
 {
 }
 
 Decoder::~Decoder()
 {
   NS_WARN_IF_FALSE(!mInFrame, "Shutting down decoder mid-frame!");
   mInitialized = false;
 }
@@ -254,19 +255,24 @@ Decoder::PostFrameStop()
   NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
 
   // Update our state
   mInFrame = false;
 
   // Flush any invalidations before we finish the frame
   FlushInvalidations();
 
-  // Fire notification
-  if (mObserver)
+  // Fire notifications
+  if (mObserver) {
     mObserver->OnStopFrame(nsnull, mFrameCount - 1); // frame # is zero-indexed
+    if (mFrameCount > 1 && !mIsAnimated) {
+      mIsAnimated = true;
+      mObserver->OnImageIsAnimated(nsnull);
+    }
+  }
 }
 
 void
 Decoder::PostInvalidation(nsIntRect& aRect)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
 
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -213,14 +213,15 @@ private:
 
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
 
   nsresult mFailCode;
 
   bool mInitialized;
   bool mSizeDecode;
   bool mInFrame;
+  bool mIsAnimated;
 };
 
 } // namespace imagelib
 } // namespace mozilla
 
 #endif // MOZILLA_IMAGELIB_DECODER_H_
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -170,20 +170,20 @@ DiscardingEnabled()
 
   return enabled;
 }
 
 namespace mozilla {
 namespace imagelib {
 
 #ifndef DEBUG
-NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
+NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
                    nsISupportsWeakReference)
 #else
-NS_IMPL_ISUPPORTS5(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
+NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
                    imgIContainerDebug, nsISupportsWeakReference)
 #endif
 
 //******************************************************************************
 RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
   Image(aStatusTracker), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
@@ -310,16 +310,179 @@ RasterImage::Init(imgIDecoderObserver *a
   CONTAINER_ENSURE_SUCCESS(rv);
 
   // Mark us as initialized
   mInitialized = true;
 
   return NS_OK;
 }
 
+bool
+RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
+{
+  NS_ASSERTION(aTime <= TimeStamp::Now(),
+               "Given time appears to be in the future");
+
+  imgFrame* nextFrame = nsnull;
+  PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
+  PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
+  PRUint32 timeout = 0;
+
+  // Figure out if we have the next full frame. This is more complicated than
+  // just checking for mFrames.Length() because decoders append their frames
+  // before they're filled in.
+  NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
+                    "How did we get 2 indices too far by incrementing?");
+
+  // If we don't have a decoder, we know we've got everything we're going to
+  // get. If we do, we only display fully-downloaded frames; everything else
+  // gets delayed.
+  bool haveFullNextFrame = !mDecoder ||
+                           nextFrameIndex < mDecoder->GetCompleteFrameCount();
+
+  // If we're done decoding the next frame, go ahead and display it now and
+  // reinit with the next frame's delay time.
+  if (haveFullNextFrame) {
+    if (mFrames.Length() == nextFrameIndex) {
+      // End of Animation, unless we are looping forever
+
+      // If animation mode is "loop once", it's time to stop animating
+      if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
+        mAnimationFinished = PR_TRUE;
+        EvaluateAnimation();
+      }
+
+      // We may have used compositingFrame to build a frame, and then copied
+      // it back into mFrames[..].  If so, delete composite to save memory
+      if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
+        mAnim->compositingFrame = nsnull;
+      }
+
+      nextFrameIndex = 0;
+
+      if (mLoopCount > 0) {
+        mLoopCount--;
+      }
+
+      if (!mAnimating) {
+        // break out early if we are actually done animating
+        return false;
+      }
+    }
+
+    if (!(nextFrame = mFrames[nextFrameIndex])) {
+      // something wrong with the next frame, skip it
+      mAnim->currentAnimationFrameIndex = nextFrameIndex;
+      return false;
+    }
+
+    timeout = nextFrame->GetTimeout();
+
+  } else {
+    // Uh oh, the frame we want to show is currently being decoded (partial)
+    // Wait until the next refresh driver tick and try again
+    return false;
+  }
+
+  if (!(timeout > 0)) {
+    mAnimationFinished = PR_TRUE;
+    EvaluateAnimation();
+  }
+
+  imgFrame *frameToUse = nsnull;
+
+  if (nextFrameIndex == 0) {
+    frameToUse = nextFrame;
+    *aDirtyRect = mAnim->firstFrameRefreshArea;
+  } else {
+    imgFrame *curFrame = mFrames[currentFrameIndex];
+
+    if (!curFrame) {
+      return false;
+    }
+
+    // Change frame
+    if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
+                              nextFrame, nextFrameIndex))) {
+      // something went wrong, move on to next
+      NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
+      nextFrame->SetCompositingFailed(PR_TRUE);
+      mAnim->currentAnimationFrameIndex = nextFrameIndex;
+      mAnim->currentAnimationFrameTime = aTime;
+      return false;
+    }
+
+    nextFrame->SetCompositingFailed(PR_FALSE);
+  }
+
+  // Set currentAnimationFrameIndex at the last possible moment
+  mAnim->currentAnimationFrameIndex = nextFrameIndex;
+  mAnim->currentAnimationFrameTime = aTime;
+
+  return true;
+}
+
+//******************************************************************************
+// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
+NS_IMETHODIMP_(void)
+RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+{
+  if (!mAnimating || !ShouldAnimate()) {
+    return;
+  }
+
+  EnsureAnimExists();
+
+  // only advance the frame if the current time is greater than or
+  // equal to the current frame's end time.
+  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
+  bool frameAdvanced = false;
+
+  // The dirtyRect variable will contain an accumulation of the sub-rectangles
+  // that are dirty for each frame we advance in AdvanceFrame().
+  nsIntRect dirtyRect;
+
+  while (currentFrameEndTime <= aTime) {
+    TimeStamp oldFrameEndTime = currentFrameEndTime;
+    nsIntRect frameDirtyRect;
+    bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
+    frameAdvanced = frameAdvanced || didAdvance;
+    currentFrameEndTime = GetCurrentImgFrameEndTime();
+
+    // Accumulate the dirty area.
+    dirtyRect = dirtyRect.Union(frameDirtyRect);
+
+    // if we didn't advance a frame, and our frame end time didn't change,
+    // then we need to break out of this loop & wait for the frame(s)
+    // to finish downloading
+    if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
+      break;
+    }
+  }
+
+  if (frameAdvanced) {
+    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
+
+    if (!observer) {
+      NS_ERROR("Refreshing image after its imgRequest is gone");
+      StopAnimation();
+      return;
+    }
+
+    // Notify listeners that our frame has actually changed, but do this only
+    // once for all frames that we've now passed (if AdvanceFrame() was called
+    // more than once).
+    #ifdef DEBUG
+      mFramesNotified++;
+    #endif
+
+    observer->FrameChanged(this, &dirtyRect);
+  }
+}
+
 //******************************************************************************
 /* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
  *                                       [const] in nsIntRect aRegion,
  *                                       in PRUint32 aFlags); */
 NS_IMETHODIMP
 RasterImage::ExtractFrame(PRUint32 aWhichFrame,
                           const nsIntRect &aRegion,
                           PRUint32 aFlags,
@@ -491,16 +654,37 @@ PRUint32
 RasterImage::GetCurrentImgFrameIndex() const
 {
   if (mAnim)
     return mAnim->currentAnimationFrameIndex;
 
   return 0;
 }
 
+TimeStamp
+RasterImage::GetCurrentImgFrameEndTime() const
+{
+  imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
+  TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
+  PRInt64 timeout = currentFrame->GetTimeout();
+
+  if (timeout < 0) {
+    // We need to return a sentinel value in this case, because our logic
+    // doesn't work correctly if we have a negative timeout value. The reason
+    // this positive infinity was chosen was because it works with the loop in
+    // RequestRefresh() above.
+    return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX_VAL);
+  }
+
+  TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
+  TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
+
+  return currentFrameEndTime;
+}
+
 imgFrame*
 RasterImage::GetCurrentImgFrame()
 {
   return GetImgFrame(GetCurrentImgFrameIndex());
 }
 
 imgFrame*
 RasterImage::GetCurrentDrawableImgFrame()
@@ -1173,53 +1357,41 @@ RasterImage::StartAnimation()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
 
   EnsureAnimExists();
 
-  NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
-  
-  // Default timeout to 100: the timer notify code will do the right
-  // thing, so just get that started.
-  PRInt32 timeout = 100;
-  imgFrame *currentFrame = GetCurrentImgFrame();
+  imgFrame* currentFrame = GetCurrentImgFrame();
   if (currentFrame) {
-    timeout = currentFrame->GetTimeout();
-    if (timeout < 0) { // -1 means display this frame forever
+    if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
       mAnimationFinished = true;
       return NS_ERROR_ABORT;
     }
+
+    // We need to set the time that this initial frame was first displayed, as
+    // this is used in AdvanceFrame().
+    mAnim->currentAnimationFrameTime = TimeStamp::Now();
   }
   
-  mAnim->timer = do_CreateInstance("@mozilla.org/timer;1");
-  NS_ENSURE_TRUE(mAnim->timer, NS_ERROR_OUT_OF_MEMORY);
-  mAnim->timer->InitWithCallback(static_cast<nsITimerCallback*>(this),
-                                 timeout, nsITimer::TYPE_REPEATING_SLACK);
-  
   return NS_OK;
 }
 
 //******************************************************************************
 /* void stopAnimation (); */
 nsresult
 RasterImage::StopAnimation()
 {
   NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
 
   if (mError)
     return NS_ERROR_FAILURE;
 
-  if (mAnim->timer) {
-    mAnim->timer->Cancel();
-    mAnim->timer = nsnull;
-  }
-
   return NS_OK;
 }
 
 //******************************************************************************
 /* void resetAnimation (); */
 NS_IMETHODIMP
 RasterImage::ResetAnimation()
 {
@@ -1466,137 +1638,16 @@ nsresult
 RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
 {
   if (sizeHint && StoringSourceData())
     return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   return NS_OK;
 }
 
 //******************************************************************************
-/* void notify(in nsITimer timer); */
-NS_IMETHODIMP
-RasterImage::Notify(nsITimer *timer)
-{
-#ifdef DEBUG
-  mFramesNotified++;
-#endif
-
-  // This should never happen since the timer is only set up in StartAnimation()
-  // after mAnim is checked to exist.
-  NS_ABORT_IF_FALSE(mAnim, "Need anim for Notify()");
-  NS_ABORT_IF_FALSE(timer, "Need timer for Notify()");
-  NS_ABORT_IF_FALSE(mAnim->timer == timer,
-                    "RasterImage::Notify() called with incorrect timer");
-
-  if (!mAnimating || !ShouldAnimate())
-    return NS_OK;
-
-  nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
-  if (!observer) {
-    // the imgRequest that owns us is dead, we should die now too.
-    NS_ABORT_IF_FALSE(mAnimationConsumers == 0,
-                      "If no observer, should have no consumers");
-    if (mAnimating)
-      StopAnimation();
-    return NS_OK;
-  }
-
-  if (mFrames.Length() == 0)
-    return NS_OK;
-  
-  imgFrame *nextFrame = nsnull;
-  PRInt32 previousFrameIndex = mAnim->currentAnimationFrameIndex;
-  PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
-  PRInt32 timeout = 0;
-
-  // Figure out if we have the next full frame. This is more complicated than
-  // just checking for mFrames.Length() because decoders append their frames
-  // before they're filled in.
-  NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
-                    "How did we get 2 indicies too far by incrementing?");
-
-  // If we don't have a decoder, we know we've got everything we're going to get.
-  // If we do, we only display fully-downloaded frames; everything else gets delayed.
-  bool haveFullNextFrame = !mDecoder || nextFrameIndex < mDecoder->GetCompleteFrameCount();
-
-  // If we're done decoding the next frame, go ahead and display it now and
-  // reinit the timer with the next frame's delay time.
-  if (haveFullNextFrame) {
-    if (mFrames.Length() == nextFrameIndex) {
-      // End of Animation
-
-      // If animation mode is "loop once", it's time to stop animating
-      if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
-        mAnimationFinished = true;
-        EvaluateAnimation();
-        return NS_OK;
-      } else {
-        // We may have used compositingFrame to build a frame, and then copied
-        // it back into mFrames[..].  If so, delete composite to save memory
-        if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1)
-          mAnim->compositingFrame = nsnull;
-      }
-
-      nextFrameIndex = 0;
-      if (mLoopCount > 0)
-        mLoopCount--;
-    }
-
-    if (!(nextFrame = mFrames[nextFrameIndex])) {
-      // something wrong with the next frame, skip it
-      mAnim->currentAnimationFrameIndex = nextFrameIndex;
-      mAnim->timer->SetDelay(100);
-      return NS_OK;
-    }
-    timeout = nextFrame->GetTimeout();
-
-  } else {
-    // Uh oh, the frame we want to show is currently being decoded (partial)
-    // Wait a bit and try again
-    mAnim->timer->SetDelay(100);
-    return NS_OK;
-  }
-
-  if (timeout > 0)
-    mAnim->timer->SetDelay(timeout);
-  else {
-    mAnimationFinished = true;
-    EvaluateAnimation();
-  }
-
-  nsIntRect dirtyRect;
-
-  if (nextFrameIndex == 0) {
-    dirtyRect = mAnim->firstFrameRefreshArea;
-  } else {
-    imgFrame *prevFrame = mFrames[previousFrameIndex];
-    if (!prevFrame)
-      return NS_OK;
-
-    // Change frame and announce it
-    if (NS_FAILED(DoComposite(&dirtyRect, prevFrame,
-                              nextFrame, nextFrameIndex))) {
-      // something went wrong, move on to next
-      NS_WARNING("RasterImage::Notify(): Composing Frame Failed\n");
-      nextFrame->SetCompositingFailed(true);
-      mAnim->currentAnimationFrameIndex = nextFrameIndex;
-      return NS_OK;
-    } else {
-      nextFrame->SetCompositingFailed(false);
-    }
-  }
-  // Set currentAnimationFrameIndex at the last possible moment
-  mAnim->currentAnimationFrameIndex = nextFrameIndex;
-  // Refreshes the screen
-  observer->FrameChanged(this, &dirtyRect);
-  
-  return NS_OK;
-}
-
-//******************************************************************************
 // DoComposite gets called when the timer for animation get fired and we have to
 // update the composited frame of the animation.
 nsresult
 RasterImage::DoComposite(nsIntRect* aDirtyRect,
                          imgFrame* aPrevFrame,
                          imgFrame* aNextFrame,
                          PRInt32 aNextFrameIndex)
 {
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -66,42 +66,61 @@
 #include "DiscardTracker.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #ifdef DEBUG
   #include "imgIContainerDebug.h"
 #endif
 
 class imgIDecoder;
+class imgIContainerObserver;
 class nsIInputStream;
 
 #define NS_RASTERIMAGE_CID \
 { /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */         \
      0x376ff2c1,                                     \
      0x9bf6,                                         \
      0x418a,                                         \
     {0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \
 }
 
 /**
+ * It would be nice if we had a macro for this in prtypes.h.
+ * TODO: Place this macro in prtypes.h as PR_UINT64_MAX.
+ */
+#define UINT64_MAX_VAL PRUint64(-1)
+
+/**
  * Handles static and animated image containers.
  *
  *
  * @par A Quick Walk Through
  * The decoder initializes this class and calls AppendFrame() to add a frame.
  * Once RasterImage detects more than one frame, it starts the animation
- * with StartAnimation().
+ * with StartAnimation(). Note that the invalidation events for RasterImage are
+ * generated automatically using nsRefreshDriver.
+ *
+ * @par
+ * StartAnimation() initializes the animation helper object and sets the time
+ * the first frame was displayed to the current clock time.
  *
  * @par
- * StartAnimation() creates a timer.  The timer calls Notify when the
- * specified frame delay time is up.
+ * When the refresh driver corresponding to the imgIContainer that this image is
+ * a part of notifies the RasterImage that it's time to invalidate,
+ * RequestRefresh() is called with a given TimeStamp to advance to. As long as
+ * the timeout of the given frame (the frame's "delay") plus the time that frame
+ * was first displayed is less than or equal to the TimeStamp given,
+ * RequestRefresh() calls AdvanceFrame().
  *
  * @par
- * Notify() moves on to the next frame, sets up the new timer delay, destroys
- * the old frame, and forces a redraw via observer->FrameChanged().
+ * AdvanceFrame() is responsible for advancing a single frame of the animation.
+ * It can return true, meaning that the frame advanced, or false, meaning that
+ * the frame failed to advance (usually because the next frame hasn't been
+ * decoded yet). It is also responsible for performing the final animation stop
+ * procedure if the final frame of a non-looping animation is reached.
  *
  * @par
  * Each frame can have a different method of removing itself. These are
  * listed as imgIContainer::cDispose... constants.  Notify() calls 
  * DoComposite() to handle any special frame destruction.
  *
  * @par
  * The basic path through DoComposite() is:
@@ -150,26 +169,24 @@ class LayerManager;
 class ImageContainer;
 }
 namespace imagelib {
 
 class imgDecodeWorker;
 class Decoder;
 
 class RasterImage : public Image
-                  , public nsITimerCallback
                   , public nsIProperties
                   , public nsSupportsWeakReference
 #ifdef DEBUG
                   , public imgIContainerDebug
 #endif
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIPROPERTIES
 #ifdef DEBUG
   NS_DECL_IMGICONTAINERDEBUG
 #endif
 
   // BEGIN NS_DECL_IMGICONTAINER (minus GetAnimationMode/SetAnimationMode)
   // ** Don't edit this chunk except to mirror changes in imgIContainer.idl **
   NS_SCRIPTABLE NS_IMETHOD GetWidth(PRInt32 *aWidth);
@@ -183,16 +200,17 @@ public:
   NS_IMETHOD CopyFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxImageSurface **_retval NS_OUTPARAM);
   NS_IMETHOD ExtractFrame(PRUint32 aWhichFrame, const nsIntRect & aRect, PRUint32 aFlags, imgIContainer **_retval NS_OUTPARAM);
   NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, PRUint32 aFlags);
   NS_IMETHOD_(nsIFrame *) GetRootLayoutFrame(void);
   NS_SCRIPTABLE NS_IMETHOD RequestDecode(void);
   NS_SCRIPTABLE NS_IMETHOD LockImage(void);
   NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
   NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
+  NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
   // END NS_DECL_IMGICONTAINER
 
   RasterImage(imgStatusTracker* aStatusTracker = nsnull);
   virtual ~RasterImage();
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
 
@@ -334,16 +352,20 @@ public:
   const char* GetURIString() { return mURIString.get();}
 
 private:
   struct Anim
   {
     //! Area of the first frame that needs to be redrawn on subsequent loops.
     nsIntRect                  firstFrameRefreshArea;
     PRUint32                   currentAnimationFrameIndex; // 0 to numFrames-1
+
+    // the time that the animation advanced to the current frame
+    TimeStamp                  currentAnimationFrameTime;
+
     //! Track the last composited frame for Optimizations (See DoComposite code)
     PRInt32                    lastCompositedFrameIndex;
     /** For managing blending of frames
      *
      * Some animations will use the compositingFrame to composite images
      * and just hand this back to the caller when it is time to draw the frame.
      * NOTE: When clearing compositingFrame, remember to set
      *       lastCompositedFrameIndex to -1.  Code assume that if
@@ -352,49 +374,60 @@ private:
     nsAutoPtr<imgFrame>        compositingFrame;
     /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
      *
      * The Previous Frame (all frames composited up to the current) needs to be
      * stored in cases where the image specifies it wants the last frame back
      * when it's done with the current frame.
      */
     nsAutoPtr<imgFrame>        compositingPrevFrame;
-    //! Timer to animate multiframed images
-    nsCOMPtr<nsITimer>         timer;
 
     Anim() :
       firstFrameRefreshArea(),
       currentAnimationFrameIndex(0),
-      lastCompositedFrameIndex(-1)
-    {
-      ;
-    }
-    ~Anim()
-    {
-      if (timer)
-        timer->Cancel();
-    }
+      lastCompositedFrameIndex(-1) {}
+    ~Anim() {}
   };
 
   /**
+   * Advances the animation. Typically, this will advance a single frame, but it
+   * may advance multiple frames. This may happen if we have infrequently
+   * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
+   * lived animation frames.
+   *
+   * @param aTime the time that the animation should advance to. This will
+   *              typically be <= TimeStamp::Now().
+   *
+   * @param [out] aDirtyRect a pointer to an nsIntRect which encapsulates the
+   *        area to be repainted after the frame is advanced.
+   *
+   * @returns true, if the frame was successfully advanced, false if it was not
+   *          able to be advanced (e.g. the frame to which we want to advance is
+   *          still decoding). Note: If false is returned, then aDirtyRect will
+   *          remain unmodified.
+   */
+  bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect);
+
+  /**
    * Deletes and nulls out the frame in mFrames[framenum].
    *
    * Does not change the size of mFrames.
    *
    * @param framenum The index of the frame to be deleted. 
    *                 Must lie in [0, mFrames.Length() )
    */
   void DeleteImgFrame(PRUint32 framenum);
 
   imgFrame* GetImgFrameNoDecode(PRUint32 framenum);
   imgFrame* GetImgFrame(PRUint32 framenum);
   imgFrame* GetDrawableImgFrame(PRUint32 framenum);
   imgFrame* GetCurrentImgFrame();
   imgFrame* GetCurrentDrawableImgFrame();
   PRUint32 GetCurrentImgFrameIndex() const;
+  mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
   
   inline void EnsureAnimExists()
   {
     if (!mAnim) {
 
       // Create the animation context
       mAnim = new Anim();
 
@@ -403,39 +436,42 @@ private:
       // 
       // Note that this is inefficient, since we could get rid of the source
       // data too. However, doing this is actually hard, because we're probably
       // calling ensureAnimExists mid-decode, and thus we're decoding out of
       // the source buffer. Since we're going to fix this anyway later, and
       // since we didn't kill the source data in the old world either, locking
       // is acceptable for the moment.
       LockImage();
+
+      // Notify our observers that we are starting animation.
+      mStatusTracker->RecordImageIsAnimated();
     }
   }
-  
+
   /** Function for doing the frame compositing of animations
    *
    * @param aDirtyRect  Area that the display will need to update
    * @param aPrevFrame  Last Frame seen/processed
    * @param aNextFrame  Frame we need to incorperate/display
    * @param aNextFrameIndex Position of aNextFrame in mFrames list
    */
   nsresult DoComposite(nsIntRect* aDirtyRect,
                        imgFrame* aPrevFrame,
                        imgFrame* aNextFrame,
                        PRInt32 aNextFrameIndex);
-  
+
   /** Clears an area of <aFrame> with transparent black.
    *
    * @param aFrame Target Frame
    *
    * @note Does also clears the transparancy mask
    */
   static void ClearFrame(imgFrame* aFrame);
-  
+
   //! @overload
   static void ClearFrame(imgFrame* aFrame, nsIntRect &aRect);
   
   //! Copy one frames's image and mask into another
   static bool CopyFrameImage(imgFrame *aSrcFrame,
                                imgFrame *aDstFrame);
   
   /** Draws one frames's image to into another,
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -167,17 +167,16 @@ SVGDrawingCallback::operator()(gfxContex
   // Clip to aFillRect so that we don't paint outside.
   aContext->NewPath();
   aContext->Rectangle(aFillRect);
   aContext->Clip();
 
   gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
   aContext->Multiply(gfxMatrix(aTransform).Invert());
 
-
   nsPresContext* presContext = presShell->GetPresContext();
   NS_ABORT_IF_FALSE(presContext, "pres shell w/out pres context");
 
   nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x),
                  presContext->DevPixelsToAppUnits(mViewport.y),
                  presContext->DevPixelsToAppUnits(mViewport.width),
                  presContext->DevPixelsToAppUnits(mViewport.height));
 
@@ -326,16 +325,24 @@ VectorImage::GetWidth(PRInt32* aWidth)
     *aWidth = 0;
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 //******************************************************************************
+/* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
+NS_IMETHODIMP_(void)
+VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+{
+  // TODO: Implement for b666446.
+}
+
+//******************************************************************************
 /* readonly attribute PRInt32 height; */
 NS_IMETHODIMP
 VectorImage::GetHeight(PRInt32* aHeight)
 {
   if (mError || !mIsFullyLoaded) {
     *aHeight = 0;
     return NS_ERROR_FAILURE;
   }
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -37,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_imagelib_VectorImage_h_
 #define mozilla_imagelib_VectorImage_h_
 
 #include "Image.h"
 #include "nsIStreamListener.h"
 #include "nsWeakReference.h"
+#include "mozilla/TimeStamp.h"
 
 class imgIDecoderObserver;
 
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
 }
@@ -76,16 +77,17 @@ public:
   NS_IMETHOD CopyFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxImageSurface **_retval NS_OUTPARAM);
   NS_IMETHOD ExtractFrame(PRUint32 aWhichFrame, const nsIntRect & aRect, PRUint32 aFlags, imgIContainer **_retval NS_OUTPARAM);
   NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, PRUint32 aFlags);
   NS_IMETHOD_(nsIFrame *) GetRootLayoutFrame(void);
   NS_SCRIPTABLE NS_IMETHOD RequestDecode(void);
   NS_SCRIPTABLE NS_IMETHOD LockImage(void);
   NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
   NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
+  NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
   // END NS_DECL_IMGICONTAINER
 
   VectorImage(imgStatusTracker* aStatusTracker = nsnull);
   virtual ~VectorImage();
 
   // Methods inherited from Image
   nsresult Init(imgIDecoderObserver* aObserver,
                 const char* aMimeType,
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -791,16 +791,30 @@ NS_IMETHODIMP imgRequest::OnDiscard(imgI
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
     mImage->GetStatusTracker().SendDiscard(iter.GetNext());
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP imgRequest::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  NS_ABORT_IF_FALSE(mImage,
+                    "OnImageIsAnimated callback before we've created our image");
+  mImage->GetStatusTracker().RecordImageIsAnimated();
+
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    mImage->GetStatusTracker().SendImageIsAnimated(iter.GetNext());
+  }
+
+  return NS_OK;
+}
+
 /** nsIRequestObserver methods **/
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
 
   // Figure out if we're multipart
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -704,16 +704,26 @@ void imgRequestProxy::OnDiscard()
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
     mListener->OnDiscard(this);
   }
 }
 
+void imgRequestProxy::OnImageIsAnimated()
+{
+  LOG_FUNC(gImgLog, "imgRequestProxy::OnImageIsAnimated");
+  if (mListener && !mCanceled) {
+    // Hold a ref to the listener while we call it, just in case.
+    nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
+    mListener->OnImageIsAnimated(this);
+  }
+}
+
 void imgRequestProxy::OnStartRequest()
 {
 #ifdef PR_LOGGING
   nsCAutoString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get());
 #endif
 
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -163,24 +163,25 @@ protected:
       nsresult mStatus;
   };
 
   // The following notification functions are protected to ensure that (friend
   // class) imgStatusTracker is the only class allowed to send us
   // notifications.
 
   /* non-virtual imgIDecoderObserver methods */
-  void OnStartDecode   ();
-  void OnStartContainer(imgIContainer *aContainer);
-  void OnStartFrame    (PRUint32 aFrame);
-  void OnDataAvailable (bool aCurrentFrame, const nsIntRect * aRect);
-  void OnStopFrame     (PRUint32 aFrame);
-  void OnStopContainer (imgIContainer *aContainer);
-  void OnStopDecode    (nsresult status, const PRUnichar *statusArg); 
-  void OnDiscard       ();
+  void OnStartDecode     ();
+  void OnStartContainer  (imgIContainer *aContainer);
+  void OnStartFrame      (PRUint32 aFrame);
+  void OnDataAvailable   (bool aCurrentFrame, const nsIntRect * aRect);
+  void OnStopFrame       (PRUint32 aFrame);
+  void OnStopContainer   (imgIContainer *aContainer);
+  void OnStopDecode      (nsresult status, const PRUnichar *statusArg);
+  void OnDiscard         ();
+  void OnImageIsAnimated ();
 
   /* non-virtual imgIContainerObserver methods */
   void FrameChanged(imgIContainer *aContainer,
                     const nsIntRect *aDirtyRect);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
   void OnStartRequest();
   void OnStopRequest(bool aLastPart);
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -249,16 +249,24 @@ imgStatusTracker::SyncNotify(imgRequestP
       // wait until we fix up the observer interface
       nsIntRect r;
       mImage->GetCurrentFrameRect(r);
       proxy->OnDataAvailable(frame, &r);
 
       if (mState & stateFrameStopped)
         proxy->OnStopFrame(frame);
     }
+
+    // OnImageIsAnimated
+    bool isAnimated = false;
+
+    nsresult rv = mImage->GetAnimated(&isAnimated);
+    if (NS_SUCCEEDED(rv) && isAnimated) {
+      proxy->OnImageIsAnimated();
+    }
   }
 
   // See bug 505385 and imgRequest::OnStopDecode for more information on why we
   // call OnStopContainer based on stateDecodeStopped, and why OnStopDecode is
   // called with OnStopRequest.
   if (mState & stateDecodeStopped) {
     NS_ABORT_IF_FALSE(mImage, "stopped decoding without ever having an image?");
     proxy->OnStopContainer(mImage);
@@ -447,16 +455,34 @@ imgStatusTracker::RecordDiscard()
 
   // Clear the status bits we no longer deserve.
   PRUint32 statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE
                                | imgIRequest::STATUS_DECODE_COMPLETE;
   mImageStatus &= ~statusBitsToClear;
 }
 
 void
+imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy)
+{
+  if (!aProxy->NotificationsDeferred())
+    aProxy->OnImageIsAnimated();
+}
+
+void
+imgStatusTracker::RecordImageIsAnimated()
+{
+  NS_ABORT_IF_FALSE(mImage,
+                    "RecordImageIsAnimated called before we have an Image");
+  // No bookkeeping necessary here - once decoding is complete, GetAnimated()
+  // will accurately return that this is an animated image. Until that time,
+  // the OnImageIsAnimated notification is the only indication an observer
+  // will have that an image has more than 1 frame.
+}
+
+void
 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
 {
   if (!aProxy->NotificationsDeferred())
     aProxy->OnDiscard();
 }
 
 /* non-virtual imgIContainerObserver methods */
 void
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -154,16 +154,18 @@ public:
   void RecordStopFrame(PRUint32 aFrame);
   void SendStopFrame(imgRequestProxy* aProxy, PRUint32 aFrame);
   void RecordStopContainer(imgIContainer* aContainer);
   void SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer);
   void RecordStopDecode(nsresult status, const PRUnichar* statusArg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, const PRUnichar* statusArg);
   void RecordDiscard();
   void SendDiscard(imgRequestProxy* aProxy);
+  void RecordImageIsAnimated();
+  void SendImageIsAnimated(imgRequestProxy *aProxy);
 
   /* non-virtual imgIContainerObserver methods */
   void RecordFrameChanged(imgIContainer* aContainer,
                           const nsIntRect* aDirtyRect);
   void SendFrameChanged(imgRequestProxy* aProxy, imgIContainer* aContainer,
                         const nsIntRect* aDirtyRect);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
--- a/image/test/mochitest/Makefile.in
+++ b/image/test/mochitest/Makefile.in
@@ -88,17 +88,36 @@ include $(topsrcdir)/config/rules.mk
                 test_bug671906.html \
                 $(NULL)
 
 # Tests disabled due to intermittent orange
 # test_bug435296.html disabled - See bug 578591
 # test_bug478398.html disabled - See bug 579139
 
 _CHROME_FILES = imgutils.js \
+                animationPolling.js \
                 lime-anim-100x100.svg \
+                animation.svg \
                 test_animSVGImage.html \
+                test_animation.html \
+                animated-gif-finalframe.gif \
+                animated-gif.gif \
+                animated-gif2.gif \
+                purple.gif \
+                test_svg_animatedGIF.html \
+                test_bullet_animation.html \
+                test_background_image_anim.html \
+                filter.svg \
+                filter-final.svg \
+                test_svg_filter_animation.html \
+                test_xultree_animation.xhtml \
+                test_changeOfSource.html \
+                test_changeOfSource2.html \
+                test_undisplayed_iframe.html \
+                iframe.html \
+                ref-iframe.html \
                 $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4e80d31a727fc725326082d71ba42ccd33bf3b49
GIT binary patch
literal 72
zc${<hbh9u|)L_tHXkcW}`p@wH|9>3@AOMLlFsZlnuRQ&hfAO3xx4Jjq+w+^h?UBc{
bXPwJlo!a(}`}ilH>)(2x|2n74%3uuu*a9G=
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..001cbfb87a05e518f53707a4237a7034d2111344
GIT binary patch
literal 146
zc${<hbhEHb)L_tHSjfbn^`GJY7s>x%p!k!8fs5fkgAM}_faDpN)O-3@o_@=}c+Qqv
x-J9?2`OV+<$Ya{G&SkGoZF|Rk{FBf1Z@tfdol|CI0P1DrVqiqk+d`yXYXIPpLiGRu
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c66cc4b7344f52e312460d7424cc2cce7f6fd87d
GIT binary patch
literal 165
zc${<hbhEHb)L_tHSjfb{!1jL{!+!`+{Lk&@8WQa67~pE8XTZ$Jz`&sRlZAnc;Xi{8
zkj((n!oZ~7(!cWbTmHp!w%qF8d~eTh{<cRR)1GxMdv$8tJMQD3e6D}%eg5m5GAl?g
PBNtE`vfdsd^;!b}g%L(;
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/animation.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+	<image id="anim" xlink:href="animated-gif.gif" width="40" height="40"/>
+</svg>
+
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/animationPolling.js
@@ -0,0 +1,395 @@
+var currentTest;
+var gIsImageLoaded = false;
+var gIsRefImageLoaded = false;
+
+function pollForSuccess ()
+{
+  if (!currentTest.isTestFinished) {
+    if (!currentTest.reusingReferenceImage || (currentTest.reusingReferenceImage
+        && gRefImageLoaded)) {
+      currentTest.checkImage();
+    }
+
+    setTimeout(pollForSuccess, currentTest.pollFreq);
+  }
+};
+
+function imageLoadCallback()
+{
+  gIsImageLoaded = true;
+}
+
+function referencePoller()
+{
+  currentTest.takeReferenceSnapshot();
+}
+
+function reuseImageCallback()
+{
+  gIsRefImageLoaded = true;
+}
+
+function failTest ()
+{
+  if (currentTest.isTestFinished || currentTest.closeFunc) {
+    return;
+  }
+
+  ok(false, "timing out after " + currentTest.timeout + "ms.  "
+     + "Animated image still doesn't look correct, " + "after call #"
+     + currentTest.onStopFrameCounter + " to onStopFrame");
+  currentTest.wereFailures = true;
+
+  currentTest.enableDisplay(document.getElementById(currentTest.debugElementId));
+
+  currentTest.cleanUpAndFinish();
+};
+
+/**
+ * Create a new AnimationTest object.
+ *
+ * @param pollFreq The amount of time (in ms) to wait between consecutive
+ *        snapshots if the reference image and the test image don't match.
+ * @param timeout The total amount of time (in ms) to wait before declaring the
+ *        test as failed.
+ * @param referenceElementId The id attribute of the reference image element, or
+ *        the source of the image to change to, once the reference snapshot has
+ *        been successfully taken. This latter option could be used if you don't
+ *        want the image to become invisible at any time during the test.
+ * @param imageElementId The id attribute of the test image element.
+ * @param debugElementId The id attribute of the div where links should be
+ *        appended if the test fails.
+ * @param cleanId The id attribute of the div or element to use as the 'clean'
+ *        test. This element is only enabled when we are testing to verify that
+ *        the reference image has been loaded. It can be undefined.
+ * @param srcAttr The location of the source of the image, for preloading. This
+ *        is usually not required, but it useful for preloading reference
+ *        images.
+ * @param xulTest A boolean value indicating whether or not this is a XUL test
+ *        (uses hidden=true/false rather than display: none to hide/show
+ *        elements).
+ * @param closeFunc A function that should be called when this test is finished.
+ *        If null, then cleanUpAndFinish() will be called. This can be used to
+ *        chain tests together, so they are all finished exactly once.
+ * @returns {AnimationTest}
+ */
+function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
+                       debugElementId, cleanId, srcAttr, xulTest, closeFunc)
+{
+  // We want to test the cold loading behavior, so clear cache in case an
+  // earlier test got our image in there already.
+  clearImageCache();
+
+  this.wereFailures = false;
+  this.pollFreq = pollFreq;
+  this.timeout = timeout;
+  this.imageElementId = imageElementId;
+  this.referenceElementId = referenceElementId;
+
+  if (!document.getElementById(referenceElementId)) {
+    // In this case, we're assuming the user passed in a string that
+    // indicates the source of the image they want to change to,
+    // after the reference image has been taken.
+    this.reusingImageAsReference = true;
+  }
+
+  this.srcAttr = srcAttr;
+  this.debugElementId = debugElementId;
+  this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
+  this.onStopFrameCounter = 0;
+  this.isTestFinished = false;
+  this.numRefsTaken = 0;
+  this.blankWaitTime = 0;
+
+  this.cleanId = cleanId ? cleanId : '';
+  this.xulTest = xulTest ? xulTest : '';
+  this.closeFunc = closeFunc ? closeFunc : '';
+
+  if (this.srcAttr) {
+    this.myImage = new Image();
+    this.myImage.onload = imageLoadCallback;
+    this.myImage.src = this.srcAttr;
+  } else {
+    gIsImageLoaded = true;
+  }
+}
+
+AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri)
+{
+  var debugElement = document.getElementById(this.debugElementId);
+  var newDataUriElement = document.createElement("a");
+  newDataUriElement.setAttribute("id", id);
+  newDataUriElement.setAttribute("href", dataUri);
+  newDataUriElement.appendChild(document.createTextNode(message));
+  debugElement.appendChild(newDataUriElement);
+  var brElement = document.createElement("br");
+  debugElement.appendChild(brElement);
+};
+
+AnimationTest.prototype.isFinished = function()
+{
+  return this.isTestFinished;
+};
+
+AnimationTest.prototype.takeCleanSnapshot = function()
+{
+  var cleanElement;
+  if (this.cleanId) {
+    cleanElement = document.getElementById(this.cleanId);
+  }
+
+  // Enable clean page comparison element
+  if (cleanElement) {
+    this.enableDisplay(cleanElement);
+  }
+
+  // Take a snapshot of the initial (clean) page
+  this.cleanSnapshot = snapshotWindow(window, false);
+
+  // Disable the clean page comparison element
+  if (cleanElement) {
+    this.disableDisplay(cleanElement);
+  }
+
+  var dataString1 = "Clean Snapshot";
+  this.outputDebugInfo(dataString1, 'cleanSnap',
+                       this.cleanSnapshot.toDataURL());
+};
+
+AnimationTest.prototype.takeBlankSnapshot = function()
+{
+  // Take a snapshot of the initial (essentially blank) page
+  this.blankSnapshot = snapshotWindow(window, false);
+
+  var dataString1 = "Initial Blank Snapshot";
+  this.outputDebugInfo(dataString1, 'blank1Snap',
+                       this.blankSnapshot.toDataURL());
+};
+
+/**
+ * Begin the AnimationTest. This will utilize the information provided in the
+ * constructor to invoke a mochitest on animated images. It will automatically
+ * fail if allowed to run past the timeout.
+ */
+AnimationTest.prototype.beginTest = function ()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  currentTest = this;
+
+  this.takeReferenceSnapshot();
+
+  // In case something goes wrong, fail earlier than mochitest timeout,
+  // and with more information.
+  setTimeout(failTest, this.timeout);
+
+  if (!this.reusingImageAsReference) {
+    this.disableDisplay(document.getElementById(this.imageElementId));
+  }
+
+  this.setupPolledImage();
+  setTimeout(pollForSuccess, 10);
+};
+
+AnimationTest.prototype.setupPolledImage = function ()
+{
+  // Make sure the image is visible
+  if (!this.reusingImageAsReference) {
+    this.enableDisplay(document.getElementById(this.imageElementId));
+    var currentSnapshot = snapshotWindow(window, false);
+    var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
+
+    var dataString = "Snapshot #" + this.onStopFrameCounter;
+    this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
+                         currentSnapshot.toDataURL());
+
+    if (result[0]) {
+      // SUCCESS!
+      ok(true, "Animated image looks correct, " + "at call #"
+         + this.onStopFrameCounter + " to onStopFrame");
+
+      this.cleanUpAndFinish();
+    }
+  } else {
+    if (!gIsRefImageLoaded) {
+      this.myImage = new Image();
+      this.myImage.onload = reuseImageCallback;
+      document.getElementById(this.imageElementId).setAttribute('src',
+        this.referenceElementId);
+    }
+  }
+}
+
+AnimationTest.prototype.checkImage = function ()
+{
+  if (this.isTestFinished) {
+    return;
+  }
+
+  this.onStopFrameCounter++;
+
+  // We need this for some tests, because we need to force the
+  // test image to be visible.
+  if (!this.reusingImageAsReference) {
+    this.enableDisplay(document.getElementById(this.imageElementId));
+  }
+
+  var currentSnapshot = snapshotWindow(window, false);
+  var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
+
+  var dataString = "Snapshot #" + this.onStopFrameCounter;
+  this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
+                         currentSnapshot.toDataURL());
+
+  if (result[0]) {
+    // SUCCESS!
+    ok(true, "Animated image looks correct, " + "at call #"
+       + this.onStopFrameCounter + " to onStopFrame");
+
+    this.cleanUpAndFinish();
+  }
+};
+
+AnimationTest.prototype.takeReferenceSnapshot = function ()
+{
+  this.numRefsTaken++;
+
+  // Test to make sure the reference image doesn't match a clean snapshot
+  if (!this.cleanSnapshot) {
+    this.takeCleanSnapshot();
+  }
+
+  // Used later to verify that the reference div disappeared
+  if (!this.blankSnapshot) {
+    this.takeBlankSnapshot();
+  }
+
+  if (this.reusingImageAsReference) {
+    // Show reference div, & take a snapshot
+    var referenceDiv = document.getElementById(this.imageElementId);
+    this.enableDisplay(referenceDiv);
+
+    this.referenceSnapshot = snapshotWindow(window, false);
+    var snapResult = compareSnapshots(this.cleanSnapshot, this.referenceSnapshot,
+                                      false);
+    if (!snapResult[0]) {
+      if (this.blankWaitTime > 2000) {
+        // if it took longer than two seconds to load the image, we probably
+        // have a problem.
+        this.wereFailures = true;
+        ok(snapResult[0],
+           "Reference snapshot shouldn't match clean (non-image) snapshot");
+      } else {
+        this.blankWaitTime += 20;
+        // let's wait a bit and see if it clears up
+        setTimeout(referencePoller, 20);
+        return;
+      }
+    }
+
+    ok(snapResult[0],
+       "Reference snapshot shouldn't match clean (non-image) snapshot");
+
+    var dataString = "Reference Snapshot #" + this.numRefsTaken;
+    this.outputDebugInfo(dataString, 'refSnapId',
+                         this.referenceSnapshot.toDataURL());
+  } else {
+    // Make sure the animation section is hidden
+    this.disableDisplay(document.getElementById(this.imageElementId));
+
+    // Show reference div, & take a snapshot
+    var referenceDiv = document.getElementById(this.referenceElementId);
+    this.enableDisplay(referenceDiv);
+
+    this.referenceSnapshot = snapshotWindow(window, false);
+    var snapResult = compareSnapshots(this.cleanSnapshot, this.referenceSnapshot, false);
+    if (!snapResult[0]) {
+      if (this.blankWaitTime > 2000) {
+        // if it took longer than two seconds to load the image, we probably
+        // have a problem.
+        this.wereFailures = true;
+        ok(snapResult[0],
+           "Reference snapshot shouldn't match clean (non-image) snapshot");
+      } else {
+        this.blankWaitTime += 20;
+        // let's wait a bit and see if it clears up
+        setTimeout(referencePoller, 20);
+        return;
+      }
+    }
+
+    ok(snapResult[0],
+       "Reference snapshot shouldn't match clean (non-image) snapshot");
+
+    var dataString = "Reference Snapshot #" + this.numRefsTaken;
+    this.outputDebugInfo(dataString, 'refSnapId',
+                         this.referenceSnapshot.toDataURL());
+
+    // Re-hide reference div, and take another snapshot to be sure it's gone
+    this.disableDisplay(referenceDiv);
+    this.testBlankCameBack();
+  }
+};
+
+AnimationTest.prototype.enableDisplay = function(element)
+{
+  if (!element) {
+    return;
+  }
+
+  if (!this.xulTest) {
+    element.style.display = '';
+  } else {
+    element.setAttribute('hidden', 'false');
+  }
+};
+
+AnimationTest.prototype.disableDisplay = function(element)
+{
+  if (!element) {
+    return;
+  }
+
+  if (!this.xulTest) {
+    element.style.display = 'none';
+  } else {
+    element.setAttribute('hidden', 'true');
+  }
+};
+
+AnimationTest.prototype.testBlankCameBack = function()
+{
+  var blankSnapshot2 = snapshotWindow(window, false);
+  var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
+  ok(result[0], "Reference image should disappear when it becomes display:none");
+
+  if (!result[0]) {
+    this.wereFailures = true;
+    var dataString = "Second Blank Snapshot";
+    this.outputDebugInfo(dataString, 'blank2SnapId', result[2]);
+  }
+};
+
+AnimationTest.prototype.cleanUpAndFinish = function ()
+{
+  // On the off chance that failTest and checkImage are triggered
+  // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+  if (this.isTestFinished) {
+    return;
+  }
+
+  this.isTestFinished = true;
+
+  // Call our closing function, if one exists
+  if (this.closeFunc) {
+    this.closeFunc();
+    return;
+  }
+
+  if (this.wereFailures) {
+    document.getElementById(this.debugElementId).style.display = 'block';
+  }
+
+  SimpleTest.finish();
+  document.getElementById(this.debugElementId).style.display = "";
+};
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/filter-final.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
+  <feImage xlink:href="animated-gif-finalframe.gif"/>
+</filter>
+<g>
+	<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/filter.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
+  <feImage xlink:href="animated-gif.gif"/>
+</filter>
+<g>
+	<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/iframe.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="gray">
+  <img src="animated-gif.gif">
+</body>
+</html>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..79826af205d7eceb7763d8a213d3ec28844db363
GIT binary patch
literal 86
zc${<hbhEHb)L_tHSjfb{!1jL{!+!`+{K>+|#lXOz1H=p<c?Kr+p8l1m-|{b>v*lLz
k=6id7^S3?nnD(r5*{f6A-f<uQ<a7O7@AF^hlvx?90h=QxX8-^I
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/ref-iframe.html
@@ -0,0 +1,6 @@
+<html>
+<body bgcolor="gray">
+  <div id="referenceImage"
+    style="height: 40px; width: 40px; background: #2aff00"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_animation.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - General Animated GIF Test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+  <div id="referenceDiv" style="height: 40px; width: 40px;
+                                display: none; background: #2aff00"></div>
+  <div id="animatedImage">
+    <img id="animatedGif" src="animated-gif.gif" style="display: none;">
+      <div id="text-descr"></div>
+  </div>
+  <div id="debug" style="display:none">
+  </div>
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                   'animatedGif', 'debug');
+  animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_background_image_anim.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Animated Background Images</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+  <div id="referenceDiv" style="height: 140px; width: 140px;
+                                display: none; background: #2aff00"></div>
+  <div id="bgImage" style="height: 140px; width: 140px; background-image: url(animated-gif.gif); display: none;">
+  </div>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+
+/** Test for Bug 666446 nsImageLoader/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                   'bgImage', 'debug');
+  animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_bullet_animation.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Animated Bullets</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+  <div id="cleanDiv" style="display: none;">
+    <ul>
+      <li>Test 1</li>
+    </ul>
+  </div>
+  <div id="referenceDiv" style="display: none;">
+    <ul>
+      <li style="list-style-image: url(animated-gif-finalframe.gif);">Test 1</li>
+    </ul>
+  </div>
+  <div id="animatedImage" style="display: none;">
+      <ul>
+        <li style="list-style-image: url(animated-gif.gif);">Test 1</li>
+      </ul>
+  </div>
+  <div id="text-descr"></div>
+  <div id="debug" style="display:none">
+  </div>
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                   'animatedImage', 'debug', 'cleanDiv',
+                                   'animated-gif-finalframe.gif');
+  animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_changeOfSource.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Change of Source (1st Version)</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+  <div id="referenceDiv" style="height: 40px; width: 40px;
+                                display: none; background: #2aff00;">
+  </div>
+  <div id="animatedImage">
+    <img id='animatedGif' src="animated-gif.gif" style="display: none;">
+  </div>
+  <div id="text-descr"></div>
+  <div id="debug" style="display:none">
+  </div>
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+var gAnimTest;
+var gIntervalId;
+
+function initSecondTest() {
+  document.getElementById('debug').style.display = 'none';
+  document.getElementById('referenceDiv').style.background = "#9600ff";
+  document.getElementById('animatedGif').setAttribute('src',
+                                                      'animated-gif2.gif');
+  document.getElementById('animatedGif').style.display = 'none';
+  var secondTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                     'animatedGif', 'debug', '', '', false);
+  secondTest.beginTest();
+}
+
+function main()
+{
+  gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                'animatedGif', 'debug', '', '', false,
+                                initSecondTest);
+  gAnimTest.beginTest();
+
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_changeOfSource2.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 691792 - Change of Source (2nd Version)</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=691792">
+Mozilla Bug 691792: Change of src attribute for animated gifs no longer works as expected
+</a>
+<p id="display"></p>
+
+<div id="content">
+  <div id="animatedImage">
+    <img id='animatedGif' src="purple.gif" style="display: none;">
+  </div>
+  <div id="text-descr"></div>
+  <div id="debug" style="display:none">
+  </div>
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+var gAnimTest;
+var gIntervalId;
+
+function main()
+{
+  gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'animated-gif2.gif',
+                                'animatedGif', 'debug', '', 'animated-gif2.gif',
+                                false);
+  gAnimTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_svg_animatedGIF.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Animated Raster Images inside of SVG Frames</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+
+<!--  Make sure embed element is snapped to an exact pixel. -->
+<div class="bug-header" style="height: 100px;">
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+  Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+  </a>
+</div>
+
+<p id="display"></p>
+<div id="content">
+  <div id="referenceDiv" style="height: 40px; width: 40px;
+                                display: none; background: #2aff00"></div>
+    <!--
+        We use <embed> here instead of <img> because the <img> tag utilizes
+        the VectorImage class for SVG, whereas in this test, we are testing
+        RasterImage.
+     -->
+  <embed id="embeddedSVG" src="animation.svg" type="image/svg+xml" style="display: none;"/>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+
+/** Test for Bug 666446 nsSVGImageFrame/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+      							   'embeddedSVG', 'debug', '', 'src');
+  animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_svg_filter_animation.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Animated Images within SVG Filters</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+  <embed id="referenceImage" src="filter-final.svg" type="image/svg+xml" style="display: none;"/>
+  <embed id="embeddedSVGFilt" src="filter.svg" type="image/svg+xml" style="display: none;"/>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+
+/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceImage',
+                       'embeddedSVGFilt', 'debug', '', 'src');
+  animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_undisplayed_iframe.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+<title>Test for Bug 666446 - Test for Animated Gif within IFRAME</title>
+<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+<script type="application/javascript" src="imgutils.js"></script>
+<script type="application/javascript" src="animationPolling.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+    Mozilla Bug 666446: lots of animated gifs swamp us with paint events</a>
+  <p id="display"></p>
+
+  <div id="content">
+    <div id="referenceDiv" style="display:none;">
+      <iframe id="referenceIFrame" src="ref-iframe.html" width="50%" height="100">
+        Browser does not support iframes.
+      </iframe>
+    </div>
+    <div id="animatedImage">
+      <iframe id="imageIFrame" src="iframe.html" width="50%" height="100" style="display: none;">
+        Browser does not support iframes.
+      </iframe>
+    </div>
+    <div id="debug" style="display: none"></div>
+  </div>
+  <pre id="test">
+<script type="text/javascript;version=1.8">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+                                   'imageIFrame', 'debug');
+  animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_xultree_animation.xhtml
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html
+xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      xml:lang="en" lang="en">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for Bug 666446 - Animated Images within SVG Filters</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="application/javascript" src="imgutils.js"></script>
+  <script type="application/javascript" src="animationPolling.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+  <xul:caption label="Bug 666446 - XULTree Test" />
+  <xul:separator />
+    <br />
+    <xul:window id="main" title="Bug 666446: XUL Tree Testing" width="100" height="100">
+      <xul:tree flex="1">
+        <xul:treecols>
+          <xul:treecol id="icon" label="Icon" flex="1" />
+        </xul:treecols>
+
+        <xul:treechildren>
+          <xul:treeitem id="referenceItem" hidden="true">
+            <xul:treerow>
+              <xul:treecell src="animated-gif-finalframe.gif" width="40" height="40" />
+            </xul:treerow>
+          </xul:treeitem>
+          <xul:treeitem id="imageItem" hidden="true">
+            <xul:treerow>
+              <xul:treecell src="animated-gif.gif" width="40" height="40" />
+            </xul:treerow>
+          </xul:treeitem>
+        </xul:treechildren>
+      </xul:tree>
+    </xul:window>
+  </div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript;version=1.8">
+
+/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
+
+const FAILURE_TIMEOUT = 5000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+  var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceItem',
+                                   'imageItem', 'debug', '',
+                                   'animated-gif-finalframe.gif', true);
+  animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
--- a/js/jsd/idl/jsdIDebuggerService.idl
+++ b/js/jsd/idl/jsdIDebuggerService.idl
@@ -269,17 +269,20 @@ interface jsdIDebuggerService : nsISuppo
      * depth", and equal number of unPause calles must be made to resume
      * normal debugging.
      *
      * @return depth Number of times pause has been called since the debugger
      *               has been unpaused.
      */
     unsigned long pause();
     /**
-     * Undo a pause.
+     * Undo a pause.  Once this is called, the debugger won't start
+     * getting execution callbacks until the stack is fully unwound so
+     * that no JS scripts are live.  There is no way to query whether
+     * there are such scripts left to unwind at a given point in time.
      *
      * @return depth The number of remaining pending pause calls.
      */
     unsigned long unPause();
     
     /**
      * Force the engine to perform garbage collection.
      */
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -466,17 +466,17 @@ jsds_NotifyPendingDeadScripts (JSContext
     JSRuntime *rt = JS_GetRuntime(cx);
 #endif
     jsdService *jsds = gJsds;
 
     nsCOMPtr<jsdIScriptHook> hook;
     if (jsds) {
         NS_ADDREF(jsds);
         jsds->GetScriptHook (getter_AddRefs(hook));
-        jsds->Pause(nsnull);
+        jsds->DoPause(nsnull, true);
     }
 
     DeadScript *deadScripts = gDeadScripts;
     gDeadScripts = nsnull;
     while (deadScripts) {
         DeadScript *ds = deadScripts;
         /* get next deleted script */
         deadScripts = reinterpret_cast<DeadScript *>
@@ -501,17 +501,17 @@ jsds_NotifyPendingDeadScripts (JSContext
 
         /* addref came from the FromPtr call in jsds_ScriptHookProc */
         NS_RELEASE(ds->script);
         /* free the struct! */
         PR_Free(ds);
     }
 
     if (jsds) {
-        jsds->UnPause(nsnull);
+        jsds->DoUnPause(nsnull, true);
         NS_RELEASE(jsds);
     }
 }
 
 static JSBool
 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
 {
 #ifdef DEBUG_verbose
@@ -578,19 +578,19 @@ jsds_ErrorHookProc (JSDContext *jsdc, JS
     else
     {
         line     = 0;
         pos      = 0;
         flags    = 0;
         errnum   = 0;
     }
     
-    gJsds->Pause(nsnull);
+    gJsds->DoPause(nsnull, true);
     hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval);
-    gJsds->UnPause(nsnull);
+    gJsds->DoUnPause(nsnull, true);
     
     running = false;
     if (!rval)
         return JSD_ERROR_REPORTER_DEBUG;
     
     return JSD_ERROR_REPORTER_PASS_ALONG;
 }
 
@@ -621,19 +621,19 @@ jsds_CallHookProc (JSDContext* jsdc, JSD
 
     if (!jsds_FilterHook (jsdc, jsdthreadstate))
         return JS_FALSE;
 
     JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
     nsCOMPtr<jsdIStackFrame> frame =
         getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
                                               native_frame));
-    gJsds->Pause(nsnull);
+    gJsds->DoPause(nsnull, true);
     hook->OnCall(frame, type);    
-    gJsds->UnPause(nsnull);
+    gJsds->DoUnPause(nsnull, true);
     jsdStackFrame::InvalidateAll();
 
     return JS_TRUE;
 }
 
 static PRUint32
 jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
                         uintN type, void* callerdata, jsval* rval)
@@ -683,23 +683,23 @@ jsds_ExecutionHookProc (JSDContext* jsdc
     
     if (!jsds_FilterHook (jsdc, jsdthreadstate))
         return JSD_HOOK_RETURN_CONTINUE;
     
     JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
     nsCOMPtr<jsdIStackFrame> frame =
         getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
                                               native_frame));
-    gJsds->Pause(nsnull);
+    gJsds->DoPause(nsnull, true);
     jsdIValue *inout_rv = js_rv;
     NS_IF_ADDREF(inout_rv);
     hook->OnExecute (frame, type, &inout_rv, &hook_rv);
     js_rv = inout_rv;
     NS_IF_RELEASE(inout_rv);
-    gJsds->UnPause(nsnull);
+    gJsds->DoUnPause(nsnull, true);
     jsdStackFrame::InvalidateAll();
         
     if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
         hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
         *rval = JSVAL_VOID;
         if (js_rv) {
             JSDValue *jsdv;
             if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv)))
@@ -729,19 +729,19 @@ jsds_ScriptHookProc (JSDContext* jsdc, J
             return;
         }
             
         nsCOMPtr<jsdIScript> script = 
             getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript));
 #ifdef CAUTIOUS_SCRIPTHOOK
         JS_UNKEEP_ATOMS(rt);
 #endif
-        gJsds->Pause(nsnull);
+        gJsds->DoPause(nsnull, true);
         hook->OnScriptCreated (script);
-        gJsds->UnPause(nsnull);
+        gJsds->DoUnPause(nsnull, true);
 #ifdef CAUTIOUS_SCRIPTHOOK
         JS_KEEP_ATOMS(rt);
 #endif
     } else {
         /* a script is being destroyed.  even if there is no registered hook
          * we'll still need to invalidate the jsdIScript record, in order
          * to remove the reference held in the JSDScript private data. */
         nsCOMPtr<jsdIScript> jsdis = 
@@ -758,19 +758,19 @@ jsds_ScriptHookProc (JSDContext* jsdc, J
                 return;
 
             /* if GC *isn't* running, we can tell the user about the script
              * delete now. */
 #ifdef CAUTIOUS_SCRIPTHOOK
             JS_UNKEEP_ATOMS(rt);
 #endif
                 
-            gJsds->Pause(nsnull);
+            gJsds->DoPause(nsnull, true);
             hook->OnScriptDestroyed (jsdis);
-            gJsds->UnPause(nsnull);
+            gJsds->DoUnPause(nsnull, true);
 #ifdef CAUTIOUS_SCRIPTHOOK
             JS_KEEP_ATOMS(rt);
 #endif
         } else {
             /* if a GC *is* running, we've got to wait until it's done before
              * we can execute any JS, so we queue the notification in a PRCList
              * until GC tells us it's done. See jsds_GCCallbackProc(). */
             DeadScript *ds = PR_NEW(DeadScript);
@@ -2541,31 +2541,22 @@ jsdService::On (void)
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 jsdService::AsyncOn (jsdIActivationCallback *activationCallback)
 {
     nsresult  rv;
 
-    /* get JS things from the CallContext */
     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
     if (NS_FAILED(rv)) return rv;
 
-    nsAXPCNativeCallContext *cc = nsnull;
-    rv = xpc->GetCurrentNativeCallContext(&cc);
-    if (NS_FAILED(rv)) return rv;
-
-    JSContext *cx;
-    rv = cc->GetJSContext (&cx);
-    if (NS_FAILED(rv)) return rv;
-
     mActivationCallback = activationCallback;
     
-    return xpc->SetDebugModeWhenPossible(true);
+    return xpc->SetDebugModeWhenPossible(true, true);
 }
 
 NS_IMETHODIMP
 jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, bool mode) {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
   /* XPConnect now does this work itself, so this IDL entry point is no longer used. */
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -2701,55 +2692,76 @@ jsdService::Off (void)
     printf ("+++ JavaScript debugging hooks removed.\n");
 #endif
 
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
     if (NS_FAILED(rv))
         return rv;
 
-    xpc->SetDebugModeWhenPossible(false);
+    xpc->SetDebugModeWhenPossible(false, true);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdService::GetPauseDepth(PRUint32 *_rval)
 {
     NS_ENSURE_ARG_POINTER(_rval);
     *_rval = mPauseLevel;
     return NS_OK;
 }
     
 NS_IMETHODIMP
 jsdService::Pause(PRUint32 *_rval)
 {
+    return DoPause(_rval, false);
+}
+
+nsresult
+jsdService::DoPause(PRUint32 *_rval, bool internalCall)
+{
     if (!mCx)
         return NS_ERROR_NOT_INITIALIZED;
 
     if (++mPauseLevel == 1) {
         JSD_SetErrorReporter (mCx, NULL, NULL);
         JSD_ClearThrowHook (mCx);
         JSD_ClearInterruptHook (mCx);
         JSD_ClearDebuggerHook (mCx);
         JSD_ClearDebugBreakHook (mCx);
         JSD_ClearTopLevelHook (mCx);
         JSD_ClearFunctionHook (mCx);
         JSD_DebuggerPause (mCx);
+
+        nsresult rv;
+        nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
+        if (NS_FAILED(rv)) return rv;
+
+        if (!internalCall) {
+            rv = xpc->SetDebugModeWhenPossible(false, false);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
     }
 
     if (_rval)
         *_rval = mPauseLevel;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 jsdService::UnPause(PRUint32 *_rval)
 {
+    return DoUnPause(_rval, false);
+}
+
+nsresult
+jsdService::DoUnPause(PRUint32 *_rval, bool internalCall)
+{
     if (!mCx)
         return NS_ERROR_NOT_INITIALIZED;
 
     if (mPauseLevel == 0)
         return NS_ERROR_NOT_AVAILABLE;
 
     /* check mOn before we muck with this stuff, it's possible the debugger
      * was turned off while we were paused.
@@ -2769,16 +2781,25 @@ jsdService::UnPause(PRUint32 *_rval)
         if (mTopLevelHook)
             JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
         else
             JSD_ClearTopLevelHook (mCx);
         if (mFunctionHook)
             JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
         else
             JSD_ClearFunctionHook (mCx);
+
+        nsresult rv;
+        nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
+        if (NS_FAILED(rv)) return rv;
+
+        if (!internalCall) {
+            rv = xpc->SetDebugModeWhenPossible(true, false);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
     }
     
     if (_rval)
         *_rval = mPauseLevel;
 
     return NS_OK;
 }
 
@@ -3105,19 +3126,19 @@ jsdService::EnterNestedEventLoop (jsdINe
     if (NS_FAILED(rv))
         return rv;
     PRUint32 nestLevel = ++mNestedLoopLevel;
     
     nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
 
     if (NS_SUCCEEDED(stack->Push(nsnull))) {
         if (callback) {
-            Pause(nsnull);
+            DoPause(nsnull, true);
             rv = callback->OnNest();
-            UnPause(nsnull);
+            DoUnPause(nsnull, true);
         }
         
         while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) {
             if (!NS_ProcessNextEvent(thread))
                 rv = NS_ERROR_UNEXPECTED;
         }
 
         JSContext* cx;
--- a/js/jsd/jsd_xpc.h
+++ b/js/jsd/jsd_xpc.h
@@ -286,16 +286,19 @@ class jsdService : public jsdIDebuggerSe
     }
 
     virtual ~jsdService();
     
     static jsdService *GetService ();
 
     bool CheckInterruptHook() { return !!mInterruptHook; }
     
+    nsresult DoPause(PRUint32 *_rval, bool internalCall);
+    nsresult DoUnPause(PRUint32 *_rval, bool internalCall);
+
   private:
     bool        mOn;
     PRUint32    mPauseLevel;
     PRUint32    mNestedLoopLevel;
     JSDContext *mCx;
     JSRuntime  *mRuntime;
 
     nsCOMPtr<jsdIErrorHook>     mErrorHook;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1770,16 +1770,26 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align"
            ;;
        esac
     fi
 
     _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/js-confdefs.h'
     _USE_CPP_INCLUDE_FLAG=1
 
+    # Recent clang and gcc support C++11 deleted functions without warnings if
+    # compiling with -std=c++0x or -std=gnu++0x (or c++11 or gnu++11 in very new
+    # versions).  We can't use -std=c++0x yet, so gcc's support must remain
+    # unused.  But clang's warning can be disabled, so when compiling with clang
+    # we use it to opt out of the warning, enabling (macro-encapsulated) use of
+    # deleted function syntax.
+    if test "$CLANG_CXX"; then
+        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-c++0x-extensions"
+    fi
+
     AC_CACHE_CHECK(whether the compiler supports -Wno-invalid-offsetof,
                    ac_has_wno_invalid_offsetof,
         [
             AC_LANG_SAVE
             AC_LANG_CPLUSPLUS
             _SAVE_CXXFLAGS="$CXXFLAGS"
             CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-invalid-offsetof"
             AC_TRY_COMPILE([],
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -900,16 +900,17 @@ OptimizeSpanDeps(JSContext *cx, Bytecode
                       case JSOP_IFNE:         op = JSOP_IFNEX; break;
                       case JSOP_OR:           op = JSOP_ORX; break;
                       case JSOP_AND:          op = JSOP_ANDX; break;
                       case JSOP_GOSUB:        op = JSOP_GOSUBX; break;
                       case JSOP_CASE:         op = JSOP_CASEX; break;
                       case JSOP_DEFAULT:      op = JSOP_DEFAULTX; break;
                       case JSOP_TABLESWITCH:  op = JSOP_TABLESWITCHX; break;
                       case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break;
+                      case JSOP_LABEL:        op = JSOP_LABELX; break;
                       default:
                         ReportStatementTooLarge(cx, bce);
                         return JS_FALSE;
                     }
                     *pc = (jsbytecode) op;
 
                     for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) {
                         if (sd2 <= sd) {
@@ -2810,17 +2811,17 @@ EmitNameOp(JSContext *cx, BytecodeEmitte
     return JS_TRUE;
 }
 
 #if JS_HAS_XML_SUPPORT
 static bool
 EmitXMLName(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(!bce->inStrictMode());
-    JS_ASSERT(pn->isXMLNameOp());
+    JS_ASSERT(pn->isKind(PNK_XMLUNARY));
     JS_ASSERT(pn->isOp(JSOP_XMLNAME));
     JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME);
 
     ParseNode *pn2 = pn->pn_kid;
     uintN oldflags = bce->flags;
     bce->flags &= ~TCF_IN_FOR_INIT;
     if (!EmitTree(cx, bce, pn2))
         return false;
@@ -4524,19 +4525,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
         break;
 #endif
       case PNK_LP:
         if (!EmitTree(cx, bce, lhs))
             return false;
         offset++;
         break;
 #if JS_HAS_XML_SUPPORT
-      case PNK_ANYNAME:
-      case PNK_AT:
-      case PNK_DBLCOLON:
+      case PNK_XMLUNARY:
         JS_ASSERT(!bce->inStrictMode());
         JS_ASSERT(lhs->isOp(JSOP_SETXMLNAME));
         if (!EmitTree(cx, bce, lhs->pn_kid))
             return false;
         if (Emit1(cx, bce, JSOP_BINDXMLNAME) < 0)
             return false;
         offset++;
         break;
@@ -4579,19 +4578,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
             } else {
                 bool isLength = (lhs->pn_atom == cx->runtime->atomState.lengthAtom);
                 EMIT_INDEX_OP(isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex);
             }
             break;
           case PNK_LB:
           case PNK_LP:
 #if JS_HAS_XML_SUPPORT
-          case PNK_ANYNAME:
-          case PNK_AT:
-          case PNK_DBLCOLON:
+          case PNK_XMLUNARY:
 #endif
             if (Emit1(cx, bce, JSOP_DUP2) < 0)
                 return false;
             if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
                 return false;
             break;
           default:;
         }
@@ -4656,19 +4653,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
 #if JS_HAS_DESTRUCTURING
       case PNK_RB:
       case PNK_RC:
         if (!EmitDestructuringOps(cx, bce, JSOP_SETNAME, lhs))
             return false;
         break;
 #endif
 #if JS_HAS_XML_SUPPORT
-      case PNK_ANYNAME:
-      case PNK_AT:
-      case PNK_DBLCOLON:
+      case PNK_XMLUNARY:
         JS_ASSERT(!bce->inStrictMode());
         if (Emit1(cx, bce, JSOP_SETXMLNAME) < 0)
             return false;
         break;
 #endif
       default:
         JS_ASSERT(0);
     }
@@ -6354,41 +6349,52 @@ frontend::EmitTree(JSContext *cx, Byteco
                     if (Emit1(cx, bce, op) < 0)
                         return JS_FALSE;
                 }
             }
         }
         break;
 
       case PNK_COLON:
-        /* Emit an annotated nop so we know to decompile a label. */
+        /*
+         * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
+         * following the labeled statement. This op has either a SRC_LABEL or
+         * SRC_LABELBRACE source note for the decompiler.
+         */
         atom = pn->pn_atom;
 
         jsatomid index;
         if (!bce->makeAtomIndex(atom, &index))
             return JS_FALSE;
 
         pn2 = pn->expr();
         noteType = (pn2->isKind(PNK_LC) ||
                     (pn2->isKind(PNK_LEXICALSCOPE) &&
                      pn2->expr()->isKind(PNK_LC)))
                    ? SRC_LABELBRACE
                    : SRC_LABEL;
         noteIndex = NewSrcNote2(cx, bce, noteType, ptrdiff_t(index));
-        if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
+        if (noteIndex < 0)
+            return JS_FALSE;
+
+        top = EmitJump(cx, bce, JSOP_LABEL, 0);
+        if (top < 0)
             return JS_FALSE;
 
         /* Emit code for the labeled statement. */
         PushStatement(bce, &stmtInfo, STMT_LABEL, bce->offset());
         stmtInfo.label = atom;
         if (!EmitTree(cx, bce, pn2))
             return JS_FALSE;
         if (!PopStatementBCE(cx, bce))
             return JS_FALSE;
 
+        /* Patch the JSOP_LABEL offset. */
+        CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, top);
+
         /* If the statement was compound, emit a note for the end brace. */
         if (noteType == SRC_LABELBRACE) {
             if (NewSrcNote(cx, bce, SRC_ENDBRACE) < 0 ||
                 Emit1(cx, bce, JSOP_NOP) < 0) {
                 return JS_FALSE;
             }
         }
         break;
@@ -6471,57 +6477,62 @@ frontend::EmitTree(JSContext *cx, Byteco
         CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp);
         if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
             return JS_FALSE;
         break;
 
       case PNK_OR:
       case PNK_AND:
         /*
-         * JSOP_OR converts the operand on the stack to boolean, and if true,
-         * leaves the original operand value on the stack and jumps; otherwise
-         * it pops and falls into the next bytecode, which evaluates the right
-         * operand.  The jump goes around the right operand evaluation.
+         * JSOP_OR converts the operand on the stack to boolean, leaves the original
+         * value on the stack and jumps if true; otherwise it falls into the next
+         * bytecode, which pops the left operand and then evaluates the right operand.
+         * The jump goes around the right operand evaluation.
          *
-         * JSOP_AND converts the operand on the stack to boolean, and if false,
-         * leaves the original operand value on the stack and jumps; otherwise
-         * it pops and falls into the right operand's bytecode.
+         * JSOP_AND converts the operand on the stack to boolean and jumps if false;
+         * otherwise it falls into the right operand's bytecode.
          */
         if (pn->isArity(PN_BINARY)) {
             if (!EmitTree(cx, bce, pn->pn_left))
                 return JS_FALSE;
-            top = EmitJump(cx, bce, JSOP_BACKPATCH_POP, 0);
+            top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
             if (top < 0)
                 return JS_FALSE;
+            if (Emit1(cx, bce, JSOP_POP) < 0)
+                return JS_FALSE;
             if (!EmitTree(cx, bce, pn->pn_right))
                 return JS_FALSE;
             off = bce->offset();
             pc = bce->code(top);
             CHECK_AND_SET_JUMP_OFFSET(cx, bce, pc, off - top);
             *pc = pn->getOp();
         } else {
             JS_ASSERT(pn->isArity(PN_LIST));
             JS_ASSERT(pn->pn_head->pn_next->pn_next);
 
             /* Left-associative operator chain: avoid too much recursion. */
             pn2 = pn->pn_head;
             if (!EmitTree(cx, bce, pn2))
                 return JS_FALSE;
-            top = EmitJump(cx, bce, JSOP_BACKPATCH_POP, 0);
+            top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
             if (top < 0)
                 return JS_FALSE;
+            if (Emit1(cx, bce, JSOP_POP) < 0)
+                return JS_FALSE;
 
             /* Emit nodes between the head and the tail. */
             jmp = top;
             while ((pn2 = pn2->pn_next)->pn_next) {
                 if (!EmitTree(cx, bce, pn2))
                     return JS_FALSE;
-                off = EmitJump(cx, bce, JSOP_BACKPATCH_POP, 0);
+                off = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
                 if (off < 0)
                     return JS_FALSE;
+                if (Emit1(cx, bce, JSOP_POP) < 0)
+                    return JS_FALSE;
                 if (!SetBackPatchDelta(cx, bce, bce->code(jmp), off - jmp))
                     return JS_FALSE;
                 jmp = off;
 
             }
             if (!EmitTree(cx, bce, pn2))
                 return JS_FALSE;
 
@@ -6572,21 +6583,17 @@ frontend::EmitTree(JSContext *cx, Byteco
                 if (Emit1(cx, bce, op) < 0)
                     return JS_FALSE;
             }
         } else {
 #if JS_HAS_XML_SUPPORT
             uintN oldflags;
 
       case PNK_DBLCOLON:
-            if (pn->getOp() == JSOP_XMLNAME) {
-                if (!EmitXMLName(cx, pn, JSOP_XMLNAME, bce))
-                    return JS_FALSE;
-                break;
-            }
+            JS_ASSERT(pn->getOp() != JSOP_XMLNAME);
             if (pn->isArity(PN_NAME)) {
                 if (!EmitTree(cx, bce, pn->expr()))
                     return JS_FALSE;
                 if (!EmitAtomOp(cx, pn, pn->getOp(), bce))
                     return JS_FALSE;
                 break;
             }
 
@@ -6607,46 +6614,57 @@ frontend::EmitTree(JSContext *cx, Byteco
 #if JS_HAS_XML_SUPPORT
             bce->flags |= oldflags & TCF_IN_FOR_INIT;
 #endif
             if (Emit1(cx, bce, pn->getOp()) < 0)
                 return JS_FALSE;
         }
         break;
 
+#if JS_HAS_XML_SUPPORT
+      case PNK_XMLUNARY:
+        if (pn->getOp() == JSOP_XMLNAME) {
+            if (!EmitXMLName(cx, pn, JSOP_XMLNAME, bce))
+                return false;
+        } else {
+            JSOp op = pn->getOp();
+            JS_ASSERT(op == JSOP_BINDXMLNAME || op == JSOP_SETXMLNAME);
+            uintN oldflags = bce->flags;
+            bce->flags &= ~TCF_IN_FOR_INIT;
+            if (!EmitTree(cx, bce, pn->pn_kid))
+                return false;
+            bce->flags |= oldflags & TCF_IN_FOR_INIT;
+            if (Emit1(cx, bce, op) < 0)
+                return false;
+        }
+        break;
+#endif
+
       case PNK_THROW:
 #if JS_HAS_XML_SUPPORT
       case PNK_AT:
       case PNK_DEFAULT:
         JS_ASSERT(pn->isArity(PN_UNARY));
         /* FALL THROUGH */
 #endif
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       unary_plusminus:
       {
-        uintN oldflags;
-
         /* Unary op, including unary +/-. */
         op = pn->getOp();
-#if JS_HAS_XML_SUPPORT
-        if (op == JSOP_XMLNAME) {
-            if (!EmitXMLName(cx, pn, op, bce))
-                return JS_FALSE;
-            break;
-        }
-#endif
         pn2 = pn->pn_kid;
 
+        JS_ASSERT(op != JSOP_XMLNAME);
         if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
             op = JSOP_TYPEOFEXPR;
 
-        oldflags = bce->flags;
+        uintN oldflags = bce->flags;
         bce->flags &= ~TCF_IN_FOR_INIT;
         if (!EmitTree(cx, bce, pn2))
             return JS_FALSE;
         bce->flags |= oldflags & TCF_IN_FOR_INIT;
         if (Emit1(cx, bce, op) < 0)
             return JS_FALSE;
         break;
       }
@@ -6717,19 +6735,17 @@ frontend::EmitTree(JSContext *cx, Byteco
             JS_ASSERT(js_CodeSpec[op].format & JOF_DECOMPOSE);
             JS_ASSERT(js_CodeSpec[op].format & JOF_ELEM);
             if (Emit1(cx, bce, (JSOp)1) < 0)
                 return JS_FALSE;
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return JS_FALSE;
             break;
 #if JS_HAS_XML_SUPPORT
-          case PNK_ANYNAME:
-          case PNK_AT:
-          case PNK_DBLCOLON:
+          case PNK_XMLUNARY:
             JS_ASSERT(!bce->inStrictMode());
             JS_ASSERT(pn2->isOp(JSOP_SETXMLNAME));
             if (!EmitTree(cx, bce, pn2->pn_kid))
                 return JS_FALSE;
             if (Emit1(cx, bce, JSOP_BINDXMLNAME) < 0)
                 return JS_FALSE;
             if (!EmitElemIncDec(cx, NULL, op, bce))
                 return JS_FALSE;
@@ -6880,19 +6896,17 @@ frontend::EmitTree(JSContext *cx, Byteco
                 return JS_FALSE;
             break;
           case PNK_LB:
             JS_ASSERT(pn2->isOp(JSOP_GETELEM));
             if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, bce))
                 return JS_FALSE;
             break;
 #if JS_HAS_XML_SUPPORT
-          case PNK_ANYNAME:
-          case PNK_AT:
-          case PNK_DBLCOLON:
+          case PNK_XMLUNARY:
             JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
             if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
                 return JS_FALSE;
             callop = true;          /* suppress JSOP_PUSH after */
             break;
 #endif
           default:
             if (!EmitTree(cx, bce, pn2))
@@ -7251,21 +7265,16 @@ frontend::EmitTree(JSContext *cx, Byteco
 
       case PNK_REGEXP:
         JS_ASSERT(pn->isOp(JSOP_REGEXP));
         ok = EmitIndexOp(cx, JSOP_REGEXP, bce->regexpList.index(pn->pn_objbox), bce);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case PNK_ANYNAME:
-        if (pn->getOp() == JSOP_XMLNAME) {
-            if (!EmitXMLName(cx, pn, JSOP_XMLNAME, bce))
-                return JS_FALSE;
-            break;
-        }
 #endif
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_THIS:
       case PNK_NULL:
         if (Emit1(cx, bce, pn->getOp()) < 0)
             return JS_FALSE;
         break;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -988,18 +988,18 @@ enum SrcNoteType {
     SRC_ASSIGNOP    = 8,        /* += or another assign-op follows */
     SRC_COND        = 9,        /* JSOP_IFEQ is from conditional ?: operator */
     SRC_BRACE       = 10,       /* mandatory brace, for scope or to avoid
                                    dangling else */
     SRC_HIDDEN      = 11,       /* opcode shouldn't be decompiled */
     SRC_PCBASE      = 12,       /* distance back from annotated getprop or
                                    setprop op to left-most obj.prop.subprop
                                    bytecode -- always a backward delta */
-    SRC_LABEL       = 13,       /* JSOP_NOP for label: with atomid immediate */
-    SRC_LABELBRACE  = 14,       /* JSOP_NOP for label: {...} begin brace */
+    SRC_LABEL       = 13,       /* JSOP_LABEL for label: with atomid immediate */
+    SRC_LABELBRACE  = 14,       /* JSOP_LABEL for label: {...} begin brace */
     SRC_ENDBRACE    = 15,       /* JSOP_NOP for label: {...} end brace */
     SRC_BREAK2LABEL = 16,       /* JSOP_GOTO for 'break label' with atomid */
     SRC_CONT2LABEL  = 17,       /* JSOP_GOTO for 'continue label' with atomid */
     SRC_SWITCH      = 18,       /* JSOP_*SWITCH with offset to end of switch,
                                    2nd off to first JSOP_CASE if condswitch */
     SRC_SWITCHBREAK = 18,       /* JSOP_GOTO is a break in a switch */
     SRC_FUNCDEF     = 19,       /* JSOP_NOP for function f() with atomid */
     SRC_CATCH       = 20,       /* catch block has guard */
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -122,16 +122,17 @@ enum ParseNodeKind {
     PNK_XMLTAGC,
     PNK_XMLNAME,
     PNK_XMLATTR,
     PNK_XMLSPACE,
     PNK_XMLTEXT,
     PNK_XMLCOMMENT,
     PNK_XMLCDATA,
     PNK_XMLPI,
+    PNK_XMLUNARY,
     PNK_AT,
     PNK_DBLCOLON,
     PNK_ANYNAME,
     PNK_DBLDOT,
     PNK_FILTER,
     PNK_XMLELEM,
     PNK_XMLLIST,
     PNK_YIELD,
@@ -374,16 +375,19 @@ enum ParseNodeKind {
  *                          of the expr member it overlays
  * PNK_NUMBER   dval        pn_dval: double value of numeric literal
  * PNK_TRUE,    nullary     pn_op: JSOp bytecode
  * PNK_FALSE,
  * PNK_NULL,
  * PNK_THIS
  *
  * <E4X node descriptions>
+ * PNK_XMLUNARY unary       pn_kid: PNK_AT, PNK_ANYNAME, or PNK_DBLCOLON node
+ *                          pn_op: JSOP_XMLNAME, JSOP_BINDXMLNAME, or
+ *                                 JSOP_SETXMLNAME
  * PNK_DEFAULT  name        pn_atom: default XML namespace string literal
  * PNK_FILTER   binary      pn_left: container expr, pn_right: filter expr
  * PNK_DBLDOT   binary      pn_left: container expr, pn_right: selector expr
  * PNK_ANYNAME  nullary     pn_op: JSOP_ANYNAME
  *                          pn_atom: cx->runtime->atomState.starAtom
  * PNK_AT       unary       pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr
  * PNK_DBLCOLON binary      pn_op: JSOP_QNAME
  *                          pn_left: PNK_ANYNAME or PNK_NAME node
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2472,19 +2472,17 @@ BindDestructuringLHS(JSContext *cx, Pars
         break;
 
       case PNK_LP:
         if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return JS_FALSE;
         break;
 
 #if JS_HAS_XML_SUPPORT
-      case PNK_ANYNAME:
-      case PNK_AT:
-      case PNK_DBLCOLON:
+      case PNK_XMLUNARY:
         JS_ASSERT(pn->isOp(JSOP_XMLNAME));
         pn->setOp(JSOP_BINDXMLNAME);
         break;
 #endif
 
       default:
         ReportCompileErrorNumber(cx, TS(tc->parser), pn,
                                  JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
@@ -3169,17 +3167,17 @@ Parser::forStatement()
                ((versionNumber() == JSVERSION_1_7 &&
                  pn->isOp(JSOP_ITER) &&
                  !(pn->pn_iflags & JSITER_FOREACH))
                 ? (!pn1->isKind(PNK_RB) || pn1->pn_count != 2)
                 : (!pn1->isKind(PNK_RB) && !pn1->isKind(PNK_RC))) &&
 #endif
                !pn1->isKind(PNK_LP) &&
 #if JS_HAS_XML_SUPPORT
-               (!pn1->isXMLNameOp() || !pn1->isOp(JSOP_XMLNAME)) &&
+               !pn1->isKind(PNK_XMLUNARY) &&
 #endif
                !pn1->isKind(PNK_LB)))
         {
             reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
             return NULL;
         }
 
         /*
@@ -4599,19 +4597,17 @@ Parser::setAssignmentLhsOps(ParseNode *p
             return false;
         break;
 #endif
       case PNK_LP:
         if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
         break;
 #if JS_HAS_XML_SUPPORT
-      case PNK_ANYNAME:
-      case PNK_AT:
-      case PNK_DBLCOLON:
+      case PNK_XMLUNARY:
         JS_ASSERT(pn->isOp(JSOP_XMLNAME));
         pn->setOp(JSOP_SETXMLNAME);
         break;
 #endif
       default:
         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
         return false;
     }
@@ -4683,17 +4679,17 @@ SetLvalKid(JSContext *cx, TokenStream *t
            const char *name)
 {
     if (!kid->isKind(PNK_NAME) &&
         !kid->isKind(PNK_DOT) &&
         (!kid->isKind(PNK_LP) ||
          (!kid->isOp(JSOP_CALL) && !kid->isOp(JSOP_EVAL) &&
           !kid->isOp(JSOP_FUNCALL) && !kid->isOp(JSOP_FUNAPPLY))) &&
 #if JS_HAS_XML_SUPPORT
-        (!kid->isXMLNameOp() || !kid->isOp(JSOP_XMLNAME)) &&
+        !kid->isKind(PNK_XMLUNARY) &&
 #endif
         !kid->isKind(PNK_LB))
     {
         ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
         return NULL;
     }
     if (!CheckStrictAssignment(cx, tc, kid))
         return NULL;
@@ -4726,19 +4722,17 @@ SetIncOpKid(JSContext *cx, TokenStream *
              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
         break;
 
       case PNK_LP:
         if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
             return JS_FALSE;
         /* FALL THROUGH */
 #if JS_HAS_XML_SUPPORT
-      case PNK_ANYNAME:
-      case PNK_AT:
-      case PNK_DBLCOLON:
+      case PNK_XMLUNARY:
         if (kid->isOp(JSOP_XMLNAME))
             kid->setOp(JSOP_SETXMLNAME);
         /* FALL THROUGH */
 #endif
       case PNK_LB:
         op = (tt == TOK_INC)
              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
@@ -5637,17 +5631,17 @@ Parser::memberExpr(JSBool allowCallSynta
         }
         pn->pn_pos.end = pn->last()->pn_pos.end;
     } else {
         pn = primaryExpr(tt, JS_FALSE);
         if (!pn)
             return NULL;
 
         if (pn->isXMLNameOp()) {
-            pn = new_<UnaryNode>(pn->getKind(), JSOP_XMLNAME, pn->pn_pos, pn);
+            pn = new_<UnaryNode>(PNK_XMLUNARY, JSOP_XMLNAME, pn->pn_pos, pn);
             if (!pn)
                 return NULL;
         }
     }
 
     while ((tt = tokenStream.getToken()) > TOK_EOF) {
         if (tt == TOK_DOT) {
             pn2 = NameNode::create(PNK_DOT, NULL, tc);
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -35,27 +35,92 @@
  * 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 <stdio.h>
 
 #include "jscntxt.h"
+#include "jscrashformat.h"
+#include "jscrashreport.h"
+#include "jsprf.h"
 #include "jsprobes.h"
 #include "jsutil.h"
-#include "jscrashformat.h"
-#include "jscrashreport.h"
 #include "prmjtime.h"
 
 #include "gc/Statistics.h"
 
 namespace js {
 namespace gcstats {
 
+Statistics::ColumnInfo::ColumnInfo(const char *title, double t, double total)
+  : title(title)
+{
+    JS_snprintf(str, sizeof(str), "%.1f", t);
+    JS_snprintf(totalStr, sizeof(totalStr), "%.1f", total);
+    width = 6;
+}
+
+Statistics::ColumnInfo::ColumnInfo(const char *title, double t)
+  : title(title)
+{
+    JS_snprintf(str, sizeof(str), "%.1f", t);
+    strcpy(totalStr, "n/a");
+    width = 6;
+}
+
+Statistics::ColumnInfo::ColumnInfo(const char *title, unsigned int data)
+  : title(title)
+{
+    JS_snprintf(str, sizeof(str), "%d", data);
+    strcpy(totalStr, "n/a");
+    width = 4;
+}
+
+Statistics::ColumnInfo::ColumnInfo(const char *title, const char *data)
+  : title(title)
+{
+    JS_ASSERT(strlen(data) < sizeof(str));
+    strcpy(str, data);
+    strcpy(totalStr, "n/a ");
+    width = 0;
+}
+
+static const int NUM_COLUMNS = 17;
+
+void
+Statistics::makeTable(ColumnInfo *cols)
+{
+    int i = 0;
+
+    cols[i++] = ColumnInfo("Type", compartment ? "Comp" : "Glob");
+
+    cols[i++] = ColumnInfo("Total", t(PHASE_GC), total(PHASE_GC));
+    cols[i++] = ColumnInfo("Wait", beginDelay(PHASE_MARK, PHASE_GC));
+    cols[i++] = ColumnInfo("Mark", t(PHASE_MARK), total(PHASE_MARK));
+    cols[i++] = ColumnInfo("Sweep", t(PHASE_SWEEP), total(PHASE_SWEEP));
+    cols[i++] = ColumnInfo("FinObj", t(PHASE_SWEEP_OBJECT), total(PHASE_SWEEP_OBJECT));
+    cols[i++] = ColumnInfo("FinStr", t(PHASE_SWEEP_STRING), total(PHASE_SWEEP_STRING));
+    cols[i++] = ColumnInfo("FinScr", t(PHASE_SWEEP_SCRIPT), total(PHASE_SWEEP_SCRIPT));
+    cols[i++] = ColumnInfo("FinShp", t(PHASE_SWEEP_SHAPE), total(PHASE_SWEEP_SHAPE));
+    cols[i++] = ColumnInfo("DisCod", t(PHASE_DISCARD_CODE), total(PHASE_DISCARD_CODE));
+    cols[i++] = ColumnInfo("DisAnl", t(PHASE_DISCARD_ANALYSIS), total(PHASE_DISCARD_ANALYSIS));
+    cols[i++] = ColumnInfo("XPCnct", t(PHASE_XPCONNECT), total(PHASE_XPCONNECT));
+    cols[i++] = ColumnInfo("Destry", t(PHASE_DESTROY), total(PHASE_DESTROY));
+    cols[i++] = ColumnInfo("End", endDelay(PHASE_GC, PHASE_DESTROY));
+
+    cols[i++] = ColumnInfo("+Chu", counts[STAT_NEW_CHUNK]);
+    cols[i++] = ColumnInfo("-Chu", counts[STAT_DESTROY_CHUNK]);
+
+    cols[i++] = ColumnInfo("Reason", ExplainReason(triggerReason));
+
+    JS_ASSERT(i == NUM_COLUMNS);
+}
+
 Statistics::Statistics(JSRuntime *rt)
   : runtime(rt)
 {
     char *env = getenv("MOZ_GCTIMER");
     if (!env || strcmp(env, "none") == 0) {
         fp = NULL;
         return;
     }
@@ -67,37 +132,43 @@ Statistics::Statistics(JSRuntime *rt)
         fullFormat = false;
         fp = stderr;
     } else {
         fullFormat = true;
 
         fp = fopen(env, "a");
         JS_ASSERT(fp);
 
-        fprintf(fp, "     AppTime,  Total,   Wait,   Mark,  Sweep, FinObj,"
-                " FinStr, FinScr, FinShp, Destry,    End, +Chu, -Chu, T, Reason\n");
+        fprintf(fp, "     AppTime");
+
+        ColumnInfo cols[NUM_COLUMNS];
+        makeTable(cols);
+        for (int i = 0; i < NUM_COLUMNS; i++)
+            fprintf(fp, ", %*s", cols[i].width, cols[i].title);
+        fprintf(fp, "\n");
     }
 
     PodArrayZero(counts);
     PodArrayZero(totals);
 
     startupTime = PRMJ_Now();
 }
 
 Statistics::~Statistics()
 {
     if (fp) {
-        if (fullFormat)
-            fprintf(fp,
-                    "------>TOTAL  "
-                    "%6.1f,         %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f\n",
-                    total(PHASE_GC), total(PHASE_MARK), total(PHASE_SWEEP),
-                    total(PHASE_SWEEP_OBJECT), total(PHASE_SWEEP_STRING),
-                    total(PHASE_SWEEP_SCRIPT), total(PHASE_SWEEP_SHAPE),
-                    total(PHASE_DESTROY));
+        if (fullFormat) {
+            fprintf(fp, "------>TOTAL");
+
+            ColumnInfo cols[NUM_COLUMNS];
+            makeTable(cols);
+            for (int i = 0; i < NUM_COLUMNS && cols[i].totalStr[0]; i++)
+                fprintf(fp, ", %*s", cols[i].width, cols[i].totalStr);
+            fprintf(fp, "\n");
+        }
 
         if (fp != stdout && fp != stderr)
             fclose(fp);
     }
 }
 
 struct GCCrashData
 {
@@ -145,33 +216,51 @@ Statistics::beginDelay(Phase phase1, Pha
 
 double
 Statistics::endDelay(Phase phase1, Phase phase2)
 {
     return double(phaseEnds[phase1] - phaseEnds[phase2]) / PRMJ_USEC_PER_MSEC;
 }
 
 void
+Statistics::statsToString(char *buffer, size_t size)
+{
+    JS_ASSERT(size);
+    buffer[0] = 0x00;
+
+    ColumnInfo cols[NUM_COLUMNS];
+    makeTable(cols);
+
+    size_t pos = 0;
+    for (int i = 0; i < NUM_COLUMNS; i++) {
+        int len = strlen(cols[i].title) + 1 + strlen(cols[i].str);
+        if (i > 0)
+            len += 2;
+        if (pos + len >= size)
+            break;
+        if (i > 0)
+            strcat(buffer, ", ");
+        strcat(buffer, cols[i].title);
+        strcat(buffer, ":");
+        strcat(buffer, cols[i].str);
+        pos += len;
+    }
+}
+
+void
 Statistics::printStats()
 {
     if (fullFormat) {
-        /*       App   , Total, Wait , Mark , Sweep, FinOb, FinSt, FinSc, FinSh, Destry, End */
-        fprintf(fp,
-                "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, ",
-                double(phaseStarts[PHASE_GC] - startupTime) / PRMJ_USEC_PER_MSEC,
-                t(PHASE_GC),
-                beginDelay(PHASE_MARK, PHASE_GC),
-                t(PHASE_MARK), t(PHASE_SWEEP),
-                t(PHASE_SWEEP_OBJECT), t(PHASE_SWEEP_STRING),
-                t(PHASE_SWEEP_SCRIPT), t(PHASE_SWEEP_SHAPE),
-                t(PHASE_DESTROY),
-                endDelay(PHASE_GC, PHASE_DESTROY));
+        fprintf(fp, "%12.0f", double(phaseStarts[PHASE_GC] - startupTime) / PRMJ_USEC_PER_MSEC);
 
-        fprintf(fp, "%4d, %4d,", counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]);
-        fprintf(fp, " %s, %s\n", compartment ? "C" : "G", ExplainReason(triggerReason));
+        ColumnInfo cols[NUM_COLUMNS];
+        makeTable(cols);
+        for (int i = 0; i < NUM_COLUMNS; i++)
+            fprintf(fp, ", %*s", cols[i].width, cols[i].str);
+        fprintf(fp, "\n");
     } else {
         fprintf(fp, "%f %f %f\n",
                 t(PHASE_GC), t(PHASE_MARK), t(PHASE_SWEEP));
     }
     fflush(fp);
 }
 
 void
@@ -189,16 +278,22 @@ Statistics::endGC()
         (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0);
         (*cb)(JS_TELEMETRY_GC_IS_SHAPE_REGEN,
               runtime->shapeGen & SHAPE_OVERFLOW_BIT ? 1 : 0);
         (*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC));
         (*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK));
         (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP));
     }
 
+    if (JSGCFinishedCallback cb = runtime->gcFinishedCallback) {
+        char buffer[1024];
+        statsToString(buffer, sizeof(buffer));
+        (*cb)(runtime, compartment, buffer);
+    }
+
     if (fp)
         printStats();
 
     PodArrayZero(counts);
 }
 
 void
 Statistics::beginPhase(Phase phase)
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -79,16 +79,19 @@ ExplainReason(Reason r)
 enum Phase {
     PHASE_GC,
     PHASE_MARK,
     PHASE_SWEEP,
     PHASE_SWEEP_OBJECT,
     PHASE_SWEEP_STRING,
     PHASE_SWEEP_SCRIPT,
     PHASE_SWEEP_SHAPE,
+    PHASE_DISCARD_CODE,
+    PHASE_DISCARD_ANALYSIS,
+    PHASE_XPCONNECT,
     PHASE_DESTROY,
 
     PHASE_LIMIT
 };
 
 enum Stat {
     STAT_NEW_CHUNK,
     STAT_DESTROY_CHUNK,
@@ -128,16 +131,32 @@ struct Statistics {
     uint64 totals[PHASE_LIMIT];
     unsigned int counts[STAT_LIMIT];
 
     double t(Phase phase);
     double total(Phase phase);
     double beginDelay(Phase phase1, Phase phase2);
     double endDelay(Phase phase1, Phase phase2);
     void printStats();
+    void statsToString(char *buffer, size_t size);
+
+    struct ColumnInfo {
+        const char *title;
+        char str[12];
+        char totalStr[12];
+        int width;
+
+        ColumnInfo() {}
+        ColumnInfo(const char *title, double t, double total);
+        ColumnInfo(const char *title, double t);
+        ColumnInfo(const char *title, unsigned int data);
+        ColumnInfo(const char *title, const char *data);
+    };
+
+    void makeTable(ColumnInfo *cols);
 };
 
 struct AutoGC {
     AutoGC(Statistics &stats, JSCompartment *comp, Reason reason JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginGC(comp, reason); }
     ~AutoGC() { stats.endGC(); }
 
     Statistics &stats;
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -563,28 +563,16 @@ ScriptAnalysis::analyzeBytecode(JSContex
         uint32 type = JOF_TYPE(js_CodeSpec[op].format);
 
         /* Check basic jump opcodes, which may or may not have a fallthrough. */
         if (type == JOF_JUMP || type == JOF_JUMPX) {
             /* Some opcodes behave differently on their branching path. */
             unsigned newStackDepth = stackDepth;
 
             switch (op) {
-              case JSOP_OR:
-              case JSOP_AND:
-              case JSOP_ORX:
-              case JSOP_ANDX:
-                /*
-                 * OR/AND instructions push the operation result when branching.
-                 * We accounted for this in GetDefCount, so subtract the pushed value
-                 * for the fallthrough case.
-                 */
-                stackDepth--;
-                break;
-
               case JSOP_CASE:
               case JSOP_CASEX:
                 /* Case instructions do not push the lvalue back when branching. */
                 newStackDepth--;
                 break;
 
               default:;
             }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -674,16 +674,17 @@ JSRuntime::JSRuntime()
     gcRegenShapes(false),
 #ifdef JS_GC_ZEAL
     gcZeal_(0),
     gcZealFrequency(0),
     gcNextScheduled(0),
     gcDebugCompartmentGC(false),
 #endif
     gcCallback(NULL),
+    gcFinishedCallback(NULL),
     gcMallocBytes(0),
     gcBlackRootsTraceOp(NULL),
     gcBlackRootsData(NULL),
     gcGrayRootsTraceOp(NULL),
     gcGrayRootsData(NULL),
     NaNValue(UndefinedValue()),
     negativeInfinityValue(UndefinedValue()),
     positiveInfinityValue(UndefinedValue()),
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2172,21 +2172,22 @@ JS_StringToVersion(const char *string);
 #define JSOPTION_METHODJIT      JS_BIT(14)      /* Whole-method JIT. */
 #define JSOPTION_PROFILING      JS_BIT(15)      /* Profiler to make tracer/methodjit choices. */
 #define JSOPTION_METHODJIT_ALWAYS \
                                 JS_BIT(16)      /* Always whole-method JIT,
                                                    don't tune at run-time. */
 #define JSOPTION_PCCOUNT        JS_BIT(17)      /* Collect per-op execution counts */
 
 #define JSOPTION_TYPE_INFERENCE JS_BIT(18)      /* Perform type inference. */
+#define JSOPTION_SOFTEN         JS_BIT(19)      /* Disable JIT hardening. */
 
 /* Options which reflect compile-time properties of scripts. */
 #define JSCOMPILEOPTION_MASK    (JSOPTION_XML)
 
-#define JSRUNOPTION_MASK        (JS_BITMASK(19) & ~JSCOMPILEOPTION_MASK)
+#define JSRUNOPTION_MASK        (JS_BITMASK(20) & ~JSCOMPILEOPTION_MASK)
 #define JSALLOPTION_MASK        (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
 
 extern JS_PUBLIC_API(uint32)
 JS_GetOptions(JSContext *cx);
 
 extern JS_PUBLIC_API(uint32)
 JS_SetOptions(JSContext *cx, uint32 options);
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -540,16 +540,17 @@ struct JSRuntime
         return false;
     }
 #else
     int gcZeal() { return 0; }
     bool needZealousGC() { return false; }
 #endif
 
     JSGCCallback        gcCallback;
+    JSGCFinishedCallback gcFinishedCallback;
 
   private:
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from gcMaxMallocBytes down to zero.
      */
     volatile ptrdiff_t  gcMallocBytes;
 
@@ -1122,16 +1123,17 @@ struct JSContext
     bool hasRunOption(uintN ropt) const {
         JS_ASSERT((ropt & JSRUNOPTION_MASK) == ropt);
         return !!(runOptions & ropt);
     }
 
     bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
     bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
     bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
+    bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
 
     js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; }
     inline js::LifoAlloc &typeLifoAlloc();
 
 #ifdef JS_THREADSAFE
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -490,37 +490,43 @@ JSCompartment::sweep(JSContext *cx, bool
 
     sweepBreakpoints(cx);
 
 #ifdef JS_TRACER
     if (hasTraceMonitor())
         traceMonitor()->sweep(cx);
 #endif
 
-    /*
-     * Kick all frames on the stack into the interpreter, and release all JIT
-     * code in the compartment.
-     */
-#ifdef JS_METHODJIT
-    mjit::ClearAllFrames(this);
-
-    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
-        mjit::ReleaseScriptCode(cx, script);
+    {
+        gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
 
         /*
-         * Use counts for scripts are reset on GC. After discarding code we
-         * need to let it warm back up to get information like which opcodes
-         * are setting array holes or accessing getter properties.
+         * Kick all frames on the stack into the interpreter, and release all JIT
+         * code in the compartment.
          */
-        script->resetUseCount();
+#ifdef JS_METHODJIT
+        mjit::ClearAllFrames(this);
+
+        for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            mjit::ReleaseScriptCode(cx, script);
+
+            /*
+             * Use counts for scripts are reset on GC. After discarding code we
+             * need to let it warm back up to get information like which opcodes
+             * are setting array holes or accessing getter properties.
+             */
+            script->resetUseCount();
+        }
+#endif
     }
-#endif
 
     if (!activeAnalysis) {
+        gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
+
         /*
          * Clear the analysis pool, but don't release its data yet. While
          * sweeping types any live data will be allocated into the pool.
          */
         LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
         oldAlloc.steal(&typeLifoAlloc);
 
         /*
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -224,16 +224,22 @@ JS_GetCustomIteratorCount(JSContext *cx)
 }
 
 JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback)
 {
     rt->telemetryCallback = callback;
 }
 
+JS_FRIEND_API(void)
+JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback)
+{
+    rt->gcFinishedCallback = callback;
+}
+
 #ifdef DEBUG
 
 struct DumpingChildInfo {
     void *node;
     JSGCTraceKind kind;
 
     DumpingChildInfo (void *n, JSGCTraceKind k)
         : node(n), kind(k)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -92,16 +92,22 @@ enum {
 };
 
 typedef void
 (* JSAccumulateTelemetryDataCallback)(int id, JSUint32 sample);
 
 extern JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback);
 
+typedef void
+(* JSGCFinishedCallback)(JSRuntime *rt, JSCompartment *comp, const char *description);
+
+extern JS_FRIEND_API(void)
+JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback);
+
 /* Data for tracking analysis/inference memory usage. */
 typedef struct TypeInferenceMemoryStats
 {
     int64 scripts;
     int64 objects;
     int64 tables;
     int64 temporary;
     int64 emptyShapes;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2630,18 +2630,21 @@ SweepPhase(JSContext *cx, GCMarker *gcma
          */
         rt->gcChunkPool.expire(rt, gckind == GC_SHRINK);
 #endif
 
         if (gckind == GC_SHRINK)
             DecommitFreePages(cx);
     }
 
-    if (rt->gcCallback)
-        (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
+    {
+        gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT);
+        if (rt->gcCallback)
+            (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
+    }
 }
 
 /*
  * Perform mark-and-sweep GC.
  *
  * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
  * other thread must be either outside all requests or blocked waiting for GC
  * to finish. The caller must hold rt->gcLock.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1487,16 +1487,20 @@ class GCHelperThread {
     bool canBackgroundAllocate() const {
         return backgroundAllocation;
     }
 
     void disableBackgroundAllocation() {
         backgroundAllocation = false;
     }
 
+    PRThread *getThread() const {
+        return thread;
+    }
+
     /*
      * Outside the GC lock may give true answer when in fact the sweeping has
      * been done.
      */
     bool sweeping() const {
         return state == SWEEPING;
     }
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3384,16 +3384,18 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_POPV:
       case JSOP_DEBUGGER:
       case JSOP_SETCALL:
       case JSOP_TABLESWITCH:
       case JSOP_TABLESWITCHX:
       case JSOP_LOOKUPSWITCH:
       case JSOP_LOOKUPSWITCHX:
       case JSOP_TRY:
+      case JSOP_LABEL:
+      case JSOP_LABELX:
         break;
 
         /* Bytecodes pushing values of known type. */
       case JSOP_VOID:
       case JSOP_PUSH:
         pushed[0].addType(cx, Type::UndefinedType());
         break;
       case JSOP_ZERO:
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1327,29 +1327,30 @@ js::FindUpvarFrame(JSContext *cx, uintN 
 #define PUSH_INT32(i)            regs.sp++->setInt32(i)
 #define PUSH_STRING(s)           do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
 #define PUSH_OBJECT(obj)         do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0)
 #define PUSH_HOLE()              regs.sp++->setMagic(JS_ARRAY_HOLE)
 #define POP_COPY_TO(v)           v = *--regs.sp
 #define POP_RETURN_VALUE()       regs.fp()->setReturnValue(*--regs.sp)
 
-#define POP_BOOLEAN(cx, vp, b)                                                \
+#define VALUE_TO_BOOLEAN(cx, vp, b)                                           \
     JS_BEGIN_MACRO                                                            \
         vp = &regs.sp[-1];                                                    \
         if (vp->isNull()) {                                                   \
             b = false;                                                        \
         } else if (vp->isBoolean()) {                                         \
             b = vp->toBoolean();                                              \
         } else {                                                              \
             b = !!js_ValueToBoolean(*vp);                                     \
         }                                                                     \
-        regs.sp--;                                                            \
     JS_END_MACRO
 
+#define POP_BOOLEAN(cx, vp, b)   do { VALUE_TO_BOOLEAN(cx, vp, b); regs.sp--; } while(0)
+
 #define VALUE_TO_OBJECT(cx, vp, obj)                                          \
     JS_BEGIN_MACRO                                                            \
         if ((vp)->isObject()) {                                               \
             obj = &(vp)->toObject();                                          \
         } else {                                                              \
             obj = js_ValueToNonNullObject(cx, *(vp));                         \
             if (!obj)                                                         \
                 goto error;                                                   \
@@ -2084,16 +2085,22 @@ ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
 END_EMPTY_CASES
 
 BEGIN_CASE(JSOP_TRACE)
 BEGIN_CASE(JSOP_NOTRACE)
     /* No-op */
 END_CASE(JSOP_TRACE)
 
+BEGIN_CASE(JSOP_LABEL)
+END_CASE(JSOP_LABEL)
+
+BEGIN_CASE(JSOP_LABELX)
+END_CASE(JSOP_LABELX)
+
 check_backedge:
 {
     CHECK_BRANCH();
     if (op != JSOP_NOTRACE && op != JSOP_TRACE)
         DO_OP();
 
 #ifdef JS_TRACER
     if (TRACING_ENABLED(cx) && (TRACE_RECORDER(cx) || TRACE_PROFILER(cx) || (op == JSOP_TRACE && !useMethodJIT))) {
@@ -2315,34 +2322,32 @@ BEGIN_CASE(JSOP_IFNE)
         BRANCH(len);
     }
 }
 END_CASE(JSOP_IFNE)
 
 BEGIN_CASE(JSOP_OR)
 {
     bool cond;
-    Value *vp;
-    POP_BOOLEAN(cx, vp, cond);
+    Value *_;
+    VALUE_TO_BOOLEAN(cx, _, cond);
     if (cond == true) {
         len = GET_JUMP_OFFSET(regs.pc);
-        PUSH_COPY(*vp);
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_OR)
 
 BEGIN_CASE(JSOP_AND)
 {
     bool cond;
-    Value *vp;
-    POP_BOOLEAN(cx, vp, cond);
+    Value *_;
+    VALUE_TO_BOOLEAN(cx, _, cond);
     if (cond == false) {
         len = GET_JUMP_OFFSET(regs.pc);
-        PUSH_COPY(*vp);
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_AND)
 
 BEGIN_CASE(JSOP_DEFAULTX)
     regs.sp--;
     /* FALL THROUGH */
@@ -2375,34 +2380,32 @@ BEGIN_CASE(JSOP_IFNEX)
         BRANCH(len);
     }
 }
 END_CASE(JSOP_IFNEX)
 
 BEGIN_CASE(JSOP_ORX)
 {
     bool cond;
-    Value *vp;
-    POP_BOOLEAN(cx, vp, cond);
+    Value *_;
+    VALUE_TO_BOOLEAN(cx, _, cond);
     if (cond == true) {
         len = GET_JUMPX_OFFSET(regs.pc);
-        PUSH_COPY(*vp);
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_ORX)
 
 BEGIN_CASE(JSOP_ANDX)
 {
     bool cond;
-    Value *vp;
-    POP_BOOLEAN(cx, vp, cond);
+    Value *_;
+    VALUE_TO_BOOLEAN(cx, _, cond);
     if (cond == JS_FALSE) {
         len = GET_JUMPX_OFFSET(regs.pc);
-        PUSH_COPY(*vp);
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_ANDX)
 
 /*
  * If the index value at sp[n] is not an int that fits in a jsval, it could
  * be an object (an XML QName, AttributeName, or AnyName), but only if we are
@@ -4462,19 +4465,16 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
     JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
     PUSH_COPY(obj->getFlatClosureUpvar(index));
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
     if (op == JSOP_CALLFCSLOT)
         PUSH_UNDEFINED();
 }
 END_CASE(JSOP_GETFCSLOT)
 
-BEGIN_CASE(JSOP_UNUSED0)
-BEGIN_CASE(JSOP_UNUSED1)
-
 BEGIN_CASE(JSOP_DEFCONST)
 BEGIN_CASE(JSOP_DEFVAR)
 {
     uint32 index = GET_INDEX(regs.pc);
     PropertyName *name = atoms[index]->asPropertyName();
 
     JSObject *obj = &regs.fp()->varObj();
     JS_ASSERT(!obj->getOps()->defineProperty);
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2384,37 +2384,16 @@ Decompile(SprintStack *ss, jsbytecode *p
                     DECOMPILE_CODE(pc2, next);
                     jp->indent -= 4;
                     js_printf(jp, "\t}\n");
 
                     /* Set len so pc skips over the entire loop. */
                     len = tail + js_CodeSpec[pc[tail]].length;
                     break;
 
-                  case SRC_LABEL:
-                    GET_SOURCE_NOTE_ATOM(sn, atom);
-                    jp->indent -= 4;
-                    rval = QuoteString(&ss->sprinter, atom, 0);
-                    if (!rval)
-                        return NULL;
-                    RETRACT(&ss->sprinter, rval);
-                    js_printf(jp, "\t%s:\n", rval);
-                    jp->indent += 4;
-                    break;
-
-                  case SRC_LABELBRACE:
-                    GET_SOURCE_NOTE_ATOM(sn, atom);
-                    rval = QuoteString(&ss->sprinter, atom, 0);
-                    if (!rval)
-                        return NULL;
-                    RETRACT(&ss->sprinter, rval);
-                    js_printf(jp, "\t%s: {\n", rval);
-                    jp->indent += 4;
-                    break;
-
                   case SRC_ENDBRACE:
                     jp->indent -= 4;
                     js_printf(jp, "\t}\n");
                     break;
 
                   case SRC_FUNCDEF:
                     fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
                   do_function:
@@ -2441,16 +2420,48 @@ Decompile(SprintStack *ss, jsbytecode *p
                     jp->indent -= 4;
                     js_printf(jp, "\t}\n");
                     break;
 
                   default:;
                 }
                 break;
 
+              case JSOP_LABEL:
+              case JSOP_LABELX:
+                sn = js_GetSrcNote(jp->script, pc);
+                todo = -2;
+                switch (sn ? SN_TYPE(sn) : SRC_NULL) {
+                  case SRC_LABEL:
+                    GET_SOURCE_NOTE_ATOM(sn, atom);
+                    jp->indent -= 4;
+                    rval = QuoteString(&ss->sprinter, atom, 0);
+                    if (!rval)
+                        return NULL;
+                    RETRACT(&ss->sprinter, rval);
+                    js_printf(jp, "\t%s:\n", rval);
+                    jp->indent += 4;
+                    break;
+
+                  case SRC_LABELBRACE:
+                    GET_SOURCE_NOTE_ATOM(sn, atom);
+                    rval = QuoteString(&ss->sprinter, atom, 0);
+                    if (!rval)
+                        return NULL;
+                    RETRACT(&ss->sprinter, rval);
+                    js_printf(jp, "\t%s: {\n", rval);
+                    jp->indent += 4;
+                    break;
+
+                  default:
+                    JS_NOT_REACHED("JSOP_LABEL without source note");
+                    break;
+                }
+                break;
+
               case JSOP_PUSH:
 #if JS_HAS_DESTRUCTURING
                 sn = js_GetSrcNote(jp->script, pc);
                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
                     pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
                     if (!pc)
                         return NULL;
                     LOCAL_ASSERT(*pc == JSOP_POPN);
@@ -3459,16 +3470,18 @@ Decompile(SprintStack *ss, jsbytecode *p
 
               do_logical_connective:
                 /* Top of stack is the first clause in a disjunction (||). */
                 lval = JS_strdup(cx, POP_STR());
                 if (!lval)
                     return NULL;
                 done = pc + GetJumpOffset(pc, pc);
                 pc += len;
+                JS_ASSERT(*pc == JSOP_POP);
+                pc += JSOP_POP_LENGTH;
                 len = done - pc;
                 if (!Decompile(ss, pc, len)) {
                     cx->free_((char *)lval);
                     return NULL;
                 }
                 rval = POP_STR();
                 if (jp->pretty &&
                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -182,18 +182,18 @@ OPDEF(JSOP_NAME,      59, "name",       
 OPDEF(JSOP_DOUBLE,    60, "double",     NULL,         3,  0,  1, 16,  JOF_ATOM)
 OPDEF(JSOP_STRING,    61, "string",     NULL,         3,  0,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_ZERO,      62, "zero",       "0",          1,  0,  1, 16,  JOF_BYTE)
 OPDEF(JSOP_ONE,       63, "one",        "1",          1,  0,  1, 16,  JOF_BYTE)
 OPDEF(JSOP_NULL,      64, js_null_str,  js_null_str,  1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_THIS,      65, js_this_str,  js_this_str,  1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_FALSE,     66, js_false_str, js_false_str, 1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_TRUE,      67, js_true_str,  js_true_str,  1,  0,  1, 19,  JOF_BYTE)
-OPDEF(JSOP_OR,        68, "or",         NULL,         3,  1,  0,  5,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
-OPDEF(JSOP_AND,       69, "and",        NULL,         3,  1,  0,  6,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
+OPDEF(JSOP_OR,        68, "or",         NULL,         3,  1,  1,  5,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
+OPDEF(JSOP_AND,       69, "and",        NULL,         3,  1,  1,  6,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
 
 /* The switch bytecodes have variable length. */
 OPDEF(JSOP_TABLESWITCH,  70, "tableswitch",  NULL,   -1,  1,  0,  0,  JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD)
 OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL,   -1,  1,  0,  0,  JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD)
 
 /* New, infallible/transitive identity ops. */
 OPDEF(JSOP_STRICTEQ,  72, "stricteq",   "===",        1,  2,  1, 10,  JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
 OPDEF(JSOP_STRICTNE,  73, "strictne",   "!==",        1,  2,  1, 10,  JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
@@ -269,19 +269,19 @@ OPDEF(JSOP_ARGDEC,   100, "argdec",     
 
 OPDEF(JSOP_INCLOCAL,  101,"inclocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_TMPSLOT3)
 OPDEF(JSOP_DECLOCAL,  102,"declocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
 OPDEF(JSOP_LOCALINC,  103,"localinc",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
 OPDEF(JSOP_LOCALDEC,  104,"localdec",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
 
 OPDEF(JSOP_IMACOP,    105,"imacop",     NULL,         1,  0,  0,  0,  JOF_BYTE)
 
-/* Static binding for globals. */
-OPDEF(JSOP_UNUSED0,   106,"unused0",    NULL,         3,  0,  1, 19,  JOF_BYTE)
-OPDEF(JSOP_UNUSED1,   107,"unused1",    NULL,         3,  0,  2, 19,  JOF_BYTE)
+/* The argument is the offset to the next statement and is used by IonMonkey. */
+OPDEF(JSOP_LABEL,     106,"label",     NULL,          3,  0,  0,  0,  JOF_JUMP)
+OPDEF(JSOP_LABELX,    107,"labelx",    NULL,          5,  0,  0,  0,  JOF_JUMPX)
 
 /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
 OPDEF(JSOP_FUNCALL,   108,"funcall",    NULL,         3, -1,  1, 18,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
 
 /* This opcode stores an index that is unique to the given loop. */
 OPDEF(JSOP_TRACE,     109,"trace",      NULL,         3,  0,  0,  0,  JOF_UINT16)
 
 /* ECMA-compliant assignment ops. */
@@ -379,18 +379,18 @@ OPDEF(JSOP_CALLFCSLOT,  137,"callfcslot"
  * The function object's atom index is the second immediate operand.
  */
 OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL,       5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
 
 /* Extended jumps. */
 OPDEF(JSOP_GOTOX,         139,"gotox",    NULL,       5,  0,  0,  0,  JOF_JUMPX)
 OPDEF(JSOP_IFEQX,         140,"ifeqx",    NULL,       5,  1,  0,  4,  JOF_JUMPX|JOF_DETECTING)
 OPDEF(JSOP_IFNEX,         141,"ifnex",    NULL,       5,  1,  0,  0,  JOF_JUMPX|JOF_PARENHEAD)
-OPDEF(JSOP_ORX,           142,"orx",      NULL,       5,  1,  0,  5,  JOF_JUMPX|JOF_DETECTING)
-OPDEF(JSOP_ANDX,          143,"andx",     NULL,       5,  1,  0,  6,  JOF_JUMPX|JOF_DETECTING)
+OPDEF(JSOP_ORX,           142,"orx",      NULL,       5,  1,  1,  5,  JOF_JUMPX|JOF_DETECTING)
+OPDEF(JSOP_ANDX,          143,"andx",     NULL,       5,  1,  1,  6,  JOF_JUMPX|JOF_DETECTING)
 OPDEF(JSOP_GOSUBX,        144,"gosubx",   NULL,       5,  0,  0,  0,  JOF_JUMPX)
 OPDEF(JSOP_CASEX,         145,"casex",    NULL,       5,  2,  1,  0,  JOF_JUMPX)
 OPDEF(JSOP_DEFAULTX,      146,"defaultx", NULL,       5,  1,  0,  0,  JOF_JUMPX)
 OPDEF(JSOP_TABLESWITCHX,  147,"tableswitchx",NULL,   -1,  1,  0,  0,  JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD)
 OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL,  -1,  1,  0,  0,  JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD)
 
 /* Placeholders for a real jump opcode set during backpatch chain fixup. */
 OPDEF(JSOP_BACKPATCH,     149,"backpatch",NULL,       3,  0,  0,  0,  JOF_JUMP|JOF_BACKPATCH)
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2637,34 +2637,27 @@ ASTSerializer::expression(ParseNode *pn,
         Value expr;
 
         return letHead(pn->pn_left, dtors) &&
                expression(pn->pn_right, &expr) &&
                builder.letExpression(dtors, expr, &pn->pn_pos, dst);
       }
 
 #ifdef JS_HAS_XML_SUPPORT
+      case PNK_XMLUNARY:
+        JS_ASSERT(pn->isOp(JSOP_XMLNAME) ||
+                  pn->isOp(JSOP_SETXMLNAME) ||
+                  pn->isOp(JSOP_BINDXMLNAME));
+        return expression(pn->pn_kid, dst);
+
       case PNK_ANYNAME:
-        if (pn->isOp(JSOP_XMLNAME) ||
-            pn->isOp(JSOP_SETXMLNAME) ||
-            pn->isOp(JSOP_BINDXMLNAME))
-        {
-            return expression(pn->pn_kid, dst);
-        }
         return builder.xmlAnyName(&pn->pn_pos, dst);
 
       case PNK_DBLCOLON:
       {
-        if (pn->isOp(JSOP_XMLNAME) ||
-            pn->isOp(JSOP_SETXMLNAME) ||
-            pn->isOp(JSOP_BINDXMLNAME))
-        {
-            return expression(pn->pn_kid, dst);
-        }
-
         Value right;
 
         LOCAL_ASSERT(pn->isArity(PN_NAME) || pn->isArity(PN_BINARY));
 
         ParseNode *pnleft;
         bool computed;
 
         if (pn->isArity(PN_BINARY)) {
@@ -2685,23 +2678,16 @@ ASTSerializer::expression(ParseNode *pn,
 
         Value left;
         return expression(pnleft, &left) &&
                builder.xmlQualifiedIdentifier(left, right, computed, &pn->pn_pos, dst);
       }
 
       case PNK_AT:
       {
-        if (pn->isOp(JSOP_XMLNAME) ||
-            pn->isOp(JSOP_SETXMLNAME) ||
-            pn->isOp(JSOP_BINDXMLNAME))
-        {
-            return expression(pn->pn_kid, dst);
-        }
-
         Value expr;
         ParseNode *kid = pn->pn_kid;
         bool computed = ((!kid->isKind(PNK_NAME) || !kid->isOp(JSOP_QNAMEPART)) &&
                          !kid->isKind(PNK_DBLCOLON) &&
                          !kid->isKind(PNK_ANYNAME));
         return expression(kid, &expr) &&
             builder.xmlAttributeSelector(expr, computed, &pn->pn_pos, dst);
       }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -15417,16 +15417,26 @@ TraceRecorder::record_JSOP_TRY()
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_FINALLY()
 {
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_LABEL() {
+    return ARECORD_CONTINUE;
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_LABELX() {
+    return ARECORD_CONTINUE;
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_NOP()
 {
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK LIns*
 TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
 {
@@ -16153,26 +16163,16 @@ TraceRecorder::record_JSOP_UNBRANDTHIS()
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_SHARPINIT()
 {
     return ARECORD_STOP;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_UNUSED0() {
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_UNUSED1() {
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETGNAME()
 {
     return record_JSOP_NAME();
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_SETGNAME()
 {
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -220,17 +220,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 96)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 98)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -2437,16 +2437,17 @@ mjit::Compiler::generateMethod()
                 return Compile_Error;
           }
           END_CASE(JSOP_SETNAME)
 
           BEGIN_CASE(JSOP_THROW)
             prepareStubCall(Uses(1));
             INLINE_STUBCALL(stubs::Throw, REJOIN_NONE);
             frame.pop();
+            fallthrough = false;
           END_CASE(JSOP_THROW)
 
           BEGIN_CASE(JSOP_IN)
           {
             prepareStubCall(Uses(2));
             INLINE_STUBCALL(stubs::In, REJOIN_PUSH_BOOLEAN);
             frame.popn(2);
             frame.takeReg(Registers::ReturnReg);
@@ -2499,16 +2500,22 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_NULLBLOCKCHAIN)
           END_CASE(JSOP_NULLBLOCKCHAIN)
 
           BEGIN_CASE(JSOP_CONDSWITCH)
             /* No-op for the decompiler. */
           END_CASE(JSOP_CONDSWITCH)
 
+          BEGIN_CASE(JSOP_LABEL)
+          END_CASE(JSOP_LABEL)
+
+          BEGIN_CASE(JSOP_LABELX)
+          END_CASE(JSOP_LABELX)
+
           BEGIN_CASE(JSOP_DEFFUN)
           {
             uint32 index = fullAtomIndex(PC);
             JSFunction *innerFun = script->getFunction(index);
 
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(innerFun), Registers::ArgReg1);
             INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun), REJOIN_FALLTHROUGH);
@@ -2636,16 +2643,17 @@ mjit::Compiler::generateMethod()
             frame.storeLocal(slot, true);
             frame.pop();
             updateVarType();
           }
           END_CASE(JSOP_DEFLOCALFUN)
 
           BEGIN_CASE(JSOP_RETRVAL)
             emitReturn(NULL);
+            fallthrough = false;
           END_CASE(JSOP_RETRVAL)
 
           BEGIN_CASE(JSOP_GETGNAME)
           BEGIN_CASE(JSOP_CALLGNAME)
           {
             uint32 index = fullAtomIndex(PC);
             jsop_getgname(index);
             frame.extra(frame.peek(-1)).name = script->getAtom(index);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1861,17 +1861,18 @@ SrcNotes(JSContext *cx, JSScript *script
         offset += delta;
         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
         const char *name = js_SrcNoteSpec[type].name;
         if (type == SRC_LABEL) {
             /* Check if the source note is for a switch case. */
             if (switchTableStart <= offset && offset < switchTableEnd) {
                 name = "case";
             } else {
-                JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
+                JSOp op = js_GetOpcode(cx, script, script->code + offset);
+                JS_ASSERT(op == JSOP_LABEL || op == JSOP_LABELX);
             }
         }
         Sprint(sp, "%3u: %4u %5u [%4u] %-8s", uintN(sn - notes), lineno, offset, delta, name);
         switch (type) {
           case SRC_SETLINE:
             lineno = js_GetSrcNoteOffset(sn, 0);
             Sprint(sp, " lineno %u", lineno);
             break;
@@ -4327,52 +4328,20 @@ Help(JSContext *cx, uintN argc, jsval *v
  * they're being called for tutorial purposes.
  */
 enum its_tinyid {
     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
     ITS_CUSTOM, ITS_CUSTOMRDONLY
 };
 
 static JSBool
-its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
-{
-  jsval *val = (jsval *) JS_GetPrivate(cx, obj);
-  *vp = val ? *val : JSVAL_VOID;
-  return JS_TRUE;
-}
+its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
 static JSBool
-its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
-{
-  jsval *val = (jsval *) JS_GetPrivate(cx, obj);
-  if (val) {
-      *val = *vp;
-      return JS_TRUE;
-  }
-
-  val = new jsval;
-  if (!val) {
-      JS_ReportOutOfMemory(cx);
-      return JS_FALSE;
-  }
-
-  if (!JS_AddValueRoot(cx, val)) {
-      delete val;
-      return JS_FALSE;
-  }
-
-  if (!JS_SetPrivate(cx, obj, (void*)val)) {
-      JS_RemoveValueRoot(cx, val);
-      delete val;
-      return JS_FALSE;
-  }
-
-  *val = *vp;
-  return JS_TRUE;
-}
+its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
 
 static JSPropertySpec its_props[] = {
     {"color",           ITS_COLOR,      JSPROP_ENUMERATE,       NULL, NULL},
     {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE,       NULL, NULL},
     {"width",           ITS_WIDTH,      JSPROP_ENUMERATE,       NULL, NULL},
     {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE,       NULL, NULL},
     {"array",           ITS_ARRAY,      JSPROP_ENUMERATE,       NULL, NULL},
     {"rdonly",          ITS_RDONLY,     JSPROP_READONLY,        NULL, NULL},
@@ -4534,16 +4503,62 @@ its_finalize(JSContext *cx, JSObject *ob
 static JSClass its_class = {
     "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
     (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
     its_convert,      its_finalize,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
+static JSBool
+its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+    if (JS_GET_CLASS(cx, obj) == &its_class) {
+        jsval *val = (jsval *) JS_GetPrivate(cx, obj);
+        *vp = val ? *val : JSVAL_VOID;
+    } else {
+        *vp = JSVAL_VOID;
+    }
+
+    return JS_TRUE;
+}
+
+static JSBool
+its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
+{
+    if (JS_GET_CLASS(cx, obj) != &its_class)
+        return JS_TRUE;
+
+    jsval *val = (jsval *) JS_GetPrivate(cx, obj);
+    if (val) {
+        *val = *vp;
+        return JS_TRUE;
+    }
+
+    val = new jsval;
+    if (!val) {
+        JS_ReportOutOfMemory(cx);
+        return JS_FALSE;
+    }
+
+    if (!JS_AddValueRoot(cx, val)) {
+        delete val;
+        return JS_FALSE;
+    }
+
+    if (!JS_SetPrivate(cx, obj, (void*)val)) {
+        JS_RemoveValueRoot(cx, val);
+        delete val;
+        return JS_FALSE;
+    }
+
+    *val = *vp;
+    return JS_TRUE;
+}
+
 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
 #define MSG_DEF(name, number, count, exception, format) \
     { format, count, JSEXN_ERR } ,
 #include "jsshell.msg"
 #undef MSG_DEF
 };
 
 static const JSErrorFormatString *
new file mode 100644
--- /dev/null
+++ b/js/src/tests/e4x/extensions/assign-to-xml.js
@@ -0,0 +1,23 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+try
+{
+  eval("d, {" +
+       "  x: [{" +
+       "    x: x::x" +
+       "  }]" +
+       "} = q");
+  throw new Error("didn't throw?");
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true, "expected syntax error, got: " + e);
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/e4x/extensions/qualified-name-expr.js
@@ -0,0 +1,15 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var v;
+(function::v);
+
+function f() { }
+(function::f);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -354,16 +354,21 @@ inline void
 RegExpPrivate::incref(JSContext *cx)
 {
     ++refCount;
 }
 
 inline void
 RegExpPrivate::decref(JSContext *cx)
 {
+#ifdef JS_THREADSAFE
+    JS_OPT_ASSERT_IF(cx->runtime->gcHelperThread.getThread(),
+                     PR_GetCurrentThread() != cx->runtime->gcHelperThread.getThread());
+#endif
+
     if (--refCount != 0)
         return;
 
     RegExpPrivateCache *cache;
     if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
         RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
         if (ptr && ptr->value == this)
             cache->remove(ptr);
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -445,19 +445,19 @@ class JSRope : public JSString
 
 JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
 
 class JSLinearString : public JSString
 {
     friend class JSString;
 
     /* Vacuous and therefore unimplemented. */
-    JSLinearString *ensureLinear(JSContext *cx);
-    bool isLinear() const;
-    JSLinearString &asLinear() const;
+    JSLinearString *ensureLinear(JSContext *cx) MOZ_DELETE;
+    bool isLinear() const MOZ_DELETE;
+    JSLinearString &asLinear() const MOZ_DELETE;
 
   public:
     void mark(JSTracer *trc);
 
     JS_ALWAYS_INLINE
     const jschar *chars() const {
         JS_ASSERT(JSString::isLinear());
         return d.u1.chars;
@@ -469,18 +469,18 @@ JS_STATIC_ASSERT(sizeof(JSLinearString) 
 class JSDependentString : public JSLinearString
 {
     friend class JSString;
     JSFixedString *undepend(JSContext *cx);
 
     void init(JSLinearString *base, const jschar *chars, size_t length);
 
     /* Vacuous and therefore unimplemented. */
-    bool isDependent() const;
-    JSDependentString &asDependent() const;
+    bool isDependent() const MOZ_DELETE;
+    JSDependentString &asDependent() const MOZ_DELETE;
 
   public:
     static inline JSDependentString *new_(JSContext *cx, JSLinearString *base,
                                           const jschar *chars, size_t length);
 
     JSLinearString *base() const {
         JS_ASSERT(JSString::isDependent());
         return d.s.u2.base;
@@ -493,19 +493,19 @@ class JSFlatString : public JSLinearStri
 {
     friend class JSRope;
     void morphExtensibleIntoDependent(JSLinearString *base) {
         d.lengthAndFlags = buildLengthAndFlags(length(), DEPENDENT_BIT);
         d.s.u2.base = base;
     }
 
     /* Vacuous and therefore unimplemented. */
-    JSFlatString *ensureFlat(JSContext *cx);
-    bool isFlat() const;
-    JSFlatString &asFlat() const;
+    JSFlatString *ensureFlat(JSContext *cx) MOZ_DELETE;
+    bool isFlat() const MOZ_DELETE;
+    JSFlatString &asFlat() const MOZ_DELETE;
 
   public:
     JS_ALWAYS_INLINE
     const jschar *charsZ() const {
         JS_ASSERT(JSString::isFlat());
         return chars();
     }
 
@@ -529,37 +529,37 @@ class JSFlatString : public JSLinearStri
     inline void finalize(JSRuntime *rt);
 };
 
 JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
 
 class JSExtensibleString : public JSFlatString
 {
     /* Vacuous and therefore unimplemented. */
-    bool isExtensible() const;
-    JSExtensibleString &asExtensible() const;
+    bool isExtensible() const MOZ_DELETE;
+    JSExtensibleString &asExtensible() const MOZ_DELETE;
 
   public:
     JS_ALWAYS_INLINE
     size_t capacity() const {
         JS_ASSERT(JSString::isExtensible());
         return d.s.u2.capacity;
     }
 };
 
 JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
 
 class JSFixedString : public JSFlatString
 {
     void init(const jschar *chars, size_t length);
 
     /* Vacuous and therefore unimplemented. */
-    JSFlatString *ensureFixed(JSContext *cx);
-    bool isFixed() const;
-    JSFixedString &asFixed() const;
+    JSFlatString *ensureFixed(JSContext *cx) MOZ_DELETE;
+    bool isFixed() const MOZ_DELETE;
+    JSFixedString &asFixed() const MOZ_DELETE;
 
   public:
     static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
 
     /*
      * Once a JSFixedString has been added to the atom state, this operation
      * changes the type (in place, as reflected by the flag bits) of the
      * JSFixedString into a JSAtom.
@@ -634,18 +634,18 @@ class JSExternalString : public JSFixedS
 {
     static void staticAsserts() {
         JS_STATIC_ASSERT(TYPE_LIMIT == 8);
     }
 
     void init(const jschar *chars, size_t length, intN type, void *closure);
 
     /* Vacuous and therefore unimplemented. */
-    bool isExternal() const;
-    JSExternalString &asExternal() const;
+    bool isExternal() const MOZ_DELETE;
+    JSExternalString &asExternal() const MOZ_DELETE;
 
   public:
     static inline JSExternalString *new_(JSContext *cx, const jschar *chars,
                                          size_t length, intN type, void *closure);
 
     intN externalType() const {
         JS_ASSERT(JSString::isExternal());
         JS_ASSERT(d.s.u2.externalType < TYPE_LIMIT);
@@ -677,18 +677,18 @@ class JSExternalString : public JSFixedS
     void finalize();
 };
 
 JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
 
 class JSAtom : public JSFixedString
 {
     /* Vacuous and therefore unimplemented. */
-    bool isAtom() const;
-    JSAtom &asAtom() const;
+    bool isAtom() const MOZ_DELETE;
+    JSAtom &asAtom() const MOZ_DELETE;
 
   public:
     /* Returns the PropertyName for this.  isIndex() must be false. */
     inline js::PropertyName *asPropertyName();
 
     inline void finalize(JSRuntime *rt);
 };
 
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -387,17 +387,17 @@ interface nsIXPCFunctionThisTranslator :
 %{ C++
 // For use with the service manager
 // {CB6593E0-F9B2-11d2-BDD6-000064657374}
 #define NS_XPCONNECT_CID \
 { 0xcb6593e0, 0xf9b2, 0x11d2, \
     { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
 %}
 
-[uuid(a995b541-d514-43f1-ac0e-f49746c0b063)]
+[uuid(29b63029-0868-4344-b0ca-d93256ee7c78)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Initializes classes on a global object that has already been created.
@@ -790,10 +790,11 @@ interface nsIXPConnect : nsISupports
 
     /**
      * When we place the browser in JS debug mode, there can't be any
      * JS on the stack. This is because we currently activate debugMode 
      * on all scripts in the JSRuntime when the debugger is activated.
      * This method will turn debug mode on or off when the context 
      * stack reaches zero length.
      */
-    [noscript] void setDebugModeWhenPossible(in boolean mode);
+    [noscript] void setDebugModeWhenPossible(in boolean mode,
+                                             in boolean allowSyncDisable);
 };
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -43,16 +43,19 @@
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG
 #endif
 
 #include <stdarg.h>
 
 #include "prlog.h"
+#ifdef ANDROID
+#include <android/log.h>
+#endif
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsICategoryManager.h"
 #include "nsIComponentManager.h"
 #include "mozilla/Module.h"
 #include "nsILocalFile.h"
 #include "nsIServiceManager.h"
@@ -199,17 +202,21 @@ Dump(JSContext *cx, uintN argc, jsval *v
     if (!str)
         return JS_FALSE;
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return JS_FALSE;
 
-    fputs(NS_ConvertUTF16toUTF8(reinterpret_cast<const PRUnichar*>(chars)).get(), stdout);
+    NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast<const PRUnichar*>(chars));
+#ifdef ANDROID
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", utf8str.get());
+#endif
+    fputs(utf8str.get(), stdout);
     fflush(stdout);
     return JS_TRUE;
 }
 
 static JSBool
 Debug(JSContext *cx, uintN argc, jsval *vp)
 {
 #ifdef DEBUG
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -85,16 +85,20 @@
 #include "xpcpublic.h"
 #ifdef XP_MACOSX
 #include "xpcshellMacUtils.h"
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 
 // all this crap is needed to do the interactive shell stuff
 #include <stdlib.h>
 #include <errno.h>
 #ifdef HAVE_IO_H
 #include <io.h>     /* for isatty() */
@@ -450,16 +454,19 @@ Dump(JSContext *cx, uintN argc, jsval *v
     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
     if (!str)
         return JS_FALSE;
 
     JSAutoByteString bytes(cx, str);
     if (!bytes)
         return JS_FALSE;
 
+#ifdef ANDROID
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", bytes.ptr());
+#endif
     fputs(bytes.ptr(), gOutFile);
     fflush(gOutFile);
     return JS_TRUE;
 }
 
 static JSBool
 Load(JSContext *cx, uintN argc, jsval *vp)
 {
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -133,23 +133,25 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrapped
         // for XPCWrappedNatives, described in a larger comment below and also
         // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
 
         JSObject *obj = tmp->GetFlatJSObjectPreserveColor();
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
         cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
     }
 
-    XPCJSRuntime *rt = tmp->GetRuntime();
-    TraverseExpandoObjectClosure closure = {
-         rt->GetXPConnect()->GetCycleCollectionContext()->GetJSContext(),
-         tmp,
-         cb
-    };
-    rt->GetCompartmentMap().EnumerateRead(TraverseExpandoObjects, &closure);
+    if (tmp->MightHaveExpandoObject()) {
+        XPCJSRuntime *rt = tmp->GetRuntime();
+        TraverseExpandoObjectClosure closure = {
+             rt->GetXPConnect()->GetCycleCollectionContext()->GetJSContext(),
+             tmp,
+             cb
+        };
+        rt->GetCompartmentMap().EnumerateRead(TraverseExpandoObjects, &closure);
+    }
 
     // XPCWrappedNative keeps its native object alive.
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
     cb.NoteXPCOMChild(tmp->GetIdentityObject());
 
     tmp->NoteTearoffs(cb);
 
     return NS_OK;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -2857,20 +2857,20 @@ nsXPConnect::Base64Decode(JSContext *cx,
     if (!str)
         return JS_FALSE;
 
     *out = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
 
 NS_IMETHODIMP
-nsXPConnect::SetDebugModeWhenPossible(bool mode)
+nsXPConnect::SetDebugModeWhenPossible(bool mode, bool allowSyncDisable)
 {
     gDesiredDebugMode = mode;
-    if (!mode)
+    if (!mode && allowSyncDisable)
         CheckForDebugMode(mRuntime->GetJSRuntime());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::GetTelemetryValue(JSContext *cx, jsval *rval)
 {
     JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -272,19 +272,19 @@ class PtrAndPrincipalHashKey : public PL
 
     static const PtrAndPrincipalHashKey*
     KeyToPointer(PtrAndPrincipalHashKey* aKey) { return aKey; }
     static PLDHashNumber HashKey(const PtrAndPrincipalHashKey* aKey)
     {
         return aKey->mSavedHash;
     }
 
-    nsISupports* GetPtr()
-    {
-        return mPtr;
+    nsISupports* GetPtr()
+    {
+        return mPtr;
     }
 
     enum { ALLOW_MEMMOVE = true };
 
   protected:
     nsCOMPtr<nsISupports> mPtr;
     nsCOMPtr<nsIPrincipal> mPrincipal;
 
@@ -2739,37 +2739,35 @@ public:
                                                 XPCNativeScriptableCreateInfo& sciProto);
 
     JSBool HasExternalReference() const {return mRefCnt > 1;}
 
     JSBool NeedsSOW() { return !!(mWrapperWord & NEEDS_SOW); }
     void SetNeedsSOW() { mWrapperWord |= NEEDS_SOW; }
     JSBool NeedsCOW() { return !!(mWrapperWord & NEEDS_COW); }
     void SetNeedsCOW() { mWrapperWord |= NEEDS_COW; }
+    JSBool MightHaveExpandoObject() { return !!(mWrapperWord & MIGHT_HAVE_EXPANDO); }
+    void SetHasExpandoObject() { mWrapperWord |= MIGHT_HAVE_EXPANDO; }
 
     JSObject* GetWrapperPreserveColor() const
         {return (JSObject*)(mWrapperWord & (size_t)~(size_t)FLAG_MASK);}
 
     JSObject* GetWrapper()
     {
         JSObject* wrapper = GetWrapperPreserveColor();
         if (wrapper) {
             xpc_UnmarkGrayObject(wrapper);
             // Call this to unmark mFlatJSObject.
             GetFlatJSObject();
         }
         return wrapper;
     }
     void SetWrapper(JSObject *obj)
     {
-        PRWord needsSOW = NeedsSOW() ? NEEDS_SOW : 0;
-        PRWord needsCOW = NeedsCOW() ? NEEDS_COW : 0;
-        mWrapperWord = PRWord(obj) |
-                         needsSOW |
-                         needsCOW;
+        mWrapperWord = PRWord(obj) | (mWrapperWord & FLAG_MASK);
     }
 
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     QITableEntry* GetOffsets()
     {
         if (!HasProto() || !GetProto()->ClassIsDOMObject())
             return nsnull;
@@ -2798,20 +2796,18 @@ protected:
 
     virtual ~XPCWrappedNative();
     void Destroy();
 
 private:
     enum {
         NEEDS_SOW = JS_BIT(0),
         NEEDS_COW = JS_BIT(1),
-
-        LAST_FLAG = NEEDS_COW,
-
-        FLAG_MASK = 0x7
+        MIGHT_HAVE_EXPANDO = JS_BIT(2),
+        FLAG_MASK = JS_BITMASK(3)
     };
 
 private:
 
     JSBool Init(XPCCallContext& ccx, JSObject* parent, JSBool isGlobal,
                 const XPCNativeScriptableCreateInfo* sci);
     JSBool Init(XPCCallContext &ccx, JSObject *existingJSObject);
     JSBool FinishInit(XPCCallContext &ccx);
@@ -4374,17 +4370,17 @@ xpc_GetJSPrivate(JSObject *obj)
 // do setup etc on, puts the sandbox object in *vp (which must be
 // rooted by the caller), and uses the principal that's either
 // directly passed in prinOrSop or indirectly as an
 // nsIScriptObjectPrincipal holding the principal. If no principal is
 // reachable through prinOrSop, a new null principal will be created
 // and used.
 nsresult
 xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop,
-                        JSObject *proto, bool preferXray, const nsACString &sandboxName,
+                        JSObject *proto, bool preferXray, const nsACString &sandboxName,
                         nsISupports *identityPtr = nsnull);
 // Helper for evaluating scripts in a sandbox object created with
 // xpc_CreateSandboxObject(). The caller is responsible of ensuring
 // that *rval doesn't get collected during the call or usage after the
 // call. This helper will use filename and lineNo for error reporting,
 // and if no filename is provided it will use the codebase from the
 // principal and line number 1 as a fallback. if returnStringOnly is
 // true, then the result in *rval, or the exception in cx->exception
@@ -4462,16 +4458,17 @@ struct CompartmentPrivate
     bool RegisterExpandoObject(XPCWrappedNative *wn, JSObject *expando) {
         if (!expandoMap) {
             expandoMap = new nsDataHashtable<nsPtrHashKey<XPCWrappedNative>, JSObject *>();
             if (!expandoMap->Init(8)) {
                 expandoMap = nsnull;
                 return false;
             }
         }
+        wn->SetHasExpandoObject();
         return expandoMap->Put(wn, expando);
     }
 
     /**
      * This lookup does not change the color of the JSObject meaning that the
      * object returned is not guaranteed to be kept alive past the next CC.
      *
      * This should only be called if you are certain that the return value won't
--- a/layout/base/nsImageLoader.cpp
+++ b/layout/base/nsImageLoader.cpp
@@ -52,37 +52,35 @@
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 
 #include "imgIContainer.h"
 
 #include "nsStyleContext.h"
 #include "nsGkAtoms.h"
+#include "nsLayoutUtils.h"
 
 // Paint forcing
 #include "prenv.h"
 
 NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver)
 
 nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
                              nsImageLoader *aNextLoader)
   : mFrame(aFrame),
     mActions(aActions),
-    mNextLoader(aNextLoader)
+    mNextLoader(aNextLoader),
+    mRequestRegistered(false)
 {
 }
 
 nsImageLoader::~nsImageLoader()
 {
-  mFrame = nsnull;
-
-  if (mRequest) {
-    mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
-  }
+  Destroy();
 }
 
 /* static */ already_AddRefed<nsImageLoader>
 nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest, 
                       PRUint32 aActions, nsImageLoader *aNextLoader)
 {
   nsRefPtr<nsImageLoader> loader =
     new nsImageLoader(aFrame, aActions, aNextLoader);
@@ -100,49 +98,61 @@ nsImageLoader::Destroy()
   mNextLoader = nsnull;
   while (list) {
     nsRefPtr<nsImageLoader> todestroy = list;
     list = todestroy->mNextLoader;
     todestroy->mNextLoader = nsnull;
     todestroy->Destroy();
   }
 
-  mFrame = nsnull;
-
-  if (mRequest) {
+  if (mRequest && mFrame) {
+    nsLayoutUtils::DeregisterImageRequest(mFrame->PresContext(), mRequest,
+                                          &mRequestRegistered);
     mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
   }
 
+  mFrame = nsnull;
   mRequest = nsnull;
 }
 
 nsresult
 nsImageLoader::Load(imgIRequest *aImage)
 {
   NS_ASSERTION(!mRequest, "can't reuse image loaders");
   NS_ASSERTION(mFrame, "not initialized");
   NS_ASSERTION(aImage, "must have non-null image");
 
   if (!mFrame)
     return NS_ERROR_NOT_INITIALIZED;
 
   if (!aImage)
     return NS_ERROR_FAILURE;
 
+  // Deregister mRequest from the refresh driver, since it is no longer
+  // going to be managed by this nsImageLoader.
+  nsPresContext* presContext = mFrame->PresContext();
+
+  nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
+                                        &mRequestRegistered);
+
   // Make sure to clone into a temporary, then set mRequest, since
   // cloning may notify and we don't want to trigger paints from this
   // code.
   nsCOMPtr<imgIRequest> newRequest;
   nsresult rv = aImage->Clone(this, getter_AddRefs(newRequest));
   mRequest.swap(newRequest);
+
+  if (mRequest) {
+    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mRequest,
+                                                  &mRequestRegistered);
+  }
+
   return rv;
 }
 
-                    
-
 NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
                                               imgIContainer *aImage)
 {
   NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
 
   /* Get requested animation policy from the pres context:
    *   normal = 0
    *   one frame = 1
@@ -169,16 +179,25 @@ NS_IMETHODIMP nsImageLoader::OnStopFrame
     DoReflow();
   }
   if (mActions & ACTION_REDRAW_ON_DECODE) {
     DoRedraw(nsnull);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP nsImageLoader::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  // Register with the refresh driver now that we are aware that
+  // we are animated.
+  nsLayoutUtils::RegisterImageRequest(mFrame->PresContext(),
+                                      aRequest, &mRequestRegistered);
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
                                            bool aLastPart)
 {
   if (!mFrame)
     return NS_ERROR_FAILURE;
 
   if (!mRequest) {
     // We're in the middle of a paint anyway
--- a/layout/base/nsImageLoader.h
+++ b/layout/base/nsImageLoader.h
@@ -79,16 +79,17 @@ public:
            PRUint32 aActions, nsImageLoader *aNextLoader);
 
   NS_DECL_ISUPPORTS
 
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
   NS_IMETHOD OnStopRequest(imgIRequest *aRequest, bool aLastPart);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
   // Do not override OnDataAvailable since background images are not
   // displayed incrementally; they are displayed after the entire image
   // has been loaded.
   // Note: Images referenced by the <img> element are displayed
   // incrementally in nsImageFrame.cpp.
 
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
@@ -104,9 +105,13 @@ private:
   void DoReflow();
   /* if aDamageRect is nsnull, the whole frame is redrawn. */
   void DoRedraw(const nsRect* aDamageRect);
 
   nsIFrame *mFrame;
   nsCOMPtr<imgIRequest> mRequest;
   PRUint32 mActions;
   nsRefPtr<nsImageLoader> mNextLoader;
+
+  // This is a boolean flag indicating whether or not the current image request
+  // has been registered with the refresh driver.
+  bool mRequestRegistered;
 };
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4322,16 +4322,112 @@ void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = NULL;
   }
 }
 
+/* static */
+void
+nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
+                                    imgIRequest* aRequest,
+                                    bool* aRequestRegistered)
+{
+  if (!aPresContext) {
+    return;
+  }
+
+  if (aRequestRegistered && *aRequestRegistered) {
+    // Our request is already registered with the refresh driver, so
+    // no need to register it again.
+    return;
+  }
+
+  if (aRequest) {
+    if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
+      NS_WARNING("Unable to add image request");
+      return;
+    }
+
+    if (aRequestRegistered) {
+      *aRequestRegistered = true;
+    }
+  }
+}
+
+/* static */
+void
+nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
+                                              imgIRequest* aRequest,
+                                              bool* aRequestRegistered)
+{
+  if (!aPresContext) {
+    return;
+  }
+
+  if (aRequestRegistered && *aRequestRegistered) {
+    // Our request is already registered with the refresh driver, so
+    // no need to register it again.
+    return;
+  }
+
+  if (aRequest) {
+    nsCOMPtr<imgIContainer> image;
+    aRequest->GetImage(getter_AddRefs(image));
+    if (image) {
+
+      // Check to verify that the image is animated. If so, then add it to the
+      // list of images tracked by the refresh driver.
+      bool isAnimated = false;
+      nsresult rv = image->GetAnimated(&isAnimated);
+      if (NS_SUCCEEDED(rv) && isAnimated) {
+        if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
+          NS_WARNING("Unable to add image request");
+          return;
+        }
+
+        if (aRequestRegistered) {
+          *aRequestRegistered = true;
+        }
+      }
+    }
+  }
+}
+
+/* static */
+void
+nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
+                                      imgIRequest* aRequest,
+                                      bool* aRequestRegistered)
+{
+  if (!aPresContext) {
+    return;
+  }
+
+  // Deregister our imgIRequest with the refresh driver to
+  // complete tear-down, but only if it has been registered
+  if (aRequestRegistered && !*aRequestRegistered) {
+    return;
+  }
+
+  if (aRequest) {
+    nsCOMPtr<imgIContainer> image;
+    aRequest->GetImage(getter_AddRefs(image));
+    if (image) {
+      aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
+
+      if (aRequestRegistered) {
+        *aRequestRegistered = false;
+      }
+    }
+  }
+}
+
 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
                                      const nsAString& aValue)
   : mContent(aContent),
     mAttrName(aAttrName),
     mValue(aValue)
 {
   NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1449,16 +1449,74 @@ public:
 
   /**
    * Checks if CSS 3D transforms are currently enabled.
    */
   static bool Are3DTransformsEnabled();
 
   static void Shutdown();
 
+  /**
+   * Register an imgIRequest object with a refresh driver.
+   *
+   * @param aPresContext The nsPresContext whose refresh driver we want to
+   *        register with.
+   * @param aRequest A pointer to the imgIRequest object which the client wants
+   *        to register with the refresh driver.
+   * @param aRequestRegistered A pointer to a boolean value which indicates
+   *        whether the given image request is registered. If
+   *        *aRequestRegistered is true, then this request will not be
+   *        registered again. If the request is registered by this function,
+   *        then *aRequestRegistered will be set to true upon the completion of
+   *        this function.
+   *
+   */
+  static void RegisterImageRequest(nsPresContext* aPresContext,
+                                   imgIRequest* aRequest,
+                                   bool* aRequestRegistered);
+
+  /**
+   * Register an imgIRequest object with a refresh driver, but only if the
+   * request is for an image that is animated.
+   *
+   * @param aPresContext The nsPresContext whose refresh driver we want to
+   *        register with.
+   * @param aRequest A pointer to the imgIRequest object which the client wants
+   *        to register with the refresh driver.
+   * @param aRequestRegistered A pointer to a boolean value which indicates
+   *        whether the given image request is registered. If
+   *        *aRequestRegistered is true, then this request will not be
+   *        registered again. If the request is registered by this function,
+   *        then *aRequestRegistered will be set to true upon the completion of
+   *        this function.
+   *
+   */
+  static void RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
+                                             imgIRequest* aRequest,
+                                             bool* aRequestRegistered);
+
+  /**
+   * Deregister an imgIRequest object from a refresh driver.
+   *
+   * @param aPresContext The nsPresContext whose refresh driver we want to
+   *        deregister from.
+   * @param aRequest A pointer to the imgIRequest object with which the client
+   *        previously registered and now wants to deregister from the refresh
+   *        driver.
+   * @param aRequestRegistered A pointer to a boolean value which indicates
+   *        whether the given image request is registered. If
+   *        *aRequestRegistered is false, then this request will not be
+   *        deregistered. If the request is deregistered by this function,
+   *        then *aRequestRegistered will be set to false upon the completion of
+   *        this function.
+   */
+  static void DeregisterImageRequest(nsPresContext* aPresContext,
+                                     imgIRequest* aRequest,
+                                     bool* aRequestRegistered);
+
 #ifdef DEBUG
   /**
    * Assert that there are no duplicate continuations of the same frame
    * within aFrameList.  Optimize the tests by assuming that all frames
    * in aFrameList have parent aContainer.
    */
   static void
   AssertNoDuplicateContinuations(nsIFrame* aContainer,
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -109,16 +109,17 @@ nsRefreshDriver::GetRefreshTimerType() c
 nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
   : mPresContext(aPresContext),
     mFrozen(false),
     mThrottled(false),
     mTestControllingRefreshes(false),
     mTimerIsPrecise(false),
     mLastTimerInterval(0)
 {
+  mRequests.Init();
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
                     "observers should have unregistered");
   NS_ABORT_IF_FALSE(!mTimer, "timer should be gone");
 }
@@ -180,16 +181,39 @@ nsRefreshDriver::AddRefreshObserver(nsAR
 bool
 nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver *aObserver,
                                        mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   return array.RemoveElement(aObserver);
 }
 
+bool
+nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
+{
+  if (!mRequests.PutEntry(aRequest)) {
+    return false;
+  }
+
+  EnsureTimerStarted(false);
+
+  return true;
+}
+
+void
+nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
+{
+  mRequests.RemoveEntry(aRequest);
+}
+
+void nsRefreshDriver::ClearAllImageRequests()
+{
+  mRequests.Clear();
+}
+
 void
 nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
 {
   if (mTimer || mFrozen || !mPresContext) {
     // It's already been started, or we don't want to start it now or
     // we've been disconnected.
     return;
   }
@@ -233,27 +257,34 @@ nsRefreshDriver::StopTimer()
 
 PRUint32
 nsRefreshDriver::ObserverCount() const
 {
   PRUint32 sum = 0;
   for (PRUint32 i = 0; i < ArrayLength(mObservers); ++i) {
     sum += mObservers[i].Length();
   }
+
   // Even while throttled, we need to process layout and style changes.  Style
   // changes can trigger transitions which fire events when they complete, and
   // layout changes can affect media queries on child documents, triggering
   // style changes, etc.
   sum += mStyleFlushObservers.Length();
   sum += mLayoutFlushObservers.Length();
   sum += mBeforePaintTargets.Length();
   sum += mAnimationFrameListenerDocs.Length();
   return sum;
 }
 
+PRUint32
+nsRefreshDriver::ImageRequestCount() const
+{
+  return mRequests.Count();
+}
+
 void
 nsRefreshDriver::UpdateMostRecentRefresh()
 {
   if (mTestControllingRefreshes) {
     return;
   }
 
   // Call JS_Now first, since that can have nonzero latency in some rare cases.
@@ -298,17 +329,17 @@ nsRefreshDriver::Notify(nsITimer *aTimer
   if (mTestControllingRefreshes && aTimer) {
     // Ignore real refreshes from our timer (but honor the others).
     return NS_OK;
   }
 
   UpdateMostRecentRefresh();
 
   nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
-  if (!presShell || ObserverCount() == 0) {
+  if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
     // Things are being destroyed, or we no longer have any observers.
     // We don't want to stop the timer when observers are initially
     // removed, because sometimes observers can be added and removed
     // often depending on what other things are going on and in that
     // situation we don't want to thrash our timer.  So instead we
     // wait until we get a Notify() call when we have no observers
     // before stopping the timer.
     StopTimer();
@@ -327,16 +358,17 @@ nsRefreshDriver::Notify(nsITimer *aTimer
       nsRefPtr<nsARefreshObserver> obs = etor.GetNext();
       obs->WillRefresh(mMostRecentRefresh);
       
       if (!mPresContext || !mPresContext->GetPresShell()) {
         StopTimer();
         return NS_OK;
       }
     }
+
     if (i == 0) {
       // Don't just loop while we have things in mBeforePaintTargets,
       // the whole point is that event handlers should readd the
       // target as needed.
       nsTArray< nsCOMPtr<nsIDocument> > targets;
       targets.SwapElements(mBeforePaintTargets);
       for (PRUint32 i = 0; i < targets.Length(); ++i) {
         targets[i]->BeforePaintEventFiring();
@@ -399,16 +431,27 @@ nsRefreshDriver::Notify(nsITimer *aTimer
           shell->mSuppressInterruptibleReflows = false;
           shell->FlushPendingNotifications(Flush_InterruptibleLayout);
           NS_RELEASE(shell);
         }
       }
     }
   }
 
+  /*
+   * Perform notification to imgIRequests subscribed to listen
+   * for refresh events.
+   */
+
+  ImageRequestParameters parms = {mMostRecentRefresh};
+  if (mRequests.Count()) {
+    mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms);
+    EnsureTimerStarted(false);
+  }
+
   if (mThrottled ||
       (mTimerIsPrecise !=
        (GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {
     // Stop the timer now and restart it here.  Stopping is in the mThrottled
     // case ok because either it's already one-shot, and it just fired, and all
     // we need to do is null it out, or it's repeating and we need to reset it
     // to be one-shot.  Stopping and restarting in the case when we need to
     // switch from precise to slack timers or vice versa is unfortunately
@@ -419,30 +462,48 @@ nsRefreshDriver::Notify(nsITimer *aTimer
     // started.
     StopTimer();
     EnsureTimerStarted(true);
   }
 
   return NS_OK;
 }
 
+PLDHashOperator
+nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry,
+                                        void* aUserArg)
+{
+  ImageRequestParameters* parms =
+    static_cast<ImageRequestParameters*> (aUserArg);
+  mozilla::TimeStamp mostRecentRefresh = parms->ts;
+  imgIRequest* req = static_cast<imgIRequest*>(aEntry->GetKey());
+  NS_ABORT_IF_FALSE(req, "Unable to retrieve the image request");
+  nsCOMPtr<imgIContainer> image;
+  req->GetImage(getter_AddRefs(image));
+  if (image) {
+    image->RequestRefresh(mostRecentRefresh);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
 void
 nsRefreshDriver::Freeze()
 {
   NS_ASSERTION(!mFrozen, "Freeze called on already-frozen refresh driver");
   StopTimer();
   mFrozen = true;
 }
 
 void
 nsRefreshDriver::Thaw()
 {
   NS_ASSERTION(mFrozen, "Thaw called on an unfrozen refresh driver");
   mFrozen = false;
-  if (ObserverCount()) {
+  if (ObserverCount() || ImageRequestCount()) {
     // FIXME: This isn't quite right, since our EnsureTimerStarted call
     // updates our mMostRecentRefresh, but the DoRefresh call won't run
     // and notify our observers until we get back to the event loop.
     // Thus MostRecentRefresh() will lie between now and the DoRefresh.
     NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsRefreshDriver::DoRefresh));
     EnsureTimerStarted(false);
   }
 }
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -45,20 +45,23 @@
 
 #include "mozilla/TimeStamp.h"
 #include "mozFlushType.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsTObserverArray.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
 
 class nsPresContext;
 class nsIPresShell;
 class nsIDocument;
+class imgIRequest;
 
 /**
  * An abstract base class to be implemented by callers wanting to be
  * notified at refresh times.  When nothing needs to be painted, callers
  * may not be notified.
  */
 class nsARefreshObserver {
 public:
@@ -124,16 +127,34 @@ public:
    * they must remove themselves before they are destroyed.
    */
   bool AddRefreshObserver(nsARefreshObserver *aObserver,
                             mozFlushType aFlushType);
   bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
                                mozFlushType aFlushType);
 
   /**
+   * Add/Remove imgIRequest versions of observers.
+   *
+   * These are used for hooking into the refresh driver for
+   * controlling animated images.
+   *
+   * @note The refresh driver owns a reference to these listeners.
+   *
+   * @note Technically, imgIRequest objects are not nsARefreshObservers, but
+   * for controlling animated image repaint events, we subscribe the
+   * imgIRequests to the nsRefreshDriver for notification of paint events.
+   *
+   * @returns whether the operation succeeded, or void in the case of removal.
+   */
+  bool AddImageRequest(imgIRequest* aRequest);
+  void RemoveImageRequest(imgIRequest* aRequest);
+  void ClearAllImageRequests();
+
+  /**
    * Add / remove presshells that we should flush style and layout on
    */
   bool AddStyleFlushObserver(nsIPresShell* aShell) {
     NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
 		 "Double-adding style flush observer");
     bool appended = mStyleFlushObservers.AppendElement(aShell) != nsnull;
     EnsureTimerStarted(false);
     return appended;
@@ -213,20 +234,25 @@ public:
    * Check whether the given observer is an observer for the given flush type
    */
   bool IsRefreshObserver(nsARefreshObserver *aObserver,
 			   mozFlushType aFlushType);
 #endif
 
 private:
   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
+  typedef nsTHashtable<nsISupportsHashKey> RequestTable;
 
   void EnsureTimerStarted(bool aAdjustingTimer);
   void StopTimer();
+
   PRUint32 ObserverCount() const;
+  PRUint32 ImageRequestCount() const;
+  static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry,
+                                          void* aUserArg);
   void UpdateMostRecentRefresh();
   ObserverArray& ArrayFor(mozFlushType aFlushType);
   // Trigger a refresh immediately, if haven't been disconnected or frozen.
   void DoRefresh();
 
   PRInt32 GetRefreshTimerInterval() const;
   PRInt32 GetRefreshTimerType() const;
 
@@ -247,21 +273,28 @@ private:
   bool mTestControllingRefreshes;
   /* If mTimer is non-null, this boolean indicates whether the timer is
      a precise timer.  If mTimer is null, this boolean's value can be
      anything.  */
   bool mTimerIsPrecise;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
+  RequestTable mRequests;
+
   nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
   nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
   // nsTArray on purpose, because we want to be able to swap.
   nsTArray< nsCOMPtr<nsIDocument> > mBeforePaintTargets;
   // nsTArray on purpose, because we want to be able to swap.
   nsTArray<nsIDocument*> mAnimationFrameListenerDocs;
 
   // This is the last interval we used for our timer.  May be 0 if we
   // haven't computed a timer interval yet.
   mutable PRInt32 mLastTimerInterval;
+
+  // Helper struct for processing image requests
+  struct ImageRequestParameters {
+      mozilla::TimeStamp ts;
+  };
 };
 
 #endif /* !defined(nsRefreshDriver_h_) */
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -75,16 +75,18 @@ public:
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
                              const nsIntRect *aRect);
   NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
                           const PRUnichar *statusArg);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
+
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *dirtyRect);
 
   void SetFrame(nsBulletFrame *frame) { mFrame = frame; }
 
 private:
   nsBulletFrame *mFrame;
@@ -96,16 +98,20 @@ nsBulletFrame::~nsBulletFrame()
 {
 }
 
 void
 nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   // Stop image loading first
   if (mImageRequest) {
+    // Deregister our image request from the refresh driver
+    nsLayoutUtils::DeregisterImageRequest(PresContext(),
+                                          mImageRequest,
+                                          &mRequestRegistered);
     mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
     mImageRequest = nsnull;
   }
 
   if (mListener)
     reinterpret_cast<nsBulletListener*>(mListener.get())->SetFrame(nsnull);
 
   // Let base class do the rest
@@ -165,28 +171,38 @@ nsBulletFrame::DidSetStyleContext(nsStyl
       nsCOMPtr<nsIURI> newURI;
       newRequest->GetURI(getter_AddRefs(newURI));
       if (oldURI && newURI) {
         bool same;
         newURI->Equals(oldURI, &same);
         if (same) {
           needNewRequest = false;
         } else {
+          nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
+                                                &mRequestRegistered);
           mImageRequest->Cancel(NS_ERROR_FAILURE);
           mImageRequest = nsnull;
         }
       }
     }
 
     if (needNewRequest) {
       newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
+      if (mImageRequest) {
+        nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
+                                                      mImageRequest,
+                                                      &mRequestRegistered);
+      }
     }
   } else {
     // No image request on the new style context
     if (mImageRequest) {
+      nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
+                                            &mRequestRegistered);
+
       mImageRequest->Cancel(NS_ERROR_FAILURE);
       mImageRequest = nsnull;
     }
   }
 
 #ifdef ACCESSIBILITY
   // Update the list bullet accessible. If old style list isn't available then
   // no need to update the accessible tree because it's not created yet.
@@ -1535,16 +1551,28 @@ NS_IMETHODIMP nsBulletFrame::OnStopDecod
       imageFailed = true;
     }
   }
 #endif
 
   return NS_OK;
 }
 
+NS_IMETHODIMP nsBulletFrame::OnImageIsAnimated(imgIRequest* aRequest)
+{
+  // Register the image request with the refresh driver now that we know it's
+  // animated.
+  if (aRequest == mImageRequest) {
+    nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
+                                        &mRequestRegistered);
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsBulletFrame::FrameChanged(imgIContainer *aContainer,
                                           const nsIntRect *aDirtyRect)
 {
   // Invalidate the entire content area. Maybe it's not optimal but it's simple and
   // always correct.
   Invalidate(nsRect(0, 0, mRect.width, mRect.height));
 
   return NS_OK;
@@ -1629,31 +1657,39 @@ NS_IMETHODIMP nsBulletListener::OnStartC
   return mFrame->OnStartContainer(aRequest, aImage);
 }
 
 NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest,
                                                 bool aCurrentFrame,
                                                 const nsIntRect *aRect)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
 
   return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect);
 }
 
 NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest,
                                              nsresult status,
                                              const PRUnichar *statusArg)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
   
   return mFrame->OnStopDecode(aRequest, status, statusArg);
 }
 
+NS_IMETHODIMP nsBulletListener::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  if (!mFrame)
+    return NS_OK;
+
+  return mFrame->OnImageIsAnimated(aRequest);
+}
+
 NS_IMETHODIMP nsBulletListener::FrameChanged(imgIContainer *aContainer,
                                              const nsIntRect *aDirtyRect)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
 
   return mFrame->FrameChanged(aContainer, aDirtyRect);
 }
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -82,16 +82,17 @@ public:
 
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   NS_IMETHOD OnDataAvailable(imgIRequest *aRequest,
                              bool aCurrentFrame,
                              const nsIntRect *aRect);
   NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
                           nsresult aStatus,
                           const PRUnichar *aStatusArg);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   /* get list item text, without '.' */
   static bool AppendCounterText(PRInt32 aListStyleType,
                                   PRInt32 aOrdinal,
                                   nsString& aResult);
 
@@ -116,11 +117,17 @@ protected:
   nsMargin mPadding;
   nsCOMPtr<imgIRequest> mImageRequest;
   nsCOMPtr<imgIDecoderObserver> mListener;
 
   nsSize mIntrinsicSize;
   nsSize mComputedSize;
   PRInt32 mOrdinal;
   bool mTextIsRTL;
+
+private:
+
+  // This is a boolean flag indicating whether or not the current image request
+  // has been registered with the refresh driver.
+  bool mRequestRegistered;
 };
 
 #endif /* nsBulletFrame_h___ */
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -37,16 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* class that manages rules for positioning floats */
 
 #ifndef nsFloatManager_h_
 #define nsFloatManager_h_
 
+#include "mozilla/Types.h"
+
 #include "nsIntervalSet.h"
 #include "nsCoord.h"
 #include "nsRect.h"
 #include "nsTArray.h"
 
 class nsIPresShell;
 class nsIFrame;
 struct nsHTMLReflowState;
@@ -339,18 +341,18 @@ private:
   // next page/column.  This means that any 'clear' needs to continue to
   // the next page/column.
   bool mSplitLeftFloatAcrossBreak;
   bool mSplitRightFloatAcrossBreak;
 
   static PRInt32 sCachedFloatManagerCount;
   static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
 
-  nsFloatManager(const nsFloatManager&);  // no implementation
-  void operator=(const nsFloatManager&);  // no implementation
+  nsFloatManager(const nsFloatManager&) MOZ_DELETE;
+  void operator=(const nsFloatManager&) MOZ_DELETE;
 };
 
 /**
  * A helper class to manage maintenance of the float manager during
  * nsBlockFrame::Reflow. It automatically restores the old float
  * manager in the reflow state when the object goes out of scope.
  */
 class nsAutoFloatManager {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -574,16 +574,20 @@ public:
    * removal and destruction.
    */
   void Destroy() { DestroyFrom(this); }
 
 protected:
   /**
    * Implements Destroy(). Do not call this directly except from within a
    * DestroyFrom() implementation.
+   *
+   * @note This will always be called, so it is not necessary to override
+   *       Destroy() in subclasses of nsFrame, just DestroyFrom().
+   *
    * @param  aDestructRoot is the root of the subtree being destroyed
    */
   virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;
   friend class nsFrameList; // needed to pass aDestructRoot through to children
   friend class nsLineBox;   // needed to pass aDestructRoot through to children
 public:
 
   /**
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -226,16 +226,20 @@ nsImageFrame::DestroyFrom(nsIFrame* aDes
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
     if (imageLoader) {
       // Push a null JSContext on the stack so that code that runs
       // within the below code doesn't think it's being called by
       // JS. See bug 604262.
       nsCxPusher pusher;
       pusher.PushNull();
 
+      // Notify our image loading content that we are going away so it can
+      // deregister with our refresh driver.
+      imageLoader->FrameDestroyed(this);
+
       imageLoader->RemoveObserver(mListener);
     }
     
     reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nsnull);
   }
   
   mListener = nsnull;
 
@@ -271,16 +275,20 @@ nsImageFrame::Init(nsIContent*      aCon
     imageLoader->AddObserver(mListener);
   }
 
   nsPresContext *aPresContext = PresContext();
   
   if (!gIconLoad)
     LoadIcons(aPresContext);
 
+  // We have a PresContext now, so we need to notify the image content node
+  // that it can register images.
+  imageLoader->FrameCreated(this);
+
   // Give image loads associated with an image frame a small priority boost!
   nsCOMPtr<imgIRequest> currentRequest;
   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                           getter_AddRefs(currentRequest));
   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
   if (p)
     p->AdjustPriority(-1);
 
@@ -1949,16 +1957,22 @@ NS_IMETHODIMP
 nsImageFrame::IconLoad::OnStopDecode(imgIRequest *aRequest,
                                      nsresult status,
                                      const PRUnichar *statusArg)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsImageFrame::IconLoad::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsImageFrame::IconLoad::OnStopRequest(imgIRequest *aRequest,
                                       bool aIsLastPart)
 {
   nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
   nsImageFrame *frame;
   while (iter.HasMore()) {
     frame = iter.GetNext();
     frame->Invalidate(frame->GetRect());
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
+++ b/layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
@@ -1,13 +1,18 @@
 <svg xmlns="http://www.w3.org/2000/svg">
   <foreignObject x="0" y="0" width="100px" height="100px">
+
     <!-- lime background -->
     <div xmlns="http://www.w3.org/1999/xhtml"
-         style="background:lime; width:100%; height:100%;"/>
-
-    <!-- embedded iframe, whose contents shouldn't be shown and whose
-         scripts shouldn't be run, when we're being viewed as an image -->
-    <iframe xmlns="http://www.w3.org/1999/xhtml"
-            style="width:80px; height:80px; border: 0"
-            src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%22background-color%3A%20red%22%3Eiframe%20contents%3Cscript%3Ealert(%22script%20shouldn't%20be%20running!!%22)%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E"/>
+         style="background:lime; width:100%; height:100%;">
+      <!-- Use an unrecognized media type, so that if we're honoring plugins,
+           the "download plugin" placeholder will be shown. -->
+      <embed xmlns="http://www.w3.org/1999/xhtml"
+             src="data:audio/hahaThisIsntReallyAFormat,"
+             width="100px" height="50px"/>
+      <!-- Also try a possibly-recognized media format, for completeness. -->
+      <embed xmlns="http://www.w3.org/1999/xhtml"
+             src="data:audio/wav,"
+             width="100px" height="50px"/>
+    </div>
   </foreignObject>
 </svg>
deleted file mode 100644
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-2-helper.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg">
-  <foreignObject x="0" y="0" width="100px" height="100px">
-
-    <!-- lime background -->
-    <div xmlns="http://www.w3.org/1999/xhtml"
-         style="background:lime; width:100%; height:100%;">
-      <!-- Use an unrecognized media type, so that if we're honoring plugins,
-           the "download plugin" placeholder will be shown. -->
-      <embed xmlns="http://www.w3.org/1999/xhtml"
-             src="data:audio/hahaThisIsntReallyAFormat,"
-             width="100px" height="50px"/>
-      <!-- Also try a possibly-recognized media format, for completeness. -->
-      <embed xmlns="http://www.w3.org/1999/xhtml"
-             src="data:audio/wav,"
-             width="100px" height="50px"/>
-    </div>
-  </foreignObject>
-</svg>
deleted file mode 100644
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-2.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
-  <img src="img-foreignObject-embed-2-helper.svg" height="100px" width="100px">
-</body>
-</html>
copy from layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
copy to layout/reftests/svg/as-image/img-foreignObject-iframe-1a-helper.svg
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
+++ b/layout/reftests/svg/as-image/img-foreignObject-iframe-1a-helper.svg
@@ -1,13 +1,14 @@
 <svg xmlns="http://www.w3.org/2000/svg">
   <foreignObject x="0" y="0" width="100px" height="100px">
     <!-- lime background -->
     <div xmlns="http://www.w3.org/1999/xhtml"
-         style="background:lime; width:100%; height:100%;"/>
+         style="background:lime; width:100%; height:100%;">
 
-    <!-- embedded iframe, whose contents shouldn't be shown and whose
-         scripts shouldn't be run, when we're being viewed as an image -->
-    <iframe xmlns="http://www.w3.org/1999/xhtml"
-            style="width:80px; height:80px; border: 0"
-            src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%22background-color%3A%20red%22%3Eiframe%20contents%3Cscript%3Ealert(%22script%20shouldn't%20be%20running!!%22)%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E"/>
+      <!-- embedded iframe, whose contents shouldn't be shown and whose
+           scripts shouldn't be run, when we're being viewed as an image -->
+      <iframe xmlns="http://www.w3.org/1999/xhtml"
+              style="width:80px; height:80px; border: 0"
+              src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%22background-color%3A%20red%22%3Eiframe%20contents%3Cscript%3Ealert(%22script%20shouldn't%20be%20running!!%22)%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E"/>
+    </div>
   </foreignObject>
 </svg>
copy from layout/reftests/svg/as-image/img-foreignObject-embed-1.html
copy to layout/reftests/svg/as-image/img-foreignObject-iframe-1a.html
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-1.html
+++ b/layout/reftests/svg/as-image/img-foreignObject-iframe-1a.html
@@ -1,5 +1,5 @@
 <html>
 <body>
-  <img src="img-foreignObject-embed-1-helper.svg" height="100px" width="100px">
+  <img src="img-foreignObject-iframe-1a-helper.svg" height="100px" width="100px">
 </body>
 </html>
copy from layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
copy to layout/reftests/svg/as-image/img-foreignObject-iframe-1b-helper.svg
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-1-helper.svg
+++ b/layout/reftests/svg/as-image/img-foreignObject-iframe-1b-helper.svg
@@ -1,13 +1,14 @@
 <svg xmlns="http://www.w3.org/2000/svg">
   <foreignObject x="0" y="0" width="100px" height="100px">
     <!-- lime background -->
     <div xmlns="http://www.w3.org/1999/xhtml"
-         style="background:lime; width:100%; height:100%;"/>
+         style="background:lime; width:100%; height:100%;">
 
-    <!-- embedded iframe, whose contents shouldn't be shown and whose
-         scripts shouldn't be run, when we're being viewed as an image -->
-    <iframe xmlns="http://www.w3.org/1999/xhtml"
-            style="width:80px; height:80px; border: 0"
-            src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%22background-color%3A%20red%22%3Eiframe%20contents%3Cscript%3Ealert(%22script%20shouldn't%20be%20running!!%22)%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E"/>
+      <!-- embedded iframe, whose contents shouldn't be shown, when we're
+           being viewed as an image -->
+      <iframe xmlns="http://www.w3.org/1999/xhtml"
+              style="width:80px; height:80px; border: 0"
+              src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%22background-color%3A%20red%22%3Eiframe%20contents%3C%2Fbody%3E%3C%2Fhtml%3E"/>
+    </div>
   </foreignObject>
 </svg>
copy from layout/reftests/svg/as-image/img-foreignObject-embed-1.html
copy to layout/reftests/svg/as-image/img-foreignObject-iframe-1b.html
--- a/layout/reftests/svg/as-image/img-foreignObject-embed-1.html
+++ b/layout/reftests/svg/as-image/img-foreignObject-iframe-1b.html
@@ -1,5 +1,5 @@
 <html>
 <body>
-  <img src="img-foreignObject-embed-1-helper.svg" height="100px" width="100px">
+  <img src="img-foreignObject-iframe-1b-helper.svg" height="100px" width="100px">
 </body>
 </html>
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -53,18 +53,29 @@ fails == canvas-drawImage-slice-1b.html 
 # Test with mix of <html:img> and <svg:image> referring to the same images,
 # with a variety of preserveAspectRatio values in play.
 random == img-and-image-1.html img-and-image-1-ref.svg # bug 645267
 
 # More complex <img> tests
 == img-content-outside-viewBox-1.html img-content-outside-viewBox-1-ref.html
 == img-dyn-1.html img-dyn-1-ref.html
 == img-foreignObject-1.html lime100x100-ref.html
-== img-foreignObject-embed-1.html lime100x100-ref.html
-== img-foreignObject-embed-2.html lime100x100-ref.html
+
+# The following tests check that content embedded via <iframe> and <embed>
+# doesn't load (or execute scripts) in SVG-as-an-image.
+# The "!=" lines are to test that the SVG content, when viewed directly (not as
+# an image), does actually render its external content (making it look
+# different from the reference case).  We don't do that check for
+# img-foreignObject-iframe-1a.html, though, because its SVG image tries to
+# alert(), and that'd trigger a reftest timeout if we loaded that file directly.
+== img-foreignObject-embed-1.html         lime100x100-ref.html
+!= img-foreignObject-embed-1-helper.svg   lime100x100-ref.html
+== img-foreignObject-iframe-1a.html       lime100x100-ref.html
+== img-foreignObject-iframe-1b.html       lime100x100-ref.html
+!= img-foreignObject-iframe-1b-helper.svg lime100x100-ref.html
 
 == img-widthAndHeight-meet-1.html  img-widthAndHeight-meet-1-ref.html
 == img-widthAndHeight-meet-2.html  img-widthAndHeight-meet-2-ref.html
 == img-widthAndHeight-slice-1.html img-widthAndHeight-slice-1-ref.html
 == img-widthAndHeight-slice-2.html img-widthAndHeight-slice-2-ref.html
 
 == img-height-meet-1.html   img-height-meet-1-ref.html
 == img-height-meet-2.html   img-height-meet-2-ref.html
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -431,19 +431,16 @@ nsSVGForeignObjectFrame::NotifyRedrawUns
   }
   return NS_OK;
 }
 
 gfxRect
 nsSVGForeignObjectFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                              PRUint32 aFlags)
 {
-  NS_ASSERTION(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
-               "Should not be calling this on a non-display child");
-
   nsSVGForeignObjectElement *content =
     static_cast<nsSVGForeignObjectElement*>(mContent);
 
   float x, y, w, h;
   content->GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
 
   if (w < 0.0f) w = 0.0f;
   if (h < 0.0f) h = 0.0f;
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -98,16 +98,17 @@ public:
 
   // nsIFrame interface:
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType);
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
+  virtual void DestroyFrom(nsIFrame* aDestructRoot);
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgImageFrame
    */
   virtual nsIAtom* GetType() const;
 
@@ -174,27 +175,44 @@ nsSVGImageFrame::Init(nsIContent* aConte
   nsresult rv = nsSVGImageFrameBase::Init(aContent, aParent, aPrevInFlow);
   if (NS_FAILED(rv)) return rv;
   
   mListener = new nsSVGImageListener(this);
   if (!mListener) return NS_ERROR_OUT_OF_MEMORY;
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
 
+  // We should have a PresContext now, so let's notify our image loader that
+  // we need to register any image animations with the refresh driver.
+  imageLoader->FrameCreated(this);
+
   // Push a null JSContext on the stack so that code that runs within
   // the below code doesn't think it's being called by JS. See bug
   // 604262.
   nsCxPusher pusher;
   pusher.PushNull();
 
   imageLoader->AddObserver(mListener);
 
   return NS_OK; 
 }
 
+/* virtual */ void
+nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  nsCOMPtr<nsIImageLoadingContent> imageLoader =
+    do_QueryInterface(nsFrame::mContent);
+
+  if (imageLoader) {
+    imageLoader->FrameDestroyed(this);
+  }
+
+  nsFrame::DestroyFrom(aDestructRoot);
+}
+
 //----------------------------------------------------------------------
 // nsIFrame methods:
 
 NS_IMETHODIMP
 nsSVGImageFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                   nsIAtom*        aAttribute,
                                   PRInt32         aModType)
 {
--- a/layout/svg/base/src/nsSVGLeafFrame.cpp
+++ b/layout/svg/base/src/nsSVGLeafFrame.cpp
@@ -31,27 +31,34 @@
  * 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 "nsFrame.h"
 #include "nsSVGEffects.h"
+#include "nsImageLoadingContent.h"
 
 class nsSVGLeafFrame : public nsFrame
 {
   friend nsIFrame*
   NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
   nsSVGLeafFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
 
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
+  NS_IMETHOD Init(nsIContent*      aContent,
+                  nsIFrame*        aParent,
+                  nsIFrame*        asPrevInFlow);
+
+  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+
   virtual bool IsFrameOfType(PRUint32 aFlags) const
   {
     return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
   }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
@@ -65,14 +72,41 @@ public:
 nsIFrame*
 NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGLeafFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGLeafFrame)
 
+NS_IMETHODIMP
+nsSVGLeafFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow)
+{
+  nsFrame::Init(aContent, aParent, asPrevInFlow);
+  nsCOMPtr<nsIImageLoadingContent> imageLoader =
+    do_QueryInterface(nsFrame::mContent);
+
+  if (imageLoader) {
+    imageLoader->FrameCreated(this);
+  }
+
+  return NS_OK;
+}
+
+/* virtual */ void
+nsSVGLeafFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  nsCOMPtr<nsIImageLoadingContent> imageLoader =
+    do_QueryInterface(nsFrame::mContent);
+
+  if (imageLoader) {
+    imageLoader->FrameDestroyed(this);
+  }
+
+  nsFrame::DestroyFrom(aDestructRoot);
+}
+
 /* virtual */ void
 nsSVGLeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsFrame::DidSetStyleContext(aOldStyleContext);
   nsSVGEffects::InvalidateRenderingObservers(this);
 }
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/693424-1.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <marker id="m">
+    <foreignObject/>
+  </marker>
+  <line marker-end="url(#m)"/>
+</svg>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -113,8 +113,9 @@ load 655025-1.svg
 load 655025-2.svg
 load 655025-3.svg
 load 657077-1.svg
 load 669025-1.svg
 load 669025-2.svg
 load 682411-1.svg
 load 692203-1.svg
 load 692203-2.svg
+load 693424-1.svg
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -67,16 +67,17 @@
 #include "nsIDOMHTMLImageElement.h"
 #include "nsINameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsIDOMHTMLMapElement.h"
 #include "nsBoxLayoutState.h"
 #include "nsIDOMDocument.h"
 #include "nsTransform2D.h"
 #include "nsITheme.h"
+#include "nsIImageLoadingContent.h"
 
 #include "nsIServiceManager.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsDisplayList.h"
@@ -168,16 +169,17 @@ nsImageBoxFrame::AttributeChanged(PRInt3
     UpdateLoadFlags();
 
   return rv;
 }
 
 nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
   nsLeafBoxFrame(aShell, aContext),
   mIntrinsicSize(0,0),
+  mRequestRegistered(false),
   mLoadFlags(nsIRequest::LOAD_NORMAL),
   mUseSrcAttr(false),
   mSuppressStyleCheck(false)
 {
   MarkIntrinsicWidthsDirty();
 }
 
 nsImageBoxFrame::~nsImageBoxFrame()
@@ -190,19 +192,23 @@ nsImageBoxFrame::MarkIntrinsicWidthsDirt
 {
   SizeNeedsRecalc(mImageSize);
   nsLeafBoxFrame::MarkIntrinsicWidthsDirty();
 }
 
 void
 nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
-  // Release image loader first so that it's refcnt can go to zero
-  if (mImageRequest)
+  if (mImageRequest) {
+    nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
+                                          &mRequestRegistered);
+
+    // Release image loader first so that it's refcnt can go to zero
     mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
+  }
 
   if (mListener)
     reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nsnull); // set the frame to null so we don't send messages to a dead object.
 
   nsLeafBoxFrame::DestroyFrom(aDestructRoot);
 }
 
 
@@ -227,17 +233,21 @@ nsImageBoxFrame::Init(nsIContent*      a
   UpdateImage();
 
   return rv;
 }
 
 void
 nsImageBoxFrame::UpdateImage()
 {
+  nsPresContext* presContext = PresContext();
+
   if (mImageRequest) {
+    nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
+                                          &mRequestRegistered);
     mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
     mImageRequest = nsnull;
   }
 
   // get the new image src
   nsAutoString src;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
   mUseSrcAttr = !src.IsEmpty();
@@ -254,16 +264,22 @@ nsImageBoxFrame::UpdateImage()
                                               doc,
                                               baseURI);
 
     if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc,
                                             mContent->NodePrincipal())) {
       nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
                                 doc->GetDocumentURI(), mListener, mLoadFlags,
                                 getter_AddRefs(mImageRequest));
+
+      if (mImageRequest) {
+        nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
+                                                      mImageRequest,
+                                                      &mRequestRegistered);
+      }
     }
   } else {
     // Only get the list-style-image if we aren't being drawn
     // by a native theme.
     PRUint8 appearance = GetStyleDisplay()->mAppearance;
     if (!(appearance && nsBox::gTheme &&
           nsBox::gTheme->ThemeSupportsWidget(nsnull, this, appearance))) {
       // get the list-style-image
@@ -596,16 +612,25 @@ NS_IMETHODIMP nsImageBoxFrame::OnStopDec
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
     FireImageDOMEvent(mContent, NS_LOAD_ERROR);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  // Register with our refresh driver, if we're animated.
+  nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
+                                      &mRequestRegistered);
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsImageBoxFrame::FrameChanged(imgIContainer *aContainer,
                                             const nsIntRect *aDirtyRect)
 {
   nsBoxLayoutState state(PresContext());
   this->Redraw(state);
 
   return NS_OK;
 }
@@ -619,40 +644,48 @@ nsImageBoxListener::nsImageBoxListener()
 nsImageBoxListener::~nsImageBoxListener()
 {
 }
 
 NS_IMETHODIMP nsImageBoxListener::OnStartContainer(imgIRequest *request,
                                                    imgIContainer *image)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
 
   return mFrame->OnStartContainer(request, image);
 }
 
 NS_IMETHODIMP nsImageBoxListener::OnStopContainer(imgIRequest *request,
                                                   imgIContainer *image)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
 
   return mFrame->OnStopContainer(request, image);
 }
 
 NS_IMETHODIMP nsImageBoxListener::OnStopDecode(imgIRequest *request,
                                                nsresult status,
                                                const PRUnichar *statusArg)
 {
   if (!mFrame)
-    return NS_ERROR_FAILURE;
+    return NS_OK;
 
   return mFrame->OnStopDecode(request, status, statusArg);
 }
 
+NS_IMETHODIMP nsImageBoxListener::OnImageIsAnimated(imgIRequest* aRequest)
+{
+  if (!mFrame)
+    return NS_OK;
+
+  return mFrame->OnImageIsAnimated(aRequest);
+}
+
 NS_IMETHODIMP nsImageBoxListener::FrameChanged(imgIContainer *aContainer,
                                                const nsIntRect *aDirtyRect)
 {
   if (!mFrame)
     return NS_ERROR_FAILURE;
 
   return mFrame->FrameChanged(aContainer, aDirtyRect);
 }
--- a/layout/xul/base/src/nsImageBoxFrame.h
+++ b/layout/xul/base/src/nsImageBoxFrame.h
@@ -53,16 +53,18 @@ public:
   virtual ~nsImageBoxListener();
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
   NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
   NS_IMETHOD OnStopDecode(imgIRequest *request, nsresult status,
                           const PRUnichar *statusArg);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest);
+
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   void SetFrame(nsImageBoxFrame *frame) { mFrame = frame; }
 
 private:
   nsImageBoxFrame *mFrame;
@@ -117,16 +119,18 @@ public:
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
 
   NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
   NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
   NS_IMETHOD OnStopDecode(imgIRequest *request,
                           nsresult status,
                           const PRUnichar *statusArg);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest);
+
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   virtual ~nsImageBoxFrame();
 
   void  PaintImage(nsRenderingContext& aRenderingContext,
                    const nsRect& aDirtyRect,
                    nsPoint aPt, PRUint32 aFlags);
@@ -137,16 +141,20 @@ protected:
   virtual void GetImageSize();
 
 private:
 
   nsRect mSubRect; ///< If set, indicates that only the portion of the image specified by the rect should be used.
   nsSize mIntrinsicSize;
   nsSize mImageSize;
 
+  // Boolean variable to determine if the current image request has been
+  // registered with the refresh driver.
+  bool mRequestRegistered;
+
   nsCOMPtr<imgIRequest> mImageRequest;
   nsCOMPtr<imgIDecoderObserver> mListener;
 
   PRInt32 mLoadFlags;
 
   bool mUseSrcAttr; ///< Whether or not the image src comes from an attribute.
   bool mSuppressStyleCheck;
 }; // class nsImageBoxFrame
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/tree/src/nsITreeImageListener.h
@@ -0,0 +1,64 @@
+/* ***** 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
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Hyatt <hyatt@mozilla.org> (Original Author)
+ *   Jan Varga <varga@ku.sk>
+ *   Scott Johnson <sjohnson@mozilla.com>, Mozilla Corporation
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 nsITreeImageListener_h__
+#define nsITreeImageListener_h__
+
+// The interface for our image listener.
+// {90586540-2D50-403e-8DCE-981CAA778444}
+#define NS_ITREEIMAGELISTENER_IID \
+{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
+
+class nsITreeImageListener : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
+
+  NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
+
+  /**
+   * Clear the internal frame pointer to prevent dereferencing an object
+   * that no longer exists.
+   */
+  NS_IMETHOD ClearFrame() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
+
+#endif
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -49,16 +49,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsISupportsArray.h"
 #include "nsPresContext.h"
 #include "nsINameSpaceManager.h"
 
 #include "nsTreeBodyFrame.h"
 #include "nsTreeSelection.h"
+#include "nsTreeImageListener.h"
 
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 
 #include "nsIContent.h"
 #include "nsStyleContext.h"
 #include "nsIBoxObject.h"
 #include "nsGUIEvent.h"
@@ -109,16 +110,24 @@
 
 using namespace mozilla;
 
 // Enumeration function that cancels all the image requests in our cache
 static PLDHashOperator
 CancelImageRequest(const nsAString& aKey,
                    nsTreeImageCacheEntry aEntry, void* aData)
 {
+
+  // If our imgIRequest object was registered with the refresh driver,
+  // then we need to deregister it.
+  nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
+
+  nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
+                                        nsnull);
+
   aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
   return PL_DHASH_NEXT;
 }
 
 //
 // NS_NewTreeFrame
 //
 // Creates a new tree frame
@@ -159,17 +168,18 @@ nsTreeBodyFrame::nsTreeBodyFrame(nsIPres
 {
   mColumns = new nsTreeColumns(nsnull);
   NS_NewISupportsArray(getter_AddRefs(mScratchArray));
 }
 
 // Destructor
 nsTreeBodyFrame::~nsTreeBodyFrame()
 {
-  mImageCache.EnumerateRead(CancelImageRequest, nsnull);
+  mImageCache.EnumerateRead(CancelImageRequest, this);
+  DetachImageListeners();
   delete mSlots;
 }
 
 static void
 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
 {
   aMargin.SizeTo(0, 0, 0, 0);
   if (!aContext->GetStylePadding()->GetPadding(aMargin)) {
@@ -192,18 +202,21 @@ nsTreeBodyFrame::Init(nsIContent*     aC
                       nsIFrame*       aPrevInFlow)
 {
   nsresult rv = nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mIndentation = GetIndentation();
   mRowHeight = GetRowHeight();
 
+  NS_ENSURE_TRUE(mCreatedListeners.Init(), NS_ERROR_OUT_OF_MEMORY);
+
   NS_ENSURE_TRUE(mImageCache.Init(16), NS_ERROR_OUT_OF_MEMORY);
   EnsureBoxObject();
+
   return rv;
 }
 
 nsSize
 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
 {
   EnsureView();
 
@@ -2149,20 +2162,24 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIn
         listener->AddCell(aRowIndex, aCol);
       return NS_OK;
     }
   }
 
   if (!*aResult) {
     // Create a new nsTreeImageListener object and pass it our row and column
     // information.
-    nsTreeImageListener* listener = new nsTreeImageListener(mTreeBoxObject);
+    nsTreeImageListener* listener = new nsTreeImageListener(this);
     if (!listener)
       return NS_ERROR_OUT_OF_MEMORY;
 
+    if (!mCreatedListeners.PutEntry(listener)) {
+      return NS_ERROR_FAILURE;
+    }
+
     listener->AddCell(aRowIndex, aCol);
     nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
 
     nsCOMPtr<imgIRequest> imageRequest;
     if (styleRequest) {
       styleRequest->Clone(imgDecoderObserver, getter_AddRefs(imageRequest));
     } else {
       nsIDocument* doc = mContent->GetDocument();
@@ -4231,17 +4248,17 @@ nsTreeBodyFrame::GetBaseElement()
 
   return nsnull;
 }
 
 nsresult
 nsTreeBodyFrame::ClearStyleAndImageCaches()
 {
   mStyleCache.Clear();
-  mImageCache.EnumerateRead(CancelImageRequest, nsnull);
+  mImageCache.EnumerateRead(CancelImageRequest, this);
   mImageCache.Clear();
   return NS_OK;
 }
 
 /* virtual */ void
 nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
@@ -4463,16 +4480,30 @@ nsTreeBodyFrame::PostScrollEvent()
   nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
   if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
     NS_WARNING("failed to dispatch ScrollEvent");
   } else {
     mScrollEvent = ev;
   }
 }
 
+void
+nsTreeBodyFrame::DetachImageListeners()
+{
+  mCreatedListeners.Clear();
+}
+
+void
+nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
+{
+  if (aListener) {
+    mCreatedListeners.RemoveEntry(aListener);
+  }
+}
+
 #ifdef ACCESSIBILITY
 void
 nsTreeBodyFrame::FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount)
 {
   nsCOMPtr<nsIContent> content(GetBaseElement());
   if (!content)
     return;
 
@@ -4636,8 +4667,17 @@ nsTreeBodyFrame::FullScrollbarsUpdate(bo
   if (aNeedsFullInvalidation) {
     Invalidate();
   }
   InvalidateScrollbars(parts, weakColumnsFrame);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   nsContentUtils::AddScriptRunner(new nsOverflowChecker(this));
   return weakFrame.IsAlive();
 }
+
+nsresult
+nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest)
+{
+  nsLayoutUtils::RegisterImageRequest(PresContext(),
+                                      aRequest, nsnull);
+
+  return NS_OK;
+}
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
@@ -49,26 +49,27 @@
 #include "nsICSSPseudoComparator.h"
 #include "nsIScrollbarMediator.h"
 #include "nsIDragSession.h"
 #include "nsITimer.h"
 #include "nsIReflowCallback.h"
 #include "nsTArray.h"
 #include "nsTreeStyleCache.h"
 #include "nsTreeColumns.h"
-#include "nsTreeImageListener.h"
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "imgIRequest.h"
 #include "imgIDecoderObserver.h"
 #include "nsScrollbarFrame.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LookAndFeel.h"
+#include "nsITreeImageListener.h"
 
 class nsOverflowChecker;
+class nsTreeImageListener;
 
 // An entry in the tree's image cache
 struct nsTreeImageCacheEntry
 {
   nsTreeImageCacheEntry() {}
   nsTreeImageCacheEntry(imgIRequest *aRequest, imgIDecoderObserver *aListener)
     : request(aRequest), listener(aListener) {}
 
@@ -86,16 +87,21 @@ class NS_FINAL_CLASS nsTreeBodyFrame
 public:
   nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
   ~nsTreeBodyFrame();
 
   NS_DECL_QUERYFRAME_TARGET(nsTreeBodyFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
+  // Callback handler methods for refresh driver based animations.
+  // Calls to these functions are forwarded from nsTreeImageListener. These
+  // mirror how nsImageFrame works.
+  nsresult OnImageIsAnimated(imgIRequest* aRequest);
+
   // non-virtual signatures like nsITreeBodyFrame
   nsresult GetColumns(nsITreeColumns **aColumns);
   nsresult GetView(nsITreeView **aView);
   nsresult SetView(nsITreeView *aView);
   nsresult GetFocused(bool *aFocused);
   nsresult SetFocused(bool aFocused);
   nsresult GetTreeBody(nsIDOMElement **aElement);
   nsresult GetRowHeight(PRInt32 *aValue);
@@ -430,16 +436,25 @@ public:
     if (!aUnknownCol)
       return nsnull;
 
     nsTreeColumn* col;
     aUnknownCol->QueryInterface(NS_GET_IID(nsTreeColumn), (void**)&col);
     return col;
   }
 
+  /**
+   * Remove an nsITreeImageListener from being tracked by this frame. Only tree
+   * image listeners that are created by this frame are tracked.
+   *
+   * @param aListener A pointer to an nsTreeImageListener to no longer
+   *        track.
+   */
+  void RemoveTreeImageListener(nsTreeImageListener* aListener);
+
 protected:
 
   // Create a new timer. This method is used to delay various actions like
   // opening/closing folders or tree scrolling.
   // aID is type of the action, aFunc is the function to be called when
   // the timer fires and aType is type of timer - one shot or repeating.
   nsresult CreateTimer(const mozilla::LookAndFeel::IntID aID,
                        nsTimerCallbackFunc aFunc, PRInt32 aType,
@@ -460,16 +475,22 @@ protected:
     void Revoke() { mInner = nsnull; }
   private:
     nsTreeBodyFrame* mInner;
   };
 
   void PostScrollEvent();
   void FireScrollEvent();
 
+  /**
+   * Clear the pointer to this frame for all nsTreeImageListeners that were
+   * created by this frame.
+   */
+  void DetachImageListeners();
+
 #ifdef ACCESSIBILITY
   /**
    * Fires 'treeRowCountChanged' event asynchronously. The event supports
    * nsIDOMDataContainerEvent interface that is used to expose the following
    * information structures.
    *
    * @param aIndex  the row index rows are added/removed from
    * @param aCount  the number of added/removed rows (the sign points to
@@ -597,11 +618,16 @@ protected: // Data Members
 
   // Do we have a fixed number of onscreen rows?
   bool mHasFixedRowCount;
 
   bool mVerticalOverflow;
   bool mHorizontalOverflow;
 
   bool mReflowCallbackPosted;
+
+  // Hash table to keep track of which listeners we created and thus
+  // have pointers to us.
+  nsTHashtable<nsPtrHashKey<nsTreeImageListener> > mCreatedListeners;
+
 }; // class nsTreeBodyFrame
 
 #endif
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
@@ -39,28 +39,38 @@
 
 #include "nsTreeImageListener.h"
 #include "nsITreeBoxObject.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 
 NS_IMPL_ISUPPORTS3(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver, nsITreeImageListener)
 
-nsTreeImageListener::nsTreeImageListener(nsITreeBoxObject* aTree)
-  : mTree(aTree),
+nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
+  : mTreeFrame(aTreeFrame),
     mInvalidationSuppressed(true),
     mInvalidationArea(nsnull)
 {
 }
 
 nsTreeImageListener::~nsTreeImageListener()
 {
   delete mInvalidationArea;
 }
 
+NS_IMETHODIMP
+nsTreeImageListener::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  if (!mTreeFrame) {
+    return NS_OK;
+  }
+
+  return mTreeFrame->OnImageIsAnimated(aRequest);
+}
+
 NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest,
                                                     imgIContainer *aImage)
 {
   // Ensure the animation (if any) is started. Note: There is no
   // corresponding call to Decrement for this. This Increment will be
   // 'cleaned up' by the Request when it is destroyed, but only then.
   aRequest->IncrementAnimationConsumers();
   return NS_OK;
@@ -108,20 +118,26 @@ nsTreeImageListener::AddCell(PRInt32 aIn
   return NS_OK;
 }
 
 
 void
 nsTreeImageListener::Invalidate()
 {
   if (!mInvalidationSuppressed) {
-    for (InvalidationArea* currArea = mInvalidationArea; currArea; currArea = currArea->GetNext()) {
+    for (InvalidationArea* currArea = mInvalidationArea; currArea;
+         currArea = currArea->GetNext()) {
       // Loop from min to max, invalidating each cell that was listening for this image.
       for (PRInt32 i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
-        mTree->InvalidateCell(i, currArea->GetCol());
+        if (mTreeFrame) {
+          nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
+          if (tree) {
+            tree->InvalidateCell(i, currArea->GetCol());
+          }
+        }
       }
     }
   }
 }
 
 nsTreeImageListener::InvalidationArea::InvalidationArea(nsITreeColumn* aCol)
   : mCol(aCol),
     mMin(-1), // min should start out "undefined"
@@ -135,8 +151,15 @@ nsTreeImageListener::InvalidationArea::A
 {
   if (mMin == -1)
     mMin = mMax = aIndex;
   else if (aIndex < mMin)
     mMin = aIndex;
   else if (aIndex > mMax)
     mMax = aIndex;
 }
+
+NS_IMETHODIMP
+nsTreeImageListener::ClearFrame()
+{
+  mTreeFrame = nsnull;
+  return NS_OK;
+}
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.h
+++ b/layout/xul/base/src/tree/src/nsTreeImageListener.h
@@ -39,60 +39,47 @@
 
 #ifndef nsTreeImageListener_h__
 #define nsTreeImageListener_h__
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsITreeColumns.h"
 #include "nsStubImageDecoderObserver.h"
-
-class nsITreeBoxObject;
-
-// The interface for our image listener.
-// {90586540-2D50-403e-8DCE-981CAA778444}
-#define NS_ITREEIMAGELISTENER_IID \
-{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
-
-class nsITreeImageListener : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
-
-  NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
+#include "nsTreeBodyFrame.h"
+#include "nsITreeImageListener.h"
 
 // This class handles image load observation.
 class nsTreeImageListener : public nsStubImageDecoderObserver, public nsITreeImageListener
 {
 public:
-  nsTreeImageListener(nsITreeBoxObject* aTree);
+  nsTreeImageListener(nsTreeBodyFrame *aTreeFrame);
   ~nsTreeImageListener();
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest);
   NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
                              const nsIntRect *aRect);
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol);
- 
+  NS_IMETHOD ClearFrame();
+
   friend class nsTreeBodyFrame;
 
 protected:
   void UnsuppressInvalidation() { mInvalidationSuppressed = false; }
   void Invalidate();
 
 private:
-  nsITreeBoxObject* mTree;
+  nsTreeBodyFrame* mTreeFrame;
 
   // A guard that prevents us from recursive painting.
   bool mInvalidationSuppressed;
 
   class InvalidationArea {
     public:
       InvalidationArea(nsITreeColumn* aCol);
       ~InvalidationArea() { delete mNext; }
--- a/mfbt/Types.h
+++ b/mfbt/Types.h
@@ -83,9 +83,45 @@
 #else
 # define MFBT_API(type_)       MOZ_IMPORT_API(type_)
 #endif
 
 
 #define MOZ_BEGIN_EXTERN_C     JS_BEGIN_EXTERN_C
 #define MOZ_END_EXTERN_C       JS_END_EXTERN_C
 
+#ifdef __cplusplus
+
+/*
+ * MOZ_DELETE, specified immediately prior to the ';' terminating an undefined-
+ * method declaration, attempts to delete that method from the corresponding
+ * class.  An attempt to use the method will produce an error *at link time*,
+ * not at compile time, in compilers for which this macro can be implemented.
+ * For example, you can use this macro to produce classes with no implicit copy
+ * constructor or assignment operator:
+ *
+ *   struct NonCopyable {
+ *     private:
+ *       NonCopyable(const NonCopyable& other) MOZ_DELETE;
+ *       void operator=(const NonCopyable& other) MOZ_DELETE;
+ *   };
+ *
+ * If MOZ_DELETE can't be implemented for the current compiler, it will still
+ * cause an error, but at link time rather than compile time.
+ *
+ * MOZ_DELETE relies on C++11 functionality not universally implemented.  As a
+ * backstop, method declarations using MOZ_DELETE should be private.
+ */
+#if defined(__clang__) && (__clang_major__ >= 3 || (__clang_major__ == 2 && __clang_minor__ >= 9))
+# define MOZ_DELETE            = delete
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
+/*
+ * g++ >= 4.4 supports deleted functions, but it requires -std=c++0x or
+ * -std=gnu++0x, and for various reasons we can't use these yet.
+ */
+# define MOZ_DELETE            /* = delete */
+#else
+# define MOZ_DELETE            /* unknown C++11 deleted function support */
+#endif
+
+#endif /* __cplusplus */
+
 #endif  /* mozilla_Types_h_ */
--- a/mobile/components/SafeBrowsing.js
+++ b/mobile/components/SafeBrowsing.js
@@ -96,23 +96,23 @@ SafeBrowsing.prototype = {
         break;
     }
   },
 
   _startup: function sb_startup() {
     this.listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(Ci.nsIUrlListManager);
 
     // Add a test chunk to the database
-    let testData = "mozilla.com/firefox/its-an-attack.html";
+    let testData = "mozilla.org/firefox/its-an-attack.html";
     let testUpdate =
       "n:1000\ni:test-malware-simple\nad:1\n" +
       "a:1:32:" + testData.length + "\n" +
       testData;
 
-    testData = "mozilla.com/firefox/its-a-trap.html";
+    testData = "mozilla.org/firefox/its-a-trap.html";
     testUpdate +=
       "n:1000\ni:test-phish-simple\nad:1\n" +
       "a:1:32:" + testData.length + "\n" +
       testData;
 
     let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
 
     let listener = {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -314,16 +314,17 @@ pref("slider.snapMultiplier", 0);
 // option to choose plug-in finder
 pref("application.use_ns_plugin_finder", false);
 
 // URI fixup prefs
 pref("browser.fixup.alternate.enabled", true);
 pref("browser.fixup.alternate.prefix", "www.");
 pref("browser.fixup.alternate.suffix", ".com");
 pref("browser.fixup.hide_user_pass", true);
+pref("browser.fixup.use-utf8", false);
 
 // Location Bar AutoComplete
 pref("browser.urlbar.autocomplete.enabled", true);
 
 // Print header customization
 // Use the following codes:
 // &T - Title
 // &U - Document URL
@@ -629,16 +630,17 @@ pref("javascript.options.tracejit.conten
 pref("javascript.options.tracejit.chrome",   false);
 pref("javascript.options.methodjit.content", true);
 pref("javascript.options.methodjit.chrome",  true);
 pref("javascript.options.jitprofiling.content", true);
 pref("javascript.options.jitprofiling.chrome",  true);
 pref("javascript.options.pccounts.content", false);
 pref("javascript.options.pccounts.chrome",  false);
 pref("javascript.options.methodjit_always", false);
+pref("javascript.options.jit_hardening", true);
 pref("javascript.options.typeinference", true);
 // This preference limits the memory usage of javascript.
 // If you want to change these values for your device,
 // please find Bug 417052 comment 17 and Bug 456721
 // Comment 32 and Bug 613551.
 pref("javascript.options.mem.high_water_mark", 128);
 pref("javascript.options.mem.max", -1);
 pref("javascript.options.mem.gc_per_compartment", true);
@@ -984,20 +986,16 @@ pref("network.dns.disableIPv6", false);
 // This preference controls whether or not URLs with UTF-8 characters are
 // escaped.  Set this preference to TRUE for strict RFC2396 conformance.
 pref("network.standard-url.escape-utf8", true);
 
 // This preference controls whether or not URLs are always encoded and sent as
 // UTF-8.
 pref("network.standard-url.encode-utf8", true);
 
-// This preference controls whether or not queries are encoded and sent as
-// UTF-8.
-pref("network.standard-url.encode-query-utf8", false);
-
 // Idle timeout for ftp control connections - 5 minute default
 pref("network.ftp.idleConnectionTimeout", 300);
 
 // directory listing format
 // 2: HTML
 // 3: XUL directory viewer
 // all other values are treated like 2
 pref("network.dir.format", 2);
--- a/netwerk/base/src/nsStandardURL.cpp
+++ b/netwerk/base/src/nsStandardURL.cpp
@@ -62,17 +62,16 @@
 static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
 static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
 
 nsIIDNService *nsStandardURL::gIDN = nsnull;
 nsICharsetConverterManager *nsStandardURL::gCharsetMgr = nsnull;
 bool nsStandardURL::gInitialized = false;
 bool nsStandardURL::gEscapeUTF8 = true;
 bool nsStandardURL::gAlwaysEncodeInUTF8 = true;
-bool nsStandardURL::gEncodeQueryInUTF8 = true;
 
 #if defined(PR_LOGGING)
 //
 // setenv NSPR_LOG_MODULES nsStandardURL:5
 //
 static PRLogModuleInfo *gStandardURLLog;
 #endif
 
@@ -139,17 +138,16 @@ end:
 
 //----------------------------------------------------------------------------
 // nsStandardURL::nsPrefObserver
 //----------------------------------------------------------------------------
 
 #define NS_NET_PREF_ESCAPEUTF8         "network.standard-url.escape-utf8"
 #define NS_NET_PREF_ENABLEIDN          "network.enableIDN"
 #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
-#define NS_NET_PREF_ENCODEQUERYINUTF8  "network.standard-url.encode-query-utf8"
 
 NS_IMPL_ISUPPORTS1(nsStandardURL::nsPrefObserver, nsIObserver)
 
 NS_IMETHODIMP nsStandardURL::
 nsPrefObserver::Observe(nsISupports *subject,
                         const char *topic,
                         const PRUnichar *data)
 {
@@ -268,18 +266,17 @@ nsSegmentEncoder::InitUnicodeEncoder()
 
 #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
     nsSegmentEncoder name(useUTF8 ? nsnull : mOriginCharset.get())
 
 #define GET_SEGMENT_ENCODER(name) \
     GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8)
 
 #define GET_QUERY_ENCODER(name) \
-    GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8 && \
-                                 gEncodeQueryInUTF8)
+    GET_SEGMENT_ENCODER_INTERNAL(name, false)
 
 //----------------------------------------------------------------------------
 // nsStandardURL <public>
 //----------------------------------------------------------------------------
 
 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
 static PRCList gAllURLs;
 #endif
@@ -345,17 +342,16 @@ DumpLeakedURLs::~DumpLeakedURLs()
 void
 nsStandardURL::InitGlobalObjects()
 {
     nsCOMPtr<nsIPrefBranch2> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
     if (prefBranch) {
         nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
         prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false);
         prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false);
-        prefBranch->AddObserver(NS_NET_PREF_ENCODEQUERYINUTF8, obs.get(), false);
         prefBranch->AddObserver(NS_NET_PREF_ENABLEIDN, obs.get(), false);
 
         PrefsChanged(prefBranch, nsnull);
     }
 
 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
     PR_INIT_CLIST(&gAllURLs);
 #endif
@@ -951,22 +947,16 @@ nsStandardURL::PrefsChanged(nsIPrefBranc
         LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled"));
     }
 
     if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) {
         if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val))
             gAlwaysEncodeInUTF8 = val;
         LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled"));
     }
-
-    if (PREF_CHANGED(NS_NET_PREF_ENCODEQUERYINUTF8)) {
-        if (GOT_PREF(NS_NET_PREF_ENCODEQUERYINUTF8, val))
-            gEncodeQueryInUTF8 = val;
-        LOG(("encode query in UTF-8 %s\n", gEncodeQueryInUTF8 ? "enabled" : "disabled"));
-    }
 #undef PREF_CHANGED
 #undef GOT_PREF
 }
 
 //----------------------------------------------------------------------------
 // nsStandardURL::nsISupports
 //----------------------------------------------------------------------------
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -1848,16 +1848,19 @@ nsHttpChannel::ProcessNotModified()
     if (NS_FAILED(rv)) return rv;
 
     // make the cached response be the current response
     mResponseHead = mCachedResponseHead;
 
     rv = UpdateExpirationTime();
     if (NS_FAILED(rv)) return rv;
 
+    rv = AddCacheEntryHeaders(mCacheEntry);
+    if (NS_FAILED(rv)) return rv;
+
     // notify observers interested in looking at a reponse that has been
     // merged with any cached headers
     gHttpHandler->OnExamineMergedResponse(this);
 
     mCachedContentIsValid = true;
     rv = ReadFromCache();
     if (NS_FAILED(rv)) return rv;
 
--- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2003
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
  *   Jim Mathies <jmathies@mozilla.com>
+ *   Guillermo Robla Vicario <groblavicario@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -47,16 +48,19 @@
 
 //-----------------------------------------------------------------------------
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIServiceManager.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
+#include "nsIX509Cert.h"
+#include "nsISSLStatus.h"
+#include "nsISSLStatusProvider.h"
 
 static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
 static const char kTrustedURIs[]  = "network.automatic-ntlm-auth.trusted-uris";
 static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
 
 // XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp,
 // but since that file lives in a separate library we cannot directly share it.
 // bug 236865 addresses this problem.
@@ -230,16 +234,19 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHtt
                                   bool            isProxyAuth,
                                   nsISupports   **sessionState,
                                   nsISupports   **continuationState,
                                   bool           *identityInvalid)
 {
     LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
          *sessionState, *continuationState));
 
+    // Use the native NTLM if available
+    mUseNative = true;
+
     // NOTE: we don't define any session state, but we do use the pointer.
 
     *identityInvalid = false;
 
     // Start a new auth sequence if the challenge is exactly "NTLM".
     // If native NTLM auth apis are available and enabled through prefs,
     // try to use them.
     if (PL_strcasecmp(challenge, "NTLM") == 0) {
@@ -293,16 +300,18 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHtt
                     return NS_ERROR_OUT_OF_MEMORY;
                 NS_ADDREF(*sessionState);
             }
 
             // Use our internal NTLM implementation. Note, this is less secure,
             // see bug 520607 for details.
             LOG(("Trying to fall back on internal ntlm auth.\n"));
             module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
+	    
+            mUseNative = false;
 
             // Prompt user for domain, username, and password.
             *identityInvalid = true;
         }
 
         // If this fails, then it means that we cannot do NTLM auth.
         if (!module) {
             LOG(("No ntlm auth modules available.\n"));
@@ -361,18 +370,71 @@ nsHttpNTLMAuth::GenerateCredentials(nsIH
             return rv;
         serviceName.AppendLiteral("HTTP@");
         serviceName.Append(host);
         // initialize auth module
         rv = module->Init(serviceName.get(), nsIAuthModule::REQ_DEFAULT, domain, user, pass);
         if (NS_FAILED(rv))
             return rv;
 
+// This update enables updated Windows machines (Win7 or patched previous
+// versions) and Linux machines running Samba (updated for Channel 
+// Binding), to perform Channel Binding when authenticating using NTLMv2 
+// and an outer secure channel.
+// 
+// Currently only implemented for Windows, linux support will be landing in 
+// a separate patch, update this #ifdef accordingly then.
+#if defined (XP_WIN) /* || defined (LINUX) */
+        // We should retrieve the server certificate and compute the CBT, 
+        // but only when we are using the native NTLM implementation and 
+        // not the internal one.
+        // It is a valid case not having the security info object.  This
+        // occures when we connect an https site through an ntlm proxy.
+        // After the ssl tunnel has been created, we get here the second
+        // time and now generate the CBT from now valid security info.
+        nsCOMPtr<nsIChannel> channel = do_QueryInterface(authChannel, &rv);
+        if (NS_FAILED(rv))
+            return rv;
+
+        nsCOMPtr<nsISupports> security;
+        rv = channel->GetSecurityInfo(getter_AddRefs(security));
+        if (NS_FAILED(rv))
+            return rv;
+
+        nsCOMPtr<nsISSLStatusProvider> statusProvider =
+            do_QueryInterface(security);
+
+        if (mUseNative && statusProvider) {
+            nsCOMPtr<nsISSLStatus> status;
+            rv = statusProvider->GetSSLStatus(getter_AddRefs(status));
+            if (NS_FAILED(rv))
+                return rv;
+
+            nsCOMPtr<nsIX509Cert> cert;
+            rv = status->GetServerCert(getter_AddRefs(cert));
+            if (NS_FAILED(rv))
+                return rv;
+
+            PRUint32 length;
+            PRUint8* certArray;
+            cert->GetRawDER(&length, &certArray);						  
+			
+            // If there is a server certificate, we pass it along the
+            // first time we call GetNextToken().
+            inBufLen = length;
+            inBuf = certArray;
+        } else { 
+            // If there is no server certificate, we don't pass anything.
+            inBufLen = 0;
+            inBuf = nsnull;
+        }
+#else // Extended protection update is just for Linux and Windows machines.
         inBufLen = 0;
         inBuf = nsnull;
+#endif
     }
     else {
         // decode challenge; skip past "NTLM " to the start of the base64
         // encoded data.
         int len = strlen(challenge);
         if (len < 6)
             return NS_ERROR_UNEXPECTED; // bogus challenge
         challenge += 5;
--- a/netwerk/protocol/http/nsHttpNTLMAuth.h
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.h
@@ -15,16 +15,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2003
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@netscape.com>
+ *   Guillermo Robla Vicario <groblavicario@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -43,11 +44,16 @@
 class nsHttpNTLMAuth : public nsIHttpAuthenticator
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIHTTPAUTHENTICATOR
 
     nsHttpNTLMAuth() {}
     virtual ~nsHttpNTLMAuth() {}
+
+private:
+    // This flag indicates whether we are using the native NTLM implementation
+    // or the internal one.
+    bool  mUseNative;
 };
 
 #endif // !nsHttpNTLMAuth_h__
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -458,28 +458,50 @@ nsMultiMixedConv::AsyncConvertData(const
     //
     // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
     //  and OnStopRequest() call combinations. We call of series of these for each sub-part
     //  in the raw stream.
     mFinalListener = aListener;
     return NS_OK;
 }
 
-#define ERR_OUT { free(buffer); return rv; }
+// AutoFree implementation to prevent memory leaks
+class AutoFree
+{
+public:
+  AutoFree() : mBuffer(NULL) {}
+
+  AutoFree(char *buffer) : mBuffer(buffer) {}
+
+  ~AutoFree() {
+    free(mBuffer);
+  }
+
+  AutoFree& operator=(char *buffer) {
+    mBuffer = buffer;
+    return *this;
+  }
+
+  operator char*() const {
+    return mBuffer;
+  }
+private:
+  char *mBuffer;
+};
 
 // nsIStreamListener implementation
 NS_IMETHODIMP
 nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
                                   nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
 
     if (mToken.IsEmpty()) // no token, no love.
         return NS_ERROR_FAILURE;
 
     nsresult rv = NS_OK;
-    char *buffer = nsnull;
+    AutoFree buffer = nsnull;
     PRUint32 bufLen = 0, read = 0;
 
     NS_ASSERTION(request, "multimixed converter needs a request");
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
     if (NS_FAILED(rv)) return rv;
 
     // fill buffer
@@ -543,58 +565,57 @@ nsMultiMixedConv::OnDataAvailable(nsIReq
     char *token = nsnull;
 
     if (mProcessingHeaders) {
         // we were not able to process all the headers
         // for this "part" given the previous buffer given to 
         // us in the previous OnDataAvailable callback.
         bool done = false;
         rv = ParseHeaders(channel, cursor, bufLen, &done);
-        if (NS_FAILED(rv)) ERR_OUT
+        if (NS_FAILED(rv)) return rv;
 
         if (done) {
             mProcessingHeaders = false;
             rv = SendStart(channel);
-            if (NS_FAILED(rv)) ERR_OUT
+            if (NS_FAILED(rv)) return rv;
         }
     }
 
     PRInt32 tokenLinefeed = 1;
     while ( (token = FindToken(cursor, bufLen)) ) {
 
         if (*(token+mTokenLen+1) == '-') {
             // This was the last delimiter so we can stop processing
             rv = SendData(cursor, LengthToToken(cursor, token));
-            free(buffer);
             if (NS_FAILED(rv)) return rv;
             return SendStop(NS_OK);
         }
 
         if (!mNewPart && token > cursor) {
             // headers are processed, we're pushing data now.
             NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
             rv = SendData(cursor, LengthToToken(cursor, token));
             bufLen -= token - cursor;
-            if (NS_FAILED(rv)) ERR_OUT
+            if (NS_FAILED(rv)) return rv;
         }
         // XXX else NS_ASSERTION(token == cursor, "?");
         token += mTokenLen;
         bufLen -= mTokenLen;
         tokenLinefeed = PushOverLine(token, bufLen);
 
         if (mNewPart) {
             // parse headers
             mNewPart = false;
             cursor = token;
             bool done = false; 
             rv = ParseHeaders(channel, cursor, bufLen, &done);
-            if (NS_FAILED(rv)) ERR_OUT
+            if (NS_FAILED(rv)) return rv;
             if (done) {
                 rv = SendStart(channel);
-                if (NS_FAILED(rv)) ERR_OUT
+                if (NS_FAILED(rv)) return rv;
             }
             else {
                 // we haven't finished processing header info.
                 // we'll break out and try to process later.
                 mProcessingHeaders = true;
                 break;
             }
         }
@@ -604,17 +625,17 @@ nsMultiMixedConv::OnDataAvailable(nsIReq
             mContentType.Truncate();
             mContentLength = LL_MAXUINT;
             mContentDisposition.Truncate();
             mIsByteRangeRequest = false;
             mByteRangeStart = 0;
             mByteRangeEnd = 0;
             
             rv = SendStop(NS_OK);
-            if (NS_FAILED(rv)) ERR_OUT
+            if (NS_FAILED(rv)) return rv;
             // reset the token to front. this allows us to treat
             // the token as a starting token.
             token -= mTokenLen + tokenLinefeed;
             bufLen += mTokenLen + tokenLinefeed;
             cursor = token;
         }
     }
 
@@ -634,26 +655,25 @@ nsMultiMixedConv::OnDataAvailable(nsIReq
         // have enough info to start a part, go ahead and buffer
         // enough to collect a boundary token.
         if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
             bufAmt = NS_MIN(mTokenLen - 1, bufLen);
     }
 
     if (bufAmt) {
         rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
-        if (NS_FAILED(rv)) ERR_OUT
+        if (NS_FAILED(rv)) return rv;
         bufLen -= bufAmt;
     }
 
     if (bufLen) {
         rv = SendData(cursor, bufLen);
-        if (NS_FAILED(rv)) ERR_OUT
+        if (NS_FAILED(rv)) return rv;
     }
 
-    free(buffer);
     return rv;
 }
 
 
 // nsIRequestObserver implementation
 NS_IMETHODIMP
 nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
     // we're assuming the content-type is available at this stage
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug669001.js
@@ -0,0 +1,158 @@
+do_load_httpd_js();
+
+var httpServer = null;
+var path = "/bug699001";
+var URI = "http://localhost:4444" + path;
+
+function make_channel(url) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return ios.newChannel(url, "", null);
+}
+
+var fetched;
+
+// The test loads a resource that expires in one year, has an etag and varies only by User-Agent
+// First we load it, then check we load it only from the cache w/o even checking with the server
+// Then we modify our User-Agent and try it again
+// We have to get a new content (even though with the same etag) and again on next load only from
+// cache w/o accessing the server
+// Goal is to check we've updated User-Agent request header in cache after we've got 304 response
+// from the server
+
+var tests = [
+{
+  prepare: function() { },
+  test: function(response) {
+    do_check_true(fetched);
+  }
+},
+{
+  prepare: function() { },
+  test: function(response) {
+    do_check_false(fetched);
+  }
+},
+{
+  prepare: function() {
+    setUA("A different User Agent");
+  },
+  test: function(response) {
+    do_check_true(fetched);
+  }
+},
+{
+  prepare: function() { },
+  test: function(response) {
+    do_check_false(fetched);
+  }
+},
+{
+  prepare: function() {
+    setUA("And another User Agent");
+  },
+  test: function(response) {
+    do_check_true(fetched);
+  }
+},
+{
+  prepare: function() { },
+  test: function(response) {
+    do_check_false(fetched);
+  }
+}
+];
+
+function handler(metadata, response)
+{
+  if (metadata.hasHeader("If-None-Match")) {
+    response.setStatusLine(metadata.httpVersion, 304, "Not modified");
+  }
+  else {
+    response.setStatusLine(metadata.httpVersion, 200, "OK");
+    response.setHeader("Content-Type", "text/plain");
+
+    var body = "body";
+    response.bodyOutputStream.write(body, body.length);
+  }
+
+  fetched = true;
+
+  response.setHeader("Expires", getDateString(+1));
+  response.setHeader("Cache-Control", "private");
+  response.setHeader("Vary", "User-Agent");
+  response.setHeader("ETag", "1234");
+}
+
+function run_test()
+{
+  httpServer = new nsHttpServer();
+  httpServer.registerPathHandler(path, handler);
+  httpServer.start(4444);
+
+  do_test_pending();
+
+  nextTest();
+}
+
+function nextTest()
+{
+  fetched = false;
+  tests[0].prepare();
+
+  dump("Testing with User-Agent: " + getUA() + "\n");
+  var chan = make_channel(URI);
+
+  // Give the old channel a chance to close the cache entry first.
+  // XXX This is actually a race condition that might be considered a bug...
+  do_execute_soon(function() {
+    chan.asyncOpen(new ChannelListener(checkAndShiftTest, null), null);
+  });
+}
+
+function checkAndShiftTest(request, response)
+{
+  tests[0].test(response);
+
+  tests.shift();
+  if (tests.length == 0) {
+    httpServer.stop(tearDown);
+    return;
+  }
+
+  nextTest();
+}
+