Merge m-c to b-s.
authorKyle Huey <khuey@kylehuey.com>
Tue, 31 Jan 2012 14:20:05 -0500
changeset 85842 29514d9b4216
parent 85841 fc1202d75165 (current diff)
parent 85840 974b0efab5c8 (diff)
child 85843 b506d48ef7aa
child 85876 a37beda9ef66
child 105633 ce25bca1ad68
push id21969
push userkhuey@mozilla.com
push date2012-01-31 19:26 +0000
treeherdermozilla-central@29514d9b4216 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b-s.
widget/tests/window_bug596600.xul
--- a/.hgtags
+++ b/.hgtags
@@ -68,8 +68,11 @@ 138f593553b66c9f815e8f57870c19d6347f7702
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+0000000000000000000000000000000000000000 AURORA_BASE_20120131
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -569,84 +569,42 @@ nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell)
     return;  // Already shutdown
 
   nsDocAccessibleWrap::Shutdown();
 }
 
-// nsRootAccessible protected member
-already_AddRefed<nsIDocShellTreeItem>
-nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
-{
-  if (!aStart) {
-    return nsnull;
-  }
-
-  PRInt32 itemType;
-  aStart->GetItemType(&itemType);
-  if (itemType == nsIDocShellTreeItem::typeContent) {
-    nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart);
-
-    // Hidden documents don't have accessibles (like SeaMonkey's sidebar),
-    // they are of no interest for a11y.
-    if (!accDoc)
-      return nsnull;
-
-    // If ancestor chain of accessibles is not completely visible,
-    // don't use this one. This happens for example if it's inside
-    // a background tab (tabbed browsing)
-    nsAccessible* parent = accDoc->Parent();
-    while (parent) {
-      if (parent->State() & states::INVISIBLE)
-        return nsnull;
-
-      if (parent == this)
-        break; // Don't check past original root accessible we started with
-
-      parent = parent->Parent();
-    }
-
-    NS_ADDREF(aStart);
-    return aStart;
-  }
-  nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
-  if (treeNode) {
-    PRInt32 subDocuments;
-    treeNode->GetChildCount(&subDocuments);
-    for (PRInt32 count = 0; count < subDocuments; count ++) {
-      nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
-      treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
-      NS_ENSURE_TRUE(treeItemChild, nsnull);
-      contentTreeItem = GetContentDocShell(treeItemChild);
-      if (contentTreeItem) {
-        NS_ADDREF(aStart = contentTreeItem);
-        return aStart;
-      }
-    }
-  }
-  return nsnull;
-}
-
 // nsIAccessible method
 Relation
 nsRootAccessible::RelationByType(PRUint32 aType)
 {
   if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
     return nsDocAccessibleWrap::RelationByType(aType);
 
-  nsCOMPtr<nsIDocShellTreeItem> treeItem =
-    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
-  nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
-  // there may be no content area, so we need a null check
-  if (!contentTreeItem)
-    return Relation();
+  nsIDOMWindow* rootWindow = mDocument->GetWindow();
+  if (rootWindow) {
+    nsCOMPtr<nsIDOMWindow> contentWindow;
+    rootWindow->GetContent(getter_AddRefs(contentWindow));
+    if (contentWindow) {
+      nsCOMPtr<nsIDOMDocument> contentDOMDocument;
+      contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
+      nsCOMPtr<nsIDocument> contentDocumentNode =
+        do_QueryInterface(contentDOMDocument);
+      if (contentDocumentNode) {
+        nsDocAccessible* contentDocument =
+          GetAccService()->GetDocAccessible(contentDocumentNode);
+        if (contentDocument)
+          return Relation(contentDocument);
+      }
+    }
+  }
 
-  return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem));
+  return Relation();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
 {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -122,18 +122,17 @@ protected:
 #ifdef MOZ_XUL
     void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                         nsXULTreeAccessible* aAccessible);
     void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
                                     nsXULTreeAccessible* aAccessible);
 
     PRUint32 GetChromeFlags();
 #endif
-    already_AddRefed<nsIDocShellTreeItem>
-           GetContentDocShell(nsIDocShellTreeItem *aStart);
+
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
 
 inline nsRootAccessible*
 nsAccessible::AsRoot()
 {
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -43,16 +43,17 @@ VPATH		= @srcdir@
 relativesrcdir  = accessible/relations
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 # test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
 
 _TEST_FILES =\
+		test_embeds.xul \
 		test_general.html \
 		test_general.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Embeds relation tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../relations.js"></script>
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    function tabBrowser()
+    {
+      return gBrowserWnd.gBrowser;
+    }
+
+    function currentBrowser()
+    {
+      return tabBrowser().selectedBrowser;
+    }
+
+    function currentTabDocument()
+    {
+      return currentBrowser().contentDocument;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadURI(aURI)
+    {
+      this.invoke = function loadURI_invoke()
+      {
+        tabBrowser().loadURI(aURI);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadURI_getID()
+      {
+        return "load uri " + aURI;
+      }
+    }
+
+    function loadOneTab(aURI)
+    {
+      this.invoke = function loadOneTab_invoke()
+      {
+        tabBrowser().loadOneTab(aURI, null, null, null, false);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadOneTab_getID()
+      {
+        return "load uri '" + aURI + "' in new tab";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Testing
+
+    var gBrowserWnd = null;
+    function loadBrowser()
+    {
+      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
+                                      "chrome,all,dialog=no", "about:");
+
+      addA11yLoadEvent(startTests, gBrowserWnd);
+    }
+
+    function startTests()
+    {
+      // Wait for tab load.
+      var browser = currentBrowser();
+      addA11yLoadEvent(doTests, browser.contentWindow);
+    }
+
+    //gA11yEventDumpToConsole = true; // debug
+
+    var gQueue = null;
+    function doTests()
+    {
+      testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                   getAccessible(currentTabDocument()));
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new loadURI("about:about"));
+      gQueue.push(new loadOneTab("about:mozilla"));
+
+      gQueue.onFinish = function()
+      {
+        gBrowserWnd.close();
+      }
+      gQueue.invoke();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(loadBrowser);
+  ]]>
+  </script>
+
+  <vbox flex="1" style="overflow: auto;">
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
+       title="Embeds relation on root accessible can return not content document">
+      Mozilla Bug 707654
+    </a>
+    <p id="display"></p>
+    <div id="content" style="display: none">
+    </div>
+    <pre id="test">
+    </pre>
+  </body>
+  </vbox>
+</window>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -121,29 +121,16 @@
       testRelation("caption", RELATION_LABEL_FOR, "table");
       testRelation("table", RELATION_LABELLED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
       testRelation("legend", RELATION_LABEL_FOR, "fieldset");
       testRelation("fieldset", RELATION_LABELLED_BY, "legend");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // finish test
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -98,29 +98,16 @@
       // aria-flowto, multiple relations
       testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
       // xul:caption
       var groupboxAcc = getAccessible("groupbox");
       var labelAcc = groupboxAcc.firstChild;
       testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc);
       testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc);
 
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -103,21 +103,16 @@ include $(topsrcdir)/config/config.mk
 
 ifdef _MSC_VER
 # Always enter a Windows program through wmain, whether or not we're
 # a console application.
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
 ifeq ($(OS_ARCH),WINNT)
-OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool)
-OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32)
-endif
-
-ifeq ($(OS_ARCH),WINNT)
 RCINCLUDE = splash.rc
 ifndef GNU_CC
 RCFLAGS += -DMOZ_PHOENIX -I$(srcdir)
 else
 RCFLAGS += -DMOZ_PHOENIX --include-dir $(srcdir)
 endif
 ifdef DEBUG
 RCFLAGS += -DDEBUG
--- a/browser/config/mozconfigs/linux32/release
+++ b/browser/config/mozconfigs/linux32/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/linux64/release
+++ b/browser/config/mozconfigs/linux64/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -9,8 +9,11 @@ ac_add_options --enable-official-brandin
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-12.0a1
+13.0a1
--- a/build/unix/build-toolchain/glibc-deterministic.patch
+++ b/build/unix/build-toolchain/glibc-deterministic.patch
@@ -17,16 +17,32 @@ diff -ru a/csu/Makefile b/csu/Makefile
  		     os=Linux; \
  		   fi; \
 -		   printf '"Compiled on a %s %s system on %s.\\n"\n' \
 -			  "$$os" "$$version" "`date +%Y-%m-%d`";; \
 +                   ;; \
  	   *) ;; \
  	 esac; \
  	 files="$(all-Banner-files)";				\
+diff -ru a/Makerules b/Makerules
+--- a/Makerules	2011-01-17 23:34:07.000000000 -0500
++++ b/Makerules	2012-01-30 08:47:56.565068903 -0500
+@@ -992,9 +992,9 @@
+ 	 echo '   Use the shared library, but some functions are only in';\
+ 	 echo '   the static library, so try that secondarily.  */';\
+ 	 cat $<; \
+-	 echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \
+-	      '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\
+-	      ' AS_NEEDED (' $(slibdir)/$(rtld-installed-name) ') )' \
++	 echo 'GROUP ( libc.so$(libc.so-version)' \
++	      '$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\
++	      ' AS_NEEDED (' $(rtld-installed-name) ') )' \
+ 	) > $@.new
+ 	mv -f $@.new $@
+ 
 diff -ru a/nscd/nscd_stat.c b/nscd/nscd_stat.c
 --- a/nscd/nscd_stat.c	2011-01-17 23:34:07.000000000 -0500
 +++ b/nscd/nscd_stat.c	2012-01-23 15:54:45.231607606 -0500
 @@ -38,7 +38,7 @@
  
  
  /* We use this to make sure the receiver is the same.  */
 -static const char compilation[21] = __DATE__ " " __TIME__;
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-12.0a1
+13.0a1
--- a/ipc/app/Makefile.in
+++ b/ipc/app/Makefile.in
@@ -91,21 +91,16 @@ FINAL_TARGET = $(DIST)/bin/lib
 endif
 
 ifdef _MSC_VER
 # Always enter a Windows program through wmain, whether or not we're
 # a console application.
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
-ifeq ($(OS_ARCH),WINNT)
-OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool)
-OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32)
-endif
-
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
 #
 # Control the default heap size.
 # This is the heap returned by GetProcessHeap().
 # As we use the CRT heap, the default size is too large and wastes VM.
 #
--- a/js/src/config/milestone.txt
+++ b/js/src/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-12.0a1
+13.0a1
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/722137.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html style="width: 1px">
+<head>
+<script>
+
+function boom()
+{
+  document.documentElement.offsetHeight;
+  var x = document.getElementById("x").firstChild;
+  x.data = "a" + x.data;
+}
+
+</script>
+</head>
+
+<body onload="boom();"><span id="x">
+&#x202a;&#x10871;</span></body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -342,8 +342,9 @@ load 663662-2.html
 load 665837.html
 load 668941.xhtml
 load 670226.html
 asserts(2) load 675246-1.xhtml # Bug 675713
 load 691118-1.html
 load 695861.html
 load 698335.html
 load 707098.html
+load 722137.html
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -814,22 +814,20 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
             PRInt32 newIndex = frameIndex;
             do {
             } while (++newIndex < frameCount &&
                      aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
             if (newIndex < frameCount) {
               RemoveBidiContinuation(aBpd, frame,
                                      frameIndex, newIndex, lineOffset);
             }
-          } else if (runLength == fragmentLength &&
-                     frame->GetNextSibling()) {
+          } else if (runLength == fragmentLength) {
             /*
-             * If the directional run ends at the end of the frame, and this is
-             * not the containing frame's last child, make sure that the next
-             * frame is a non-fluid continuation
+             * If the directional run ends at the end of the frame, make sure
+             * that any continuation is non-fluid
              */
             nsIFrame* next = frame->GetNextInFlow();
             if (next) {
               frame->SetNextContinuation(next);
               next->SetPrevContinuation(frame);
             }
           }
           frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -201,18 +201,26 @@ public class GeckoInputConnection
             a = b;
             b = tmp;
         }
 
         extract.selectionStart = a;
         extract.selectionEnd = b;
 
         extract.startOffset = 0;
-        extract.text = content.toString();
 
+        try {
+            extract.text = content.toString();
+        } catch (IndexOutOfBoundsException iob) {
+            Log.d(LOGTAG,
+                  "IndexOutOfBoundsException thrown from getExtractedText(). start: " +
+                  Selection.getSelectionStart(content) +
+                  " end: " + Selection.getSelectionEnd(content));
+            return null;
+        }
         return extract;
     }
 
     @Override
     public boolean setSelection(int start, int end) {
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, end - start));
 
--- a/mobile/android/base/gfx/FloatSize.java
+++ b/mobile/android/base/gfx/FloatSize.java
@@ -56,16 +56,20 @@ public class FloatSize {
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
     public String toString() { return "(" + width + "," + height + ")"; }
 
+    public boolean isPositive() {
+        return (width > 0 && height > 0);
+    }
+
     public boolean fuzzyEquals(FloatSize size) {
         return (FloatUtils.fuzzyEquals(size.width, width) &&
                 FloatUtils.fuzzyEquals(size.height, height));
     }
 
     public FloatSize scale(float factor) {
         return new FloatSize(width * factor, height * factor);
     }
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -424,18 +424,24 @@ public class GeckoSoftwareLayerClient ex
         sendResizeEventIfNecessary(false);
     }
 
     /* Informs Gecko that the screen size has changed. */
     private void sendResizeEventIfNecessary(boolean force) {
         DisplayMetrics metrics = new DisplayMetrics();
         GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
-        if (!force && metrics.widthPixels == mScreenSize.width &&
-            metrics.heightPixels == mScreenSize.height) {
+        // Return immediately if the screen size hasn't changed or the viewport
+        // size is zero (which indicates that the rendering surface hasn't been
+        // allocated yet).
+        boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width ||
+                                     metrics.heightPixels != mScreenSize.height);
+        boolean viewportSizeValid = (getLayerController() != null &&
+                                     getLayerController().getViewportSize().isPositive());
+        if (!(force || (screenSizeChanged && viewportSizeValid))) {
             return;
         }
 
         mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
         IntSize bufferSize;
         IntSize tileSize;
 
         // Round up depending on layer implementation to remove texture wastage
--- a/mobile/android/base/gfx/MultiTileLayer.java
+++ b/mobile/android/base/gfx/MultiTileLayer.java
@@ -66,16 +66,21 @@ public class MultiTileLayer extends Laye
     private final IntSize mTileSize;
     private IntSize mBufferSize;
     private Region mDirtyRegion;
     private Region mValidRegion;
     private Point mRenderOffset;
     private final LinkedList<SubTile> mTiles;
     private final HashMap<Long, SubTile> mPositionHash;
 
+    // Copies of the last set origin/resolution, to decide when to invalidate
+    // the buffer
+    private Point mOrigin;
+    private float mResolution;
+
     public MultiTileLayer(CairoImage image, IntSize tileSize) {
         super();
 
         mImage = image;
         mTileSize = tileSize;
         mBufferSize = new IntSize(0, 0);
         mDirtyRegion = new Region();
         mValidRegion = new Region();
@@ -97,17 +102,17 @@ public class MultiTileLayer extends Laye
         mDirtyRegion.union(dirtyRect);
         mValidRegion.union(dirtyRect);
     }
 
     /**
      * Invalidates the backing buffer. Data will not be uploaded from an invalid
      * backing buffer. This method is only valid inside a transaction.
      */
-    public void invalidateBuffer() {
+    protected void invalidateBuffer() {
         if (!inTransaction()) {
             throw new RuntimeException("invalidateBuffer() is only valid inside a transaction");
         }
 
         mDirtyRegion.setEmpty();
         mValidRegion.setEmpty();
     }
 
@@ -176,27 +181,26 @@ public class MultiTileLayer extends Laye
         tile.beginTransaction(null);
         try {
             if (reused) {
                 // Invalidate any area that isn't represented in the current
                 // buffer. This is done as SingleTileLayer always updates the
                 // entire width, regardless of the dirty-rect's width, and so
                 // can override existing data.
                 Point origin = getOffsetOrigin();
-                Rect validRect = tile.getValidTextureArea();
-                validRect.offset(tileOrigin.x - origin.x, tileOrigin.y - origin.y);
-                Region validRegion = new Region(validRect);
+                Region validRegion = new Region(tile.getValidTextureArea());
+                validRegion.translate(tileOrigin.x - origin.x, tileOrigin.y - origin.y);
                 validRegion.op(mValidRegion, Region.Op.INTERSECT);
 
                 // SingleTileLayer can't draw complex regions, so in that case,
                 // just invalidate the entire area.
                 tile.invalidateTexture();
-                if (!validRegion.isComplex()) {
-                    validRect.set(validRegion.getBounds());
-                    validRect.offset(origin.x - tileOrigin.x, origin.y - tileOrigin.y);
+                if (!validRegion.isEmpty() && !validRegion.isComplex()) {
+                    validRegion.translate(origin.x - tileOrigin.x, origin.y - tileOrigin.y);
+                    tile.getValidTextureArea().set(validRegion.getBounds());
                 }
             } else {
                 // Update tile metrics
                 tile.setOrigin(tileOrigin);
                 tile.setResolution(getResolution());
 
                 // Make sure that non-reused tiles are marked as invalid before
                 // uploading new content.
@@ -281,32 +285,31 @@ public class MultiTileLayer extends Laye
         // off-buffer tiles, but this is a rare case that we allow for
         // optimisation purposes.
         //
         // XXX Ideally, we want to remove this second invalidation clause
         //     somehow. It may be possible to know if off-screen tiles are
         //     valid by monitoring reflows on the browser element, or
         //     something along these lines.
         LinkedList<SubTile> invalidTiles = new LinkedList<SubTile>();
-        Rect bounds = mValidRegion.getBounds();
         for (ListIterator<SubTile> i = mTiles.listIterator(); i.hasNext();) {
             SubTile tile = i.next();
 
             if (tile.key == null) {
                 continue;
             }
 
             RectF tileBounds = tile.getBounds(context, new FloatSize(tile.getSize()));
             Rect tilespaceTileBounds =
                 RectUtils.round(RectUtils.scale(tileBounds, scaleFactor));
             tilespaceTileBounds.offset(-origin.x, -origin.y);
 
             // First bracketed clause: Invalidate off-screen, off-buffer tiles
             // Second: Invalidate visible tiles at the wrong resolution that have updates
-            if ((!Rect.intersects(bounds, tilespaceTileBounds) &&
+            if ((!opRegion.op(tilespaceTileBounds, mValidRegion, Region.Op.INTERSECT) &&
                  !Rect.intersects(tilespaceViewport, tilespaceTileBounds)) ||
                 (!FloatUtils.fuzzyEquals(tile.getResolution(), getResolution()) &&
                  opRegion.op(tilespaceTileBounds, updateRegion, Region.Op.INTERSECT))) {
                 tile.invalidateTexture();
 
                 // Add to the list of invalid tiles and remove from the main list
                 invalidTiles.add(tile);
                 i.remove();
@@ -410,43 +413,41 @@ public class MultiTileLayer extends Laye
             if (RectF.intersects(layerBounds, context.viewport)) {
                 layer.draw(context);
             }
         }
     }
 
     @Override
     public void setOrigin(Point origin) {
-        Point oldOrigin = getOrigin();
-
-        if (!origin.equals(oldOrigin)) {
+        if (mOrigin == null || !origin.equals(mOrigin)) {
+            mOrigin = origin;
             super.setOrigin(origin);
             invalidateBuffer();
         }
     }
 
     @Override
     public void setResolution(float resolution) {
-        float oldResolution = getResolution();
-
-        if (!FloatUtils.fuzzyEquals(resolution, oldResolution)) {
+        if (!FloatUtils.fuzzyEquals(resolution, mResolution)) {
+            mResolution = resolution;
             super.setResolution(resolution);
             invalidateBuffer();
         }
     }
 
     public void setRenderOffset(Point offset) {
         mRenderOffset.set(offset.x, offset.y);
     }
 
     /**
      * Invalidates all sub-tiles. This should be called if the source backing
      * this layer has changed. This method is only valid inside a transaction.
      */
-    public void invalidateTiles() {
+    protected void invalidateTiles() {
         if (!inTransaction()) {
             throw new RuntimeException("invalidateTiles() is only valid inside a transaction");
         }
 
         for (SubTile tile : mTiles) {
             // Remove tile from position hash and mark it as invalid
             if (tile.key != null) {
                 mPositionHash.remove(tile.key);
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -33,17 +33,17 @@
 # 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 *****
 
 MOZ_APP_BASENAME=Fennec
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=12.0a1
+MOZ_APP_VERSION=13.0a1
 
 MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=
 
--- a/mobile/xul/confvars.sh
+++ b/mobile/xul/confvars.sh
@@ -33,17 +33,17 @@
 # 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 *****
 
 MOZ_APP_BASENAME=Fennec
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=12.0a1
+MOZ_APP_VERSION=13.0a1
 
 MOZ_BRANDING_DIRECTORY=mobile/xul/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/xul/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=1
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -2728,65 +2728,73 @@ pref("font.name.sans-serif.el", "Droid S
 pref("font.name.monospace.el", "Droid Sans Mono");
 
 pref("font.name.serif.he", "Droid Serif");
 pref("font.name.sans-serif.he", "Droid Sans");
 pref("font.name.monospace.he", "Droid Sans Mono");
 
 pref("font.name.serif.ja", "Droid Serif");
 pref("font.name.sans-serif.ja", "Droid Sans Japanese");
-pref("font.name.monospace.ja", "Droid Sans Mono");
+pref("font.name.monospace.ja", "MotoyaLMaru");
+pref("font.name-list.sans-serif.ja", "MotoyaLMaru, MotoyaLCedar, Droid Sans Japanese");
+pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar");
 
 pref("font.name.serif.ko", "Droid Serif");
 pref("font.name.sans-serif.ko", "Droid Sans");
 pref("font.name.monospace.ko", "Droid Sans Mono");
 
 pref("font.name.serif.th", "Droid Serif");
 pref("font.name.sans-serif.th", "Droid Sans");
 pref("font.name.monospace.th", "Droid Sans Mono");
 
 pref("font.name.serif.tr", "Droid Serif");
 pref("font.name.sans-serif.tr", "Droid Sans");
 pref("font.name.monospace.tr", "Droid Sans Mono");
 
 pref("font.name.serif.x-baltic", "Droid Serif");
 pref("font.name.sans-serif.x-baltic", "Droid Sans");
 pref("font.name.monospace.x-baltic", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-baltic", "Roboto, Droid Sans");
 
 pref("font.name.serif.x-central-euro", "Droid Serif");
 pref("font.name.sans-serif.x-central-euro", "Droid Sans");
 pref("font.name.monospace.x-central-euro", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-central-euro", "Roboto, Droid Sans");
 
 pref("font.name.serif.x-cyrillic", "Droid Serif");
 pref("font.name.sans-serif.x-cyrillic", "Droid Sans");
 pref("font.name.monospace.x-cyrillic", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-cyrillic", "Roboto, Droid Sans");
 
 pref("font.name.serif.x-unicode", "Droid Serif");
 pref("font.name.sans-serif.x-unicode", "Droid Sans");
 pref("font.name.monospace.x-unicode", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-unicode", "Roboto, Droid Sans");
 
 pref("font.name.serif.x-user-def", "Droid Serif");
 pref("font.name.sans-serif.x-user-def", "Droid Sans");
 pref("font.name.monospace.x-user-def", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-user-def", "Roboto, Droid Sans");
 
 pref("font.name.serif.x-western", "Droid Serif");
 pref("font.name.sans-serif.x-western", "Droid Sans");
 pref("font.name.monospace.x-western", "Droid Sans Mono");
+pref("font.name-list.sans-serif.x-western", "Roboto, Droid Sans");
 
 pref("font.name.serif.zh-CN", "Droid Serif");
 pref("font.name.sans-serif.zh-CN", "Droid Sans");
 pref("font.name.monospace.zh-CN", "Droid Sans Mono");
 
-// ming_uni.ttf (HKSCS-2001) 
-// http://www.info.gov.hk/digital21/eng/hkscs/download/uime.exe
 pref("font.name.serif.zh-HK", "Droid Serif");
 pref("font.name.sans-serif.zh-HK", "Droid Sans");
 pref("font.name.monospace.zh-HK", "Droid Sans Mono");
 
-// zh-TW
+pref("font.name.serif.zh-TW", "Droid Serif");
+pref("font.name.sans-serif.zh-TW", "Droid Sans");
+pref("font.name.monospace.zh-TW", "Droid Sans Mono");
 
 pref("font.default.ar", "sans-serif");
 pref("font.size.variable.ar", 16);
 pref("font.size.fixed.ar", 12);
 
 pref("font.default.el", "sans-serif");
 pref("font.size.variable.el", 16);
 pref("font.size.fixed.el", 12);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -149,16 +149,18 @@ void MyLogFunction(const char *fmt, ...)
 
 #define PR_LOG(module,level,args) MyLogFunction args
 #endif
 
 nsNSSSocketInfo::nsNSSSocketInfo()
   : mMutex("nsNSSSocketInfo::nsNSSSocketInfo"),
     mFd(nsnull),
     mCertVerificationState(before_cert_verification),
+    mCertVerificationStarted(0),
+    mCertVerificationEnded(0),
     mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
     mSubRequestsHighSecurity(0),
     mSubRequestsLowSecurity(0),
     mSubRequestsBrokenSecurity(0),
     mSubRequestsNoSecurity(0),
     mErrorCode(0),
     mErrorMessageType(PlainErrorMessage),
     mForSTARTTLS(false),
@@ -955,25 +957,28 @@ void
 nsNSSSocketInfo::SetCertVerificationWaiting()
 {
   // mCertVerificationState may be before_cert_verification for the first
   // handshake on the connection, or after_cert_verification for subsequent
   // renegotiation handshakes.
   NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
                "Invalid state transition to waiting_for_cert_verification");
   mCertVerificationState = waiting_for_cert_verification;
+  mCertVerificationStarted = PR_IntervalNow();
 }
 
 void
 nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
                                            SSLErrorMessageType errorMessageType)
 {
   NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
                "Invalid state transition to cert_verification_finished");
 
+  mCertVerificationEnded = PR_IntervalNow();
+
   if (errorCode != 0) {
     SetCanceled(errorCode, errorMessageType);
   } else if (mFd) {
     // We haven't closed the connection already, so restart it
     SECStatus rv = SSL_RestartHandshakeAfterAuthCertificate(mFd);
     if (rv != SECSuccess) {
       errorCode = PR_GetError();
       if (errorCode == 0) {
@@ -1020,21 +1025,46 @@ void nsNSSSocketInfo::SetAllowTLSIntoler
 {
   mAllowTLSIntoleranceTimeout = aAllow;
 }
 
 #define HANDSHAKE_TIMEOUT_SECONDS 25
 
 bool nsNSSSocketInfo::HandshakeTimeout()
 {
+  if (mCertVerificationState == waiting_for_cert_verification) {
+    // Do not do a TLS interlerance timeout during cert verification because:
+    //
+    //  * If we would have timed out, but cert verification is still ongoing,
+    //    then the handshake probably already completed, and it is probably the
+    //    certificate validation (OCSP responder or similar) that is timing
+    //    out.
+    //  * If certificate validation AND the handshake is slow, then that is a
+    //    good indication that the network is bad, and so the problem probably
+    //    isn't the server being TLS intolerant.
+    //  * When we timeout, we return non-zero flags from PR_Poll, which will
+    //    cause the application to try to read from and/or write to the socket,
+    //    possibly in a loop. But, it is likely that the socket is blocked on
+    //    cert authentication, so those read and/or write calls would result in
+    //    PR_WOULD_BLOCK_ERROR, causing the application to spin.
+    return false;
+  }
+
   if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout)
     return false;
 
-  return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime)
-          > PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS));
+  PRIntervalTime now = PR_IntervalNow();
+  PRIntervalTime certVerificationTime =
+      mCertVerificationEnded - mCertVerificationStarted;
+  PRIntervalTime totalTime = now - mHandshakeStartTime;
+  PRIntervalTime totalTimeExceptCertVerificationTime =
+      totalTime - certVerificationTime;
+
+  return totalTimeExceptCertVerificationTime > 
+      PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS);
 }
 
 void nsSSLIOLayerHelpers::Cleanup()
 {
   if (mTLSIntolerantSites) {
     delete mTLSIntolerantSites;
     mTLSIntolerantSites = nsnull;
   }
@@ -2077,16 +2107,17 @@ nsSSLIOLayerPoll(PRFileDesc * fd, PRInt1
   }
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
          (socketInfo->IsWaitingForCertVerification()
             ?  "[%p] polling SSL socket during certificate verification using lower %d\n"
             :  "[%p] poll SSL socket using lower %d\n",
          fd, (int) in_flags));
 
+  // See comments in HandshakeTimeout before moving and/or changing this block
   if (socketInfo->HandshakeTimeout()) {
     NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
                  "caller did not poll for EXCEPT (handshake timeout)");
     *out_flags = in_flags | PR_POLL_EXCEPT;
     return in_flags;
   }
 
   // We want the handshake to continue during certificate validation, so we
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -179,16 +179,18 @@ public:
   
 
 protected:
   mutable ::mozilla::Mutex mMutex;
 
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   PRFileDesc* mFd;
   CertVerificationState mCertVerificationState;
+  PRIntervalTime mCertVerificationStarted;
+  PRIntervalTime mCertVerificationEnded;
   PRUint32 mSecurityState;
   PRInt32 mSubRequestsHighSecurity;
   PRInt32 mSubRequestsLowSecurity;
   PRInt32 mSubRequestsBrokenSecurity;
   PRInt32 mSubRequestsNoSecurity;
   nsString mShortDesc;
 
   PRErrorCode mErrorCode;
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -1950,17 +1950,31 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
 	    }
 	}
     } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
 	*p_out_flags = PR_POLL_READ;	/* it's ready already. */
 	return new_flags;
     } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
 	       (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */
 	new_flags |=  PR_POLL_WRITE;   /* also select on write. */
-    } 
+    }
+
+    if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+	ss->ssl3.hs.restartTarget != NULL) {
+	/* Read and write will block until the asynchronous callback completes
+	 * (e.g. until SSL_AuthCertificateComplete is called), so don't tell
+	 * the caller to poll the socket unless there is pending write data.
+	 */
+	if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) {
+	    new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT);
+	} else {
+	    new_flags = 0;
+	}
+    }
+
     if (new_flags && (fd->lower->methods->poll != NULL)) {
 	PRInt16    lower_out_flags = 0;
 	PRInt16    lower_new_flags;
         lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags, 
 					           &lower_out_flags);
 	if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) {
 	    PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW;
 	    if (lower_out_flags & PR_POLL_READ) 
--- a/security/patches/README
+++ b/security/patches/README
@@ -1,8 +1,9 @@
 This directory contains patches that were added locally
 on top of the NSS release.
 
 bug-542832-ssl-restart-4.patch and bug-542832-ssl-restart-tstclnt-4.patch were
 added so that we could test the new PSM SSL threading code (bug 674147) and
 SPDY (bug 528288). bug-717906-lowhash was added to fix an issue with recent
-Mozilla builds on fedora. These patches will be removed when the NSS 3.13.2
+Mozilla builds on fedora. bug-710176-ssl-restart-7-poll-v5.patch were added
+to fix a bug 710176. These patches will be removed when the NSS 3.13.2
 release that includes them is imported into mozilla-central.
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-710176-ssl-restart-7-poll-v5.patch
@@ -0,0 +1,40 @@
+# HG changeset patch
+# Parent 4560e2c22b83f85f9238b9094de7a190042676df
+# User Brian Smith <bsmith@mozilla.com>
+
+diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c
+--- a/security/nss/lib/ssl/sslsock.c
++++ b/security/nss/lib/ssl/sslsock.c
+@@ -1950,17 +1950,31 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
+ 	    }
+ 	}
+     } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
+ 	*p_out_flags = PR_POLL_READ;	/* it's ready already. */
+ 	return new_flags;
+     } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
+ 	       (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */
+ 	new_flags |=  PR_POLL_WRITE;   /* also select on write. */
+-    } 
++    }
++
++    if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
++	ss->ssl3.hs.restartTarget != NULL) {
++	/* Read and write will block until the asynchronous callback completes
++	 * (e.g. until SSL_AuthCertificateComplete is called), so don't tell
++	 * the caller to poll the socket unless there is pending write data.
++	 */
++	if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) {
++	    new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT);
++	} else {
++	    new_flags = 0;
++	}
++    }
++
+     if (new_flags && (fd->lower->methods->poll != NULL)) {
+ 	PRInt16    lower_out_flags = 0;
+ 	PRInt16    lower_new_flags;
+         lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags, 
+ 					           &lower_out_flags);
+ 	if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) {
+ 	    PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW;
+ 	    if (lower_out_flags & PR_POLL_READ) 
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -33,32 +33,36 @@
  * 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 "base/histogram.h"
+#include "base/pickle.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsCOMPtr.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsIXPConnect.h"
 #include "mozilla/Services.h"
 #include "jsapi.h" 
 #include "nsStringGlue.h"
 #include "nsITelemetry.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
 #include "Telemetry.h" 
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsBaseHashtable.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/FileUtils.h"
 
 namespace {
 
 using namespace base;
 using namespace mozilla;
 
 class TelemetryImpl : public nsITelemetry
 {
@@ -222,21 +226,19 @@ FillRanges(JSContext *cx, JSObject *arra
 
 enum reflectStatus {
   REFLECT_OK,
   REFLECT_CORRUPT,
   REFLECT_FAILURE
 };
 
 enum reflectStatus
-ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
+ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h,
+                           const Histogram::SampleSet &ss)
 {
-  Histogram::SampleSet ss;
-  h->SnapshotSample(&ss);
-
   // We don't want to reflect corrupt histograms.
   if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
     return REFLECT_CORRUPT;
   }
 
   JSObject *counts_array;
   JSObject *rarray;
   const size_t count = h->bucket_count();
@@ -255,16 +257,24 @@ ReflectHistogramSnapshot(JSContext *cx, 
   for (size_t i = 0; i < count; i++) {
     if (!JS_DefineElement(cx, counts_array, i, INT_TO_JSVAL(ss.counts(i)), NULL, NULL, JSPROP_ENUMERATE)) {
       return REFLECT_FAILURE;
     }
   }
   return REFLECT_OK;
 }
 
+enum reflectStatus
+ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
+{
+  Histogram::SampleSet ss;
+  h->SnapshotSample(&ss);
+  return ReflectHistogramAndSamples(cx, obj, h, ss);
+}
+
 JSBool
 JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp)
 {
   if (!argc) {
     JS_ReportError(cx, "Expected one argument");
     return JS_FALSE;
   }
 
@@ -672,16 +682,343 @@ TelemetryImpl::GetHistogramById(const ns
   Histogram *h;
   nsresult rv = GetHistogramByName(name, &h);
   if (NS_FAILED(rv))
     return rv;
 
   return WrapAndReturnHistogram(h, cx, ret);
 }
 
+class TelemetrySessionData : public nsITelemetrySessionData
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITELEMETRYSESSIONDATA
+
+public:
+  static nsresult LoadFromDisk(nsIFile *, TelemetrySessionData **ptr);
+  static nsresult SaveToDisk(nsIFile *, const nsACString &uuid);
+
+  TelemetrySessionData(const char *uuid);
+  ~TelemetrySessionData();
+
+private:
+  struct EnumeratorArgs {
+    JSContext *cx;
+    JSObject *snapshots;
+  };
+  typedef nsBaseHashtableET<nsUint32HashKey, Histogram::SampleSet> EntryType;
+  typedef nsTHashtable<EntryType> SessionMapType;
+  static PLDHashOperator ReflectSamples(EntryType *entry, void *arg);
+  SessionMapType mSampleSetMap;
+  nsCString mUUID;
+
+  bool DeserializeHistogramData(Pickle &pickle, void **iter);
+  static bool SerializeHistogramData(Pickle &pickle);
+
+  // The file format version.  Should be incremented whenever we change
+  // how individual SampleSets are stored in the file.
+  static const unsigned int sVersion = 1;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetrySessionData, nsITelemetrySessionData)
+
+TelemetrySessionData::TelemetrySessionData(const char *uuid)
+  : mUUID(uuid)
+{
+  mSampleSetMap.Init();
+}
+
+TelemetrySessionData::~TelemetrySessionData()
+{
+  mSampleSetMap.Clear();
+}
+
+NS_IMETHODIMP
+TelemetrySessionData::GetUuid(nsACString &uuid)
+{
+  uuid = mUUID;
+  return NS_OK;
+}
+
+PLDHashOperator
+TelemetrySessionData::ReflectSamples(EntryType *entry, void *arg)
+{
+  struct EnumeratorArgs *args = static_cast<struct EnumeratorArgs *>(arg);
+  // This has the undesirable effect of creating a histogram for the
+  // current session with the given ID.  But there's no good way to
+  // compute the ranges and buckets from scratch.
+  Histogram *h = nsnull;
+  nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h);
+  if (NS_FAILED(rv)) {
+    return PL_DHASH_STOP;
+  }
+
+  // Don't reflect histograms with no data associated with them.
+  if (entry->mData.sum() == 0) {
+    return PL_DHASH_NEXT;
+  }
+
+  JSObject *snapshot = JS_NewObject(args->cx, NULL, NULL, NULL);
+  if (!(snapshot
+        && ReflectHistogramAndSamples(args->cx, snapshot, h, entry->mData)
+        && JS_DefineProperty(args->cx, args->snapshots,
+                             h->histogram_name().c_str(),
+                             OBJECT_TO_JSVAL(snapshot), NULL, NULL,
+                             JSPROP_ENUMERATE))) {
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+TelemetrySessionData::GetSnapshots(JSContext *cx, jsval *ret)
+{
+  JSObject *snapshots = JS_NewObject(cx, NULL, NULL, NULL);
+  if (!snapshots) {
+    return NS_ERROR_FAILURE;
+  }
+
+  struct EnumeratorArgs args = { cx, snapshots };
+  PRUint32 count = mSampleSetMap.EnumerateEntries(ReflectSamples,
+                                                  static_cast<void*>(&args));
+  if (count != mSampleSetMap.Count()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *ret = OBJECT_TO_JSVAL(snapshots);
+  return NS_OK;
+}
+
+bool
+TelemetrySessionData::DeserializeHistogramData(Pickle &pickle, void **iter)
+{
+  PRUint32 count = 0;
+  if (!pickle.ReadUInt32(iter, &count)) {
+    return false;
+  }
+
+  for (size_t i = 0; i < count; ++i) {
+    int stored_length;
+    const char *name;
+    if (!pickle.ReadData(iter, &name, &stored_length)) {
+      return false;
+    }
+
+    Telemetry::ID id;
+    nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id);
+    if (NS_FAILED(rv)) {
+      // We serialized a non-static histogram.  Just drop its data on
+      // the floor.  If we can't deserialize the data, though, we're in
+      // trouble.
+      Histogram::SampleSet ss;
+      if (!ss.Deserialize(iter, pickle)) {
+        return false;
+      }
+    }
+
+    EntryType *entry = mSampleSetMap.GetEntry(id);
+    if (!entry) {
+      entry = mSampleSetMap.PutEntry(id);
+      if (NS_UNLIKELY(!entry)) {
+        return false;
+      }
+      if (!entry->mData.Deserialize(iter, pickle)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+nsresult
+TelemetrySessionData::LoadFromDisk(nsIFile *file, TelemetrySessionData **ptr)
+{
+  *ptr = nsnull;
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> f(do_QueryInterface(file, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  AutoFDClose fd;
+  rv = f->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PRInt32 size = PR_Available(fd);
+  if (size == -1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoArrayPtr<char> data(new char[size]);
+  PRInt32 amount = PR_Read(fd, data, size);
+  if (amount != size) {
+    return NS_ERROR_FAILURE;
+  }
+
+  Pickle pickle(data, size);
+  void *iter = NULL;
+
+  unsigned int storedVersion;
+  if (!(pickle.ReadUInt32(&iter, &storedVersion)
+        && storedVersion == sVersion)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const char *uuid;
+  int uuidLength;
+  if (!pickle.ReadData(&iter, &uuid, &uuidLength)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoPtr<TelemetrySessionData> sessionData(new TelemetrySessionData(uuid));
+  if (!sessionData->DeserializeHistogramData(pickle, &iter)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *ptr = sessionData.forget();
+  return NS_OK;
+}
+
+bool
+TelemetrySessionData::SerializeHistogramData(Pickle &pickle)
+{
+  StatisticsRecorder::Histograms hs;
+  StatisticsRecorder::GetHistograms(&hs);
+
+  if (!pickle.WriteUInt32(hs.size())) {
+    return false;
+  }
+
+  for (StatisticsRecorder::Histograms::const_iterator it = hs.begin();
+       it != hs.end();
+       ++it) {
+    const Histogram *h = *it;
+    const char *name = h->histogram_name().c_str();
+
+    Histogram::SampleSet ss;
+    h->SnapshotSample(&ss);
+
+    if (!(pickle.WriteData(name, strlen(name)+1)
+          && ss.Serialize(&pickle))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+nsresult
+TelemetrySessionData::SaveToDisk(nsIFile *file, const nsACString &uuid)
+{
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> f(do_QueryInterface(file, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  AutoFDClose fd;
+  rv = f->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  Pickle pickle;
+  if (!pickle.WriteUInt32(sVersion)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Include the trailing NULL for the UUID to make reading easier.
+  const char *data;
+  size_t length = uuid.GetData(&data);
+  if (!(pickle.WriteData(data, length+1)
+        && SerializeHistogramData(pickle))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PRInt32 amount = PR_Write(fd, static_cast<const char*>(pickle.data()),
+                            pickle.size());
+  if (amount != pickle.size()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+class SaveHistogramEvent : public nsRunnable
+{
+public:
+  SaveHistogramEvent(nsIFile *file, const nsACString &uuid,
+                     nsITelemetrySaveSessionDataCallback *callback)
+    : mFile(file), mUUID(uuid), mCallback(callback)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    nsresult rv = TelemetrySessionData::SaveToDisk(mFile, mUUID);
+    mCallback->Handle(!!NS_SUCCEEDED(rv));
+    return rv;
+  }
+
+private:
+  nsCOMPtr<nsIFile> mFile;
+  nsCString mUUID;
+  nsCOMPtr<nsITelemetrySaveSessionDataCallback> mCallback;
+};
+
+NS_IMETHODIMP
+TelemetryImpl::SaveHistograms(nsIFile *file, const nsACString &uuid,
+                              nsITelemetrySaveSessionDataCallback *callback,
+                              bool isSynchronous)
+{
+  nsCOMPtr<nsIRunnable> event = new SaveHistogramEvent(file, uuid, callback);
+  if (isSynchronous) {
+    return event ? event->Run() : NS_ERROR_FAILURE;
+  } else {
+    return NS_DispatchToCurrentThread(event);
+  }
+}
+
+class LoadHistogramEvent : public nsRunnable
+{
+public:
+  LoadHistogramEvent(nsIFile *file,
+                     nsITelemetryLoadSessionDataCallback *callback)
+    : mFile(file), mCallback(callback)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    TelemetrySessionData *sessionData = nsnull;
+    nsresult rv = TelemetrySessionData::LoadFromDisk(mFile, &sessionData);
+    if (NS_FAILED(rv)) {
+      mCallback->Handle(nsnull);
+    } else {
+      nsCOMPtr<nsITelemetrySessionData> data(sessionData);
+      mCallback->Handle(data);
+    }
+    return rv;
+  }
+
+private:
+  nsCOMPtr<nsIFile> mFile;
+  nsCOMPtr<nsITelemetryLoadSessionDataCallback> mCallback;
+};
+
+NS_IMETHODIMP
+TelemetryImpl::LoadHistograms(nsIFile *file,
+                              nsITelemetryLoadSessionDataCallback *callback)
+{
+  nsCOMPtr<nsIRunnable> event = new LoadHistogramEvent(file, callback);
+  return NS_DispatchToCurrentThread(event);
+}
+
 NS_IMETHODIMP
 TelemetryImpl::GetCanRecord(bool *ret) {
   *ret = mCanRecord;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelemetryImpl::SetCanRecord(bool canRecord) {
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -37,16 +37,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
 
 // When modifying the payload in incompatible ways, please bump this version number
 const PAYLOAD_VERSION = 1;
 
 const PREF_SERVER = "toolkit.telemetry.server";
 const PREF_ENABLED = "toolkit.telemetry.enabled";
 // Do not gather data more than once a minute
 const TELEMETRY_INTERVAL = 60000;
@@ -174,26 +175,29 @@ function getUpdateChannel() {
 }
 
 function TelemetryPing() {}
 
 TelemetryPing.prototype = {
   _histograms: {},
   _initialized: false,
   _prevValues: {},
+  // Generate a unique id once per session so the server can cope with
+  // duplicate submissions.
+  _uuid: generateUUID(),
+  _prevSession: null,
 
   /**
    * Returns a set of histograms that can be converted into JSON
    * @return a snapshot of the histograms of form:
    *  { histogram_name: {range:[minvalue,maxvalue], bucket_count:<number of buckets>,
    *    histogram_type: <0 for exponential, 1 for linear>, bucketX:countX, ....} ...}
    * where bucket[XY], count[XY] are positive integers.
    */
-  getHistograms: function getHistograms() {
-    let hls = Telemetry.histogramSnapshots;
+  getHistograms: function getHistograms(hls) {
     let info = Telemetry.registeredHistograms;
     let ret = {};
 
     function processHistogram(name, hgram) {
       let r = hgram.ranges;;
       let c = hgram.counts;
       let retgram = {
         range: [r[1], r[r.length - 1]],
@@ -392,58 +396,91 @@ TelemetryPing.prototype = {
   },
 
   /**
    * Send data to the server. Record success/send-time in histograms
    */
   send: function send(reason, server) {
     // populate histograms one last time
     this.gatherMemory();
+    let data = this.getSessionPayloadAndSlug(reason);
+
+    // Don't record a successful ping for previous session data.
+    this.doPing(server, data.slug, data.payload, !data.previous);
+    this._prevSession = null;
+
+    // We were sending off data from before; now send the actual data
+    // we've collected this session.
+    if (data.previous) {
+      data = this.getSessionPayloadAndSlug(reason);
+      this.doPing(server, data.slug, data.payload, true);
+    }
+  },
+
+  getSessionPayloadAndSlug: function getSessionPayloadAndSlug(reason) {
+    // Use a deterministic url for testing.
+    let isTestPing = (reason == "test-ping");
+    let havePreviousSession = !!this._prevSession;
+    let slug = (isTestPing
+                ? reason
+                : (havePreviousSession
+                   ? this._prevSession.uuid
+                   : this._uuid));
     let payloadObj = {
       ver: PAYLOAD_VERSION,
-      info: this.getMetadata(reason),
-      simpleMeasurements: getSimpleMeasurements(),
-      histograms: this.getHistograms(),
-      slowSQL: Telemetry.slowSQL
+      // Send a different reason string for previous session data.
+      info: this.getMetadata(havePreviousSession ? "saved-session" : reason),
     };
+    if (havePreviousSession) {
+      payloadObj.histograms = this.getHistograms(this._prevSession.snapshots);
+    }
+    else {
+      payloadObj.simpleMeasurements = getSimpleMeasurements();
+      payloadObj.histograms = this.getHistograms(Telemetry.histogramSnapshots);
+      payloadObj.slowSQL = Telemetry.slowSQL;
+    }
+    return { previous: !!havePreviousSession, slug: slug, payload: JSON.stringify(payloadObj) };
+  },
 
-    let isTestPing = (reason == "test-ping");
-    // Generate a unique id once per session so the server can cope with duplicate submissions.
-    // Use a deterministic url for testing.
-    if (!this._path)
-      this._path = "/submit/telemetry/" + (isTestPing ? reason : generateUUID());
-    
-    let hping = Telemetry.getHistogramById("TELEMETRY_PING");
-    let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
-
-    let url = server + this._path;
+  doPing: function doPing(server, slug, payload, recordSuccess) {
+    let submitPath = "/submit/telemetry/" + slug;
+    let url = server + submitPath;
     let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                   .createInstance(Ci.nsIXMLHttpRequest);
     request.mozBackgroundRequest = true;
     request.open("POST", url, true);
     request.overrideMimeType("text/plain");
     request.setRequestHeader("Content-Type", "application/json");
 
     let startTime = new Date();
+    let file = this.savedHistogramsFile();
 
     function finishRequest(channel) {
       let success = false;
       try {
         success = channel.QueryInterface(Ci.nsIHttpChannel).requestSucceeded;
       } catch(e) {
       }
-      hsuccess.add(success);
-      hping.add(new Date() - startTime);
-      if (isTestPing)
+      if (recordSuccess) {
+        let hping = Telemetry.getHistogramById("TELEMETRY_PING");
+        let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
+
+        hsuccess.add(success);
+        hping.add(new Date() - startTime);
+      }
+      if (success && file.exists()) {
+        file.remove(true);
+      }
+      if (slug == "test-ping")
         Services.obs.notifyObservers(null, "telemetry-test-xhr-complete", null);
     }
     request.addEventListener("error", function(aEvent) finishRequest(request.channel), false);
     request.addEventListener("load", function(aEvent) finishRequest(request.channel), false);
 
-    request.send(JSON.stringify(payloadObj));
+    request.send(payload);
   },
   
   attachObservers: function attachObservers() {
     if (!this._initialized)
       return;
     Services.obs.addObserver(this, "cycle-collector-begin", false);
     Services.obs.addObserver(this, "idle-daily", false);
   },
@@ -454,16 +491,35 @@ TelemetryPing.prototype = {
     Services.obs.removeObserver(this, "idle-daily");
     Services.obs.removeObserver(this, "cycle-collector-begin");
     if (this._isIdleObserver) {
       idleService.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
       this._isIdleObserver = false;
     }
   },
 
+  savedHistogramsFile: function savedHistogramsFile() {
+    let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
+    let profileFile = profileDirectory.clone();
+
+    // There's a bunch of binary data in the file, so we need to be
+    // sensitive to multiple machine types.  Use ctypes to get some
+    // discriminating information.
+    let size = ctypes.voidptr_t.size;
+    // Hack to figure out endianness.
+    let uint32_array_t = ctypes.uint32_t.array(1);
+    let array = uint32_array_t([0xdeadbeef]);
+    let uint8_array_t = ctypes.uint8_t.array(4);
+    let array_as_bytes = ctypes.cast(array, uint8_array_t);
+    let endian = (array_as_bytes[0] === 0xde) ? "big" : "little"
+    let name = "sessionHistograms.dat." + size + endian;
+    profileFile.append(name);
+    return profileFile;
+  },
+
   /**
    * Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry.
    */
   setup: function setup() {
     let enabled = false; 
     try {
       enabled = Services.prefs.getBoolPref(PREF_ENABLED);
       this._server = Services.prefs.getCharPref(PREF_SERVER);
@@ -474,39 +530,47 @@ TelemetryPing.prototype = {
       // Turn off local telemetry if telemetry is disabled.
       // This may change once about:telemetry is added.
       Telemetry.canRecord = false;
       return;
     }
     Services.obs.addObserver(this, "private-browsing", false);
     Services.obs.addObserver(this, "profile-before-change", false);
     Services.obs.addObserver(this, "sessionstore-windows-restored", false);
+    Services.obs.addObserver(this, "quit-application-granted", false);
 
     // Delay full telemetry initialization to give the browser time to
     // run various late initializers. Otherwise our gathered memory
     // footprint and other numbers would be too optimistic.
     let self = this;
     this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     let timerCallback = function() {
       self._initialized = true;
       self.attachObservers();
       self.gatherMemory();
       delete self._timer
     }
     this._timer.initWithCallback(timerCallback, TELEMETRY_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
+
+    // Load data from the previous session.
+    let loadCallback = function(data) {
+      self._prevSession = data;
+    }
+    Telemetry.loadHistograms(this.savedHistogramsFile(), loadCallback);
   },
 
   /** 
    * Remove observers to avoid leaks
    */
   uninstall: function uninstall() {
     this.detachObservers()
     Services.obs.removeObserver(this, "sessionstore-windows-restored");
     Services.obs.removeObserver(this, "profile-before-change");
     Services.obs.removeObserver(this, "private-browsing");
+    Services.obs.removeObserver(this, "quit-application-granted");
   },
 
   /**
    * This observer drives telemetry.
    */
   observe: function (aSubject, aTopic, aData) {
     // Allows to change the server for testing
     var server = this._server;
@@ -562,16 +626,21 @@ TelemetryPing.prototype = {
       // fall through
     case "idle":
       if (this._isIdleObserver) {
         idleService.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
         this._isIdleObserver = false;
       }
       this.send(aTopic == "idle" ? "idle-daily" : aTopic, server);
       break;
+    case "quit-application-granted":
+      Telemetry.saveHistograms(this.savedHistogramsFile(),
+                               this._uuid, function (success) success,
+                               /*isSynchronous=*/true);
+      break;
     }
   },
 
   classID: Components.ID("{55d6a5fa-130e-4ee6-a158-0133af3b86ba}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 };
 
 let NSGetFactory = XPCOMUtils.generateNSGetFactory([TelemetryPing]);
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -33,18 +33,49 @@
  * 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 "nsISupports.idl"
+#include "nsIFile.idl"
 
-[scriptable, uuid(db854295-478d-4de9-8211-d73ed7d81cd0)]
+[scriptable, uuid(c177b6b0-5ef1-44f5-bc67-6bcf7d2518e5)]
+interface nsITelemetrySessionData : nsISupports
+{
+  /**
+   * The UUID of our previous session.
+   */
+  readonly attribute ACString uuid;
+
+  /**
+   * An object containing a snapshot from all registered histograms that had
+   * data recorded in the previous session.
+   * { name1: data1, name2: data2, .... }
+   * where the individual dataN are as nsITelemetry.histogramSnapshots.
+   */
+  [implicit_jscontext]
+  readonly attribute jsval snapshots;
+};
+
+[scriptable, function, uuid(aff36c9d-7e4c-41ab-a9b6-53773bbca0cd)]
+interface nsITelemetryLoadSessionDataCallback : nsISupports
+{
+  void handle(in nsITelemetrySessionData data);
+};
+
+[scriptable, function, uuid(40065f26-afd2-4417-93de-c1de9adb1548)]
+interface nsITelemetrySaveSessionDataCallback : nsISupports
+{
+  void handle(in bool success);
+};
+
+[scriptable, uuid(22fc825e-288f-457e-80d5-5bb35f06d37e)]
 interface nsITelemetry : nsISupports
 {
   /**
    * Histogram types:
    * HISTOGRAM_EXPONENTIAL - buckets increase exponentially
    * HISTOGRAM_LINEAR - buckets increase linearly
    * HISTOGRAM_BOOLEAN - For storing 0/1 values
    */
@@ -123,12 +154,40 @@ interface nsITelemetry : nsISupports
    * Same as newHistogram above, but for histograms registered in TelemetryHistograms.h.
    *
    * @param id - unique identifier from TelemetryHistograms.h
    */
   [implicit_jscontext]
   jsval getHistogramById(in ACString id);
 
   /**
+   * Save persistent histograms to the given file.
+   *
+   * @param file - filename for saving
+   * @param uuid - UUID of this session
+   * @param callback - function to be caled when file writing is complete
+   * @param isSynchronous - whether the save is done synchronously.  Defaults
+   *        to asynchronous saving.
+   */
+  void saveHistograms(in nsIFile file, in ACString uuid,
+                      in nsITelemetrySaveSessionDataCallback callback,
+                      [optional] in boolean isSynchronous);
+
+  /* Reconstruct an nsITelemetryDataSession object containing histogram
+   * information from the given file; the file must have been produced
+   * via saveHistograms.  The file is read asynchronously.
+   *
+   * This method does not modify the histogram information being
+   * collected in the current session.
+   *
+   * The reconstructed object is then passed to the given callback.
+   *
+   * @param file - the file to load histogram information from
+   * @param callback - function to process histogram information
+   */
+  void loadHistograms(in nsIFile file,
+                      in nsITelemetryLoadSessionDataCallback callback);
+
+  /**
    * Set this to false to disable gathering of telemetry statistics.
    */
   attribute boolean canRecord;
 };
--- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js
+++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js
@@ -100,25 +100,66 @@ function test_privateMode() {
   Telemetry.canRecord = false;
   h.add(1);
   do_check_eq(uneval(orig), uneval(h.snapshot()));
   Telemetry.canRecord = true;
   h.add(1);
   do_check_neq(uneval(orig), uneval(h.snapshot()));
 }
 
+function generateUUID() {
+  let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+  // strip {}
+  return str.substring(1, str.length - 1);
+}
+
+// Check that we do sane things when saving to disk.
+function test_loadSave()
+{
+  let dirService = Cc["@mozilla.org/file/directory_service;1"]
+                    .getService(Ci.nsIProperties);
+  let tmpDir = dirService.get("TmpD", Ci.nsILocalFile);
+  let tmpFile = tmpDir.clone();
+  tmpFile.append("saved-histograms.dat");
+  if (tmpFile.exists()) {
+    tmpFile.remove(true);
+  }
+
+  let saveFinished = false;
+  let loadFinished = false;
+  let uuid = generateUUID();
+  let loadCallback = function(data) {
+    do_check_true(data != null);
+    do_check_eq(uuid, data.uuid);
+    loadFinished = true;
+    do_test_finished();
+  };
+  let saveCallback = function(success) {
+    do_check_true(success);
+    Telemetry.loadHistograms(tmpFile, loadCallback);
+    saveFinished = true;
+  };
+  do_test_pending();
+  Telemetry.saveHistograms(tmpFile, uuid, saveCallback);
+  do_register_cleanup(function () do_check_true(saveFinished));
+  do_register_cleanup(function () do_check_true(loadFinished));
+  do_register_cleanup(function () tmpFile.exists() && tmpFile.remove(true));
+}
+
 function run_test()
 {
   let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR]
   for each (let histogram_type in kinds) {
     let [min, max, bucket_count] = [1, INT_MAX - 1, 10]
     test_histogram(histogram_type, "test::"+histogram_type, min, max, bucket_count);
     
     const nh = Telemetry.newHistogram;
     expect_fail(function () nh("test::min", 0, max, bucket_count, histogram_type));
     expect_fail(function () nh("test::bucket_count", min, max, 1, histogram_type));
   }
 
   test_boolean_histogram();
   test_getHistogramById();
   test_getSlowSQL();
   test_privateMode();
+  test_loadSave();
+
 }
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -1057,27 +1057,24 @@ NS_IMETHODIMP nsCocoaWindow::Move(PRInt3
 
 // Position the window behind the given window
 NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
                                      nsIWidget *aWidget, bool aActivate)
 {
   return NS_OK;
 }
 
-// Note bug 278777, we need to update state when the window is unminimized
-// from the dock by users.
 NS_METHOD nsCocoaWindow::SetSizeMode(PRInt32 aMode)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  PRInt32 previousMode;
-  nsBaseWidget::GetSizeMode(&previousMode);
-
-  nsresult rv = nsBaseWidget::SetSizeMode(aMode);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // mSizeMode will be updated in DispatchSizeModeEvent, which will be called
+  // from a delegate method that handles the state change during one of the
+  // calls below.
+  nsSizeMode previousMode = mSizeMode;
 
   if (aMode == nsSizeMode_Normal) {
     if ([mWindow isMiniaturized])
       [mWindow deminiaturize:nil];
     else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
       [mWindow zoom:nil];
   }
   else if (aMode == nsSizeMode_Minimized) {
@@ -1414,18 +1411,23 @@ nsCocoaWindow::ReportMoveEvent()
   mInReportMoveEvent = false;
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
 nsCocoaWindow::DispatchSizeModeEvent()
 {
+  nsSizeMode newMode = GetWindowSizeMode(mWindow);
+  if (mSizeMode == newMode)
+    return;
+
+  mSizeMode = newMode;
   nsSizeModeEvent event(true, NS_SIZEMODE, this);
-  event.mSizeMode = GetWindowSizeMode(mWindow);
+  event.mSizeMode = mSizeMode;
   event.time = PR_IntervalNow();
 
   nsEventStatus status = nsEventStatus_eIgnore;
   DispatchEvent(&event, status);
 }
 
 void
 nsCocoaWindow::ReportSizeEvent()
--- a/widget/tests/Makefile.in
+++ b/widget/tests/Makefile.in
@@ -82,16 +82,18 @@ include $(topsrcdir)/config/rules.mk
 		window_wheeltransaction.xul \
 		test_imestate.html \
 		window_imestate_iframes.html \
 		test_plugin_scroll_consistency.html \
 		test_composition_text_querycontent.xul \
 		window_composition_text_querycontent.xul \
 		test_input_events_on_deactive_window.xul \
 		test_position_on_resize.xul \
+		empty_window.xul \
+		test_sizemode_events.xul \
 		$(NULL)
 		
 # test_bug413277.html mac-only based on 604789, 605178
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _CHROME_FILES += native_menus_window.xul \
                test_native_menus.xul \
                native_mouse_mac_window.xul \
@@ -104,17 +106,16 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
                window_bug522217.xul \
                test_platform_colors.xul \
                test_standalone_native_menu.xul \
                standalone_native_menu_window.xul \
                test_bug586713.xul \
                bug586713_window.xul \
                test_key_event_counts.xul \
                test_bug596600.xul \
-               window_bug596600.xul \
                test_bug673301.xul \
                $(NULL)
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 _CHROME_FILES  += taskbar_previews.xul \
 		window_state_windows.xul \
 		taskbar_progress.xul \
rename from widget/tests/window_bug596600.xul
rename to widget/tests/empty_window.xul
--- a/widget/tests/window_bug596600.xul
+++ b/widget/tests/empty_window.xul
@@ -1,4 +1,4 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<window title="Window for Test for Mozilla Bug 596600"
+<window title="Empty window"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
--- a/widget/tests/test_bug596600.xul
+++ b/widget/tests/test_bug596600.xul
@@ -31,19 +31,19 @@ var gExpectedEvents = [];
 function moveMouseTo(x, y, andThen) {
   var utils = gLeftWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                                  getInterface(Components.interfaces.nsIDOMWindowUtils);
   utils.sendNativeMouseEvent(x, y, NSMouseMoved, 0, gLeftWindow.documentElement);
   SimpleTest.executeSoon(andThen);
 }
 
 function openWindows() {
-  gLeftWindow = open('window_bug596600.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200');
+  gLeftWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200');
   SimpleTest.waitForFocus(function () {
-    gRightWindow = open('window_bug596600.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200');
+    gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200');
     SimpleTest.waitForFocus(attachIFrameToRightWindow, gRightWindow);
   }, gLeftWindow);
 }
 
 function attachIFrameToRightWindow() {
   gIFrame = gLeftWindow.document.createElementNS(XUL_NS, "iframe");
   gIFrame.setAttribute("type", "content");
   gIFrame.setAttribute("clickthrough", "never");
new file mode 100644
--- /dev/null
+++ b/widget/tests/test_sizemode_events.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="Test for bug 715867"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for bug 715867</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body  xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+gWindow = null;
+
+gSizeModeDidChange = false;
+gSizeModeDidChangeTo = 0;
+
+function sizemodeChanged(e) {
+  gSizeModeDidChange = true;
+  gSizeModeDidChangeTo = gWindow.windowState;
+}
+
+function expectSizeModeChange(newMode, duringActionCallback) {
+  gSizeModeDidChange = false;
+
+  duringActionCallback();
+
+  if (newMode == 0) {
+    // No change should have taken place, no event should have fired.
+    ok(!gSizeModeDidChange, "No sizemodechange event should have fired.");
+  } else {
+    // Size mode change event was expected to fire.
+    ok(gSizeModeDidChange, "A sizemodechanged event should have fired.");
+    is(gSizeModeDidChangeTo, newMode, "The new sizemode should have the expected value.");
+  }
+}
+
+function startTest() {
+  if (navigator.platform.indexOf("Lin") != -1) {
+    ok(true, "This test is disabled on Linux because it expects window sizemode changes to be synchronous (which is not the case on Linux).");
+    SimpleTest.finish();
+    return;
+  };
+  openWindow();
+}
+
+function openWindow() {
+  gWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200,resizable');
+  SimpleTest.waitForFocus(runTest, gWindow);
+}
+
+function runTest() {
+  // Install event handler.
+  gWindow.addEventListener("sizemodechange", sizemodeChanged, false);
+
+  // Run tests.
+  expectSizeModeChange(gWindow.STATE_MINIMIZED, function () {
+    gWindow.minimize();
+  });
+
+  expectSizeModeChange(gWindow.STATE_NORMAL, function () {
+    gWindow.restore();
+  });
+
+  expectSizeModeChange(gWindow.STATE_MAXIMIZED, function () {
+    gWindow.maximize();
+  });
+
+  expectSizeModeChange(gWindow.STATE_NORMAL, function () {
+    gWindow.restore();
+  });
+
+  // Normal window resizing shouldn't fire a sizemodechanged event, bug 715867.
+  expectSizeModeChange(0, function () {
+    gWindow.resizeTo(gWindow.outerWidth + 10, gWindow.outerHeight);
+  });
+
+  expectSizeModeChange(0, function () {
+    gWindow.resizeTo(gWindow.outerWidth, gWindow.outerHeight + 10);
+  });
+
+  gWindow.removeEventListener("sizemodechange", sizemodeChanged, false);
+  gWindow.close();
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(startTest);
+
+]]>
+</script>
+
+</window>
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -140,50 +140,65 @@ static PRInt64 GetResident()
 #elif defined(SOLARIS)
 
 #include <procfs.h>
 #include <fcntl.h>
 #include <unistd.h>
 
 static void XMappingIter(PRInt64& Vsize, PRInt64& Resident)
 {
+    Vsize = -1;
+    Resident = -1;
     int mapfd = open("/proc/self/xmap", O_RDONLY);
     struct stat st;
-    prxmap_t *prmapp;
+    prxmap_t *prmapp = NULL;
     if (mapfd >= 0) {
         if (!fstat(mapfd, &st)) {
             int nmap = st.st_size / sizeof(prxmap_t);
-            prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
-            int n = read(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t));
-            if (n > 0) {
-                Vsize = 0;
-                Resident = 0;
-                for (int i = 0; i < n / sizeof(prxmap_t); i++) {
-                    Vsize += prmapp[i].pr_size;
-                    Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
+            while (1) {
+                // stat(2) on /proc/<pid>/xmap returns an incorrect value,
+                // prior to the release of Solaris 11.
+                // Here is a workaround for it.
+                nmap *= 2;
+                prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
+                if (!prmapp) {
+                    // out of memory
+                    break;
                 }
+                int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
+                if (n < 0) {
+                    break;
+                }
+                if (nmap >= n / sizeof (prxmap_t)) {
+                    Vsize = 0;
+                    Resident = 0;
+                    for (int i = 0; i < n / sizeof (prxmap_t); i++) {
+                        Vsize += prmapp[i].pr_size;
+                        Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
+                    }
+                    break;
+                }
+                free(prmapp);
             }
             free(prmapp);
         }
         close(mapfd);
     }
 }
 
 static PRInt64 GetVsize()
 {
-    PRInt64 Vsize = -1;
-    PRInt64 Resident = -1;
+    PRInt64 Vsize, Resident;
     XMappingIter(Vsize, Resident);
     return Vsize;
 }
 
 static PRInt64 GetResident()
 {
-    PRInt64 Vsize = -1;
-    PRInt64 Resident = -1;
+    PRInt64 Vsize, Resident;
     XMappingIter(Vsize, Resident);
     return Resident;
 }
 
 #elif defined(XP_MACOSX)
 
 #include <mach/mach_init.h>
 #include <mach/task.h>
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -48,17 +48,17 @@ namespace mozilla {
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module
 {
-  static const unsigned int kVersion = 12;
+  static const unsigned int kVersion = 13;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)
     (const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
                                          const nsIID& aIID,