merge the last green changeset on fx-team to m-c
authorTim Taubert <tim.taubert@gmx.de>
Wed, 10 Aug 2011 01:30:21 +0200
changeset 74105 2805d0fc9d91f4548e563bf78d1a1023a773c9a6
parent 74104 248aed2b337d2c9a0012f21c055fddc1cd0f851a (current diff)
parent 74101 42992432ed560519f3d1d93c6788458200daf85f (diff)
child 74106 04dfb49d3a3d3469be379c76405f18f3592ac48b
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone8.0a1
merge the last green changeset on fx-team to m-c
accessible/tests/mochitest/test_takeFocus.html
content/base/public/nsISelection2.idl
content/base/public/nsISelection3.idl
testing/tps/config.json
xpcom/glue/pldhash.c
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -165,16 +165,28 @@ nsApplicationAccessible::GroupPosition(P
 
 nsAccessible*
 nsApplicationAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                       EWhichChildAtPoint aWhichChild)
 {
   return nsnull;
 }
 
+nsAccessible*
+nsApplicationAccessible::FocusedChild()
+{
+  if (gLastFocusedNode) {
+    nsAccessible* focusedChild =
+      GetAccService()->GetAccessible(gLastFocusedNode);
+    if (focusedChild && focusedChild->Parent() == this)
+      return focusedChild;
+  }
+  return nsnull;
+}
+
 NS_IMETHODIMP
 nsApplicationAccessible::GetRelationByType(PRUint32 aRelationType,
                                            nsIAccessibleRelation **aRelation)
 {
   NS_ENSURE_ARG_POINTER(aRelation);
   *aRelation = nsnull;
   return NS_OK;
 }
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -117,18 +117,20 @@ public:
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual void Description(nsString& aDescription);
   virtual PRUint32 NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
+
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
+  virtual nsAccessible* FocusedChild();
 
   virtual void InvalidateChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
 
 protected:
--- a/accessible/src/base/nsCaretAccessible.cpp
+++ b/accessible/src/base/nsCaretAccessible.cpp
@@ -206,20 +206,20 @@ nsCaretAccessible::NotifySelectionChange
 {
   NS_ENSURE_ARG(aDOMDocument);
   NS_ENSURE_STATE(mRootAccessible);
 
   nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
   nsDocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
 
 #ifdef DEBUG_NOTIFICATIONS
-  nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSelection));
+  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
 
   PRInt16 type = 0;
-  sel2->GetType(&type);
+  privSel->GetType(&type);
 
   if (type == nsISelectionController::SELECTION_NORMAL ||
       type == nsISelectionController::SELECTION_SPELLCHECK) {
 
     bool isNormalSelection =
       (type == nsISelectionController::SELECTION_NORMAL);
 
     bool isIgnored = !document || !document->IsContentLoaded();
@@ -240,20 +240,20 @@ nsCaretAccessible::NotifySelectionChange
   }
 
   return NS_OK;
 }
 
 void
 nsCaretAccessible::ProcessSelectionChanged(nsISelection* aSelection)
 {
-  nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSelection));
+  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
 
   PRInt16 type = 0;
-  sel2->GetType(&type);
+  privSel->GetType(&type);
 
   if (type == nsISelectionController::SELECTION_NORMAL)
     NormalSelectionChanged(aSelection);
 
   else if (type == nsISelectionController::SELECTION_SPELLCHECK)
     SpellcheckSelectionChanged(aSelection);
 }
 
--- a/accessible/src/base/nsCaretAccessible.h
+++ b/accessible/src/base/nsCaretAccessible.h
@@ -37,17 +37,16 @@
 
 #ifndef __nsCaretAccessible_h__
 #define __nsCaretAccessible_h__
 
 #include "NotificationController.h"
 #include "nsHyperTextAccessible.h"
 
 #include "nsISelectionListener.h"
-#include "nsISelection2.h"
 
 class nsRootAccessible;
 
 /*
  * This special accessibility class is for the caret, which is really the currently focused selection.
  * There is only 1 visible caret per top level window (nsRootAccessible),
  * However, there may be several visible selections.
  *
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -51,17 +51,17 @@
 #include "nsIDOMXULElement.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsEventListenerManager.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIScrollableFrame.h"
 #include "nsEventStateManager.h"
-#include "nsISelection2.h"
+#include "nsISelectionPrivate.h"
 #include "nsISelectionController.h"
 #include "nsPIDOMWindow.h"
 #include "nsGUIEvent.h"
 #include "nsIView.h"
 #include "nsLayoutUtils.h"
 
 #include "nsContentCID.h"
 #include "nsComponentManagerUtils.h"
@@ -326,30 +326,28 @@ nsCoreUtils::ScrollSubstringTo(nsIFrame 
 
   nsCOMPtr<nsISelectionController> selCon;
   aFrame->GetSelectionController(presContext, getter_AddRefs(selCon));
   NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
 
   scrollToRange->SetStart(aStartNode, aStartIndex);
   scrollToRange->SetEnd(aEndNode, aEndIndex);
 
-  nsCOMPtr<nsISelection> selection1;
+  nsCOMPtr<nsISelection> selection;
   selCon->GetSelection(nsISelectionController::SELECTION_ACCESSIBILITY,
-                       getter_AddRefs(selection1));
+                       getter_AddRefs(selection));
 
-  nsCOMPtr<nsISelection2> selection(do_QueryInterface(selection1));
-  if (selection) {
-    selection->RemoveAllRanges();
-    selection->AddRange(scrollToRange);
+  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(selection));
+  selection->RemoveAllRanges();
+  selection->AddRange(scrollToRange);
 
-    selection->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION,
-                              PR_TRUE, aVPercent, aHPercent);
+  privSel->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION,
+                          PR_TRUE, aVPercent, aHPercent);
 
-    selection->CollapseToStart();
-  }
+  selection->CollapseToStart();
 
   return NS_OK;
 }
 
 void
 nsCoreUtils::ScrollFrameToPoint(nsIFrame *aScrollableFrame,
                                 nsIFrame *aFrame,
                                 const nsIntPoint& aPoint)
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -45,17 +45,16 @@
 #include "nsAccUtils.h"
 #include "nsDocAccessible.h"
 #include "nsRelUtils.h"
 #include "nsTextEquivUtils.h"
 
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
-#include "nsISelection2.h"
 #include "nsISelectionPrivate.h"
 #include "nsINameSpaceManager.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLTableCellElement.h"
 #include "nsIDOMHTMLTableElement.h"
 #include "nsIDOMHTMLTableRowElement.h"
 #include "nsIDOMHTMLTableSectionElem.h"
 #include "nsIDocument.h"
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -55,17 +55,16 @@
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsFrameSelection.h"
 #include "nsILineIterator.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPlaintextEditor.h"
 #include "nsIScrollableFrame.h"
-#include "nsISelection2.h"
 #include "nsISelectionPrivate.h"
 #include "nsIServiceManager.h"
 #include "nsTextFragment.h"
 #include "gfxSkipChars.h"
 
 using namespace mozilla::a11y;
 
 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
@@ -1786,30 +1785,29 @@ nsHyperTextAccessible::GetSelections(PRI
   if (aSelCon) {
     NS_ADDREF(*aSelCon = selCon);
   }
   if (aDomSel) {
     NS_ADDREF(*aDomSel = domSel);
   }
 
   if (aRanges) {
-    nsCOMPtr<nsISelection2> selection2(do_QueryInterface(domSel));
-    NS_ENSURE_TRUE(selection2, NS_ERROR_FAILURE);
+    nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
 
     nsCOMPtr<nsINode> startNode = GetNode();
     if (peditor) {
       nsCOMPtr<nsIDOMElement> editorRoot;
       editor->GetRootElement(getter_AddRefs(editorRoot));
       startNode = do_QueryInterface(editorRoot);
     }
     NS_ENSURE_STATE(startNode);
 
     PRUint32 childCount = startNode->GetChildCount();
     nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(startNode));
-    nsresult rv = selection2->
+    nsresult rv = privSel->
       GetRangesForIntervalCOMArray(startDOMNode, 0, startDOMNode, childCount,
                                    PR_TRUE, aRanges);
     NS_ENSURE_SUCCESS(rv, rv);
     // Remove collapsed ranges
     PRInt32 numRanges = aRanges->Count();
     for (PRInt32 count = 0; count < numRanges; count ++) {
       PRBool isCollapsed;
       (*aRanges)[count]->GetCollapsed(&isCollapsed);
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -42,16 +42,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible
 
 DIRS	= \
   actions \
   attributes \
   editabletext \
   events \
+  focus \
   hyperlink \
   hypertext \
   name \
   relations \
   selectable \
   states \
   table \
   text \
@@ -98,17 +99,16 @@ include $(topsrcdir)/config/rules.mk
 		test_elm_nsApplicationAcc.html \
 		test_elm_plugin.html \
  		test_nsIAccessible_selects.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleImage.html \
 		test_nsIAccessNode_utils.html \
 		test_nsOuterDocAccessible.html \
 		test_role_nsHyperTextAcc.html \
-		test_takeFocus.html \
 		test_text_caret.html \
 		test_textboxes.html \
 		test_textboxes.xul \
 		testTextboxes.js \
 		text.js \
 		treeview.css \
 		treeview.js \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/focus/Makefile.in
@@ -0,0 +1,54 @@
+#
+# ***** 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
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = accessible/focus
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =\
+		test_focusedChild.html \
+		test_takeFocus.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/focus/test_focusedChild.html
@@ -0,0 +1,85 @@
+<html>
+
+<head>
+  <title>nsIAccessible::focusedChild testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <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="../common.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function openWnd()
+    {
+      this.eventSeq = [ new invokerChecker(EVENT_FOCUS,
+                                           getDialogAccessible,
+                                           this) ];
+
+      this.invoke = function openWnd_invoke()
+      {
+        this.dialog = window.openDialog("about:mozilla",
+                                        "AboutMozilla",
+                                        "chrome,width=600,height=600");
+      }
+
+      this.finalCheck = function openWnd_finalCheck()
+      {
+        var app = getApplicationAccessible();
+        is(app.focusedChild, getDialogAccessible(this),
+           "Wrong focused child");
+
+        this.dialog.close();
+      }
+
+      this.getID = function openWnd_getID()
+      {
+        return "focusedChild for application accessible";
+      }
+
+      function getDialogAccessible(aInvoker)
+      {
+        return getAccessible(aInvoker.dialog.document);
+      }
+    }
+
+    //gA11yEventDumpToConsole = true;
+    var gQueue = null;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new openWnd());
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=677467"
+     title="focusedChild crashes on application accessible">
+    Mozilla Bug 677467
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+</body>
+</html>
rename from accessible/tests/mochitest/test_takeFocus.html
rename to accessible/tests/mochitest/focus/test_takeFocus.html
--- a/accessible/tests/mochitest/test_takeFocus.html
+++ b/accessible/tests/mochitest/focus/test_takeFocus.html
@@ -5,22 +5,23 @@
 
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <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="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="states.js"></script>
+          src="../states.js"></script>
   <script type="application/javascript"
-          src="events.js"></script>
+          src="../events.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     var gQueue = null;
 
     function takeFocusInvoker(aID)
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -69,17 +69,17 @@
           }
         },
 
         tabBrowser: tabBrowser,
         reorderCnt: 0
       };
       registerA11yEventListener(EVENT_REORDER, handler);
 
-      tabBrowser.loadTabs(["about:", "about:mozilla"], false, true);
+      tabBrowser.loadTabs(docURIs, false, true);
     }
 
     function testRelations()
     {
       //////////////////////////////////////////////////////////////////////////
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
 
       var tabs = getNode("tabbrowser").tabContainer.childNodes;
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -70,43 +70,52 @@ endif
 
 CSRCS := \
   inject/$(CPU).c \
   inject/$(CPU)-noinit.c \
   $(NULL)
 
 libs:: $(CSRCS:.c=.$(OBJ_SUFFIX))
 
-ifndef CROSS_COMPILE
+WRAP_MALLOC_CFLAGS=
+WRAP_MALLOC_LIB=
+
 test$(DLL_SUFFIX): test.$(OBJ_SUFFIX) elfhack $(CSRCS:.c=.$(OBJ_SUFFIX))
 	$(MKSHLIB) $(LDFLAGS) $<
 	@echo ===
 	@echo === If you get failures below, please file a bug describing the error
 	@echo === and your environment \(compiler and linker versions\), and use
 	@echo === --disable-elf-hack until this is fixed.
 	@echo ===
 	@rm -f $@.bak
 	$(CURDIR)/elfhack -b $@
 	# Fail if the backup file doesn't exist
 	[ -f "$@.bak" ]
 	# Fail if the new library doesn't contain less relocations
 	[ $$(objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
 
 .PRECIOUS: test$(DLL_SUFFIX)
 
+CSRCS += test.c
+
+GARBAGE += test$(DLL_SUFFIX) test$(DLL_SUFFIX).bak
+
+libs:: test$(DLL_SUFFIX)
+
+ifndef CROSS_COMPILE
 dummy: dummy.$(OBJ_SUFFIX) test$(DLL_SUFFIX)
 	$(CC) -o $@ $^ $(LDFLAGS)
 
 libs:: dummy
 	# Will either crash or return exit code 1 if elfhack is broken
 	LD_LIBRARY_PATH=$(CURDIR) $(CURDIR)/dummy
 
-CSRCS += test.c dummy.c
+CSRCS += dummy.c
 
-GARBAGE += dummy test$(DLL_SUFFIX) test$(DLL_SUFFIX).bak
+GARBAGE += dummy
 endif
 
 inject:
 	$(NSINSTALL) -D $@
 
 inject/%.c: inject.c | inject
 	cp $< $@
 
--- a/config/config.mk
+++ b/config/config.mk
@@ -40,19 +40,19 @@
 # config.mk
 #
 # Determines the platform and builds the macros needed to load the
 # appropriate platform-specific .mk file, then defines all (most?)
 # of the generic macros.
 #
 
 # Define an include-at-most-once flag
-#ifdef INCLUDED_CONFIG_MK
-#$(error Don't include config.mk twice!)
-#endif
+ifdef INCLUDED_CONFIG_MK
+$(error Don't include config.mk twice!)
+endif
 INCLUDED_CONFIG_MK = 1
 
 EXIT_ON_ERROR = set -e; # Shell loops continue past errors without this.
 
 ifndef topsrcdir
 topsrcdir	= $(DEPTH)
 endif
 
--- a/configure.in
+++ b/configure.in
@@ -338,17 +338,17 @@ case "$target" in
     elif test "$target" != "arm-android-eabi"; then
        dnl fail if we're not building with NDKr4
        AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
     fi
 
     CPPFLAGS="-I$android_platform/usr/include $STLPORT_CPPFLAGS $CPPFLAGS"
     CFLAGS="-mandroid -I$android_platform/usr/include -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -I$android_platform/usr/include -fno-short-enums -fno-exceptions $CXXFLAGS"
-    LIBS="$LIBS $STLPORT_LIBS -static-libstdc++"
+    LIBS="$LIBS $STLPORT_LIBS"
 
     dnl Add -llog by default, since we use it all over the place.
     dnl Add --allow-shlib-undefined, because libGLESv2 links to an
     dnl undefined symbol (present on the hardware, just not in the
     dnl NDK.)
     LDFLAGS="-mandroid $STLPORT_LDFLAGS -L$android_platform/usr/lib -Wl,-rpath-link=$android_platform/usr/lib --sysroot=$android_platform -llog -Wl,--allow-shlib-undefined $LDFLAGS"
 
     dnl prevent cross compile section from using these flags as host flags
@@ -361,18 +361,18 @@ case "$target" in
     if test -z "$HOST_CXXFLAGS" ; then
         HOST_CXXFLAGS=" "
     fi
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
 
     ANDROID_NDK="${android_ndk}"
-    ANDROID_TOOLCHAIN="{android_toolchain}"
-    ANDROID_PLATFORM="{android_platform}"
+    ANDROID_TOOLCHAIN="${android_toolchain}"
+    ANDROID_PLATFORM="${android_platform}"
     ANDROID_SDK="${android_sdk}"
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
     ANDROID_VERSION="${android_version}"
     if test -z "$ANDROID_PACKAGE_NAME" ; then
         ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)'
     fi
 
     AC_DEFINE(ANDROID)
@@ -7263,16 +7263,48 @@ MOZ_ARG_ENABLE_STRING(debug-symbols,
   MOZ_DEBUG_SYMBOLS=1)
 
 if test -n "$MOZ_DEBUG" -o -n "$MOZ_DEBUG_SYMBOLS"; then
     AC_DEFINE(MOZ_DEBUG_SYMBOLS)
     export MOZ_DEBUG_SYMBOLS
 fi
 
 dnl ========================================================
+dnl = Identical Code Folding
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(icf,
+[  --disable-icf          Disable Identical Code Folding],
+    MOZ_DISABLE_ICF=1,
+    MOZ_DISABLE_ICF= )
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
+    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
+        LD_SUPPORTS_ICF,
+        [echo 'int foo() {return 42;}' \
+              'int bar() {return 42;}' \
+              'int main() {return foo() - bar();}' > conftest.${ac_ext}
+        # If the linker supports ICF, foo and bar symbols will have
+        # the same address
+        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} &&
+           objdump -t conftest${ac_exeext} | awk '{a[[$6]] = $1} END {if (a[["foo"]] && (a[["foo"]] != a[["bar"]])) { exit 1 }}'; then
+            LD_SUPPORTS_ICF=yes
+        else
+            LD_SUPPORTS_ICF=no
+        fi
+        rm -rf conftest*])
+    if test "$LD_SUPPORTS_ICF" = yes; then
+        LDFLAGS="$LDFLAGS -Wl,--icf=safe"
+        CFLAGS="$CFLAGS -ffunction-sections"
+        CXXFLAGS="$CXXFLAGS -ffunction-sections"
+    fi
+fi
+
+dnl ========================================================
 dnl = Automatically remove dead symbols
 dnl ========================================================
 
 if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
    dnl See bug 670659
    AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
        GC_SECTIONS_BREAKS_DEBUG_RANGES,
        [echo 'int foo() {return 42;}' \
@@ -7289,18 +7321,26 @@ if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -
             fi
         else
              dnl We really don't expect to get here, but just in case
              AC_ERROR([couldn't compile a simple C file])
         fi
         rm -rf conftest*])
     if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
         DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
-        CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
-        CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+        case "$CFLAGS" in
+        *-ffunction-sections*)
+            CFLAGS="$CFLAGS -fdata-sections"
+            CXXFLAGS="$CXXFLAGS -fdata-sections"
+            ;;
+        *)
+            CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
+            CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+            ;;
+        esac
     fi
 fi
 
 dnl ========================================================
 dnl = Disable any treating of compile warnings as errors
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(warnings-as-errors,
 [  --disable-warnings-as-errors
@@ -7854,17 +7894,17 @@ dnl Omnijar packaging is compatible with
 dnl In unpackaged builds, omnijar looks for files as if
 dnl things were flat packaged. After packaging, all files
 dnl are loaded from a single jar. MOZ_CHROME_FILE_FORMAT
 dnl is set to flat since putting files into jars is only
 dnl done during packaging with omnijar.
 if test "$MOZ_CHROME_FILE_FORMAT" = "omni"; then
     MOZ_OMNIJAR=1
     AC_DEFINE(MOZ_OMNIJAR)
-    if test "$OS_ARCH" = "WINNT"; then
+    if test "$OS_ARCH" = "WINNT" -o "$OS_ARCH" = "OS2"; then
         MOZ_CHROME_FILE_FORMAT=flat
     else
         MOZ_CHROME_FILE_FORMAT=symlink
     fi
 elif test "$MOZ_CHROME_FILE_FORMAT" = "jar"; then
     AC_DEFINE(MOZ_CHROME_FILE_FORMAT_JAR)
 fi
 
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -103,18 +103,16 @@ XPIDLSRCS	= \
 		nsIDOMFile.idl \
 		nsIDOMFileReader.idl \
 		nsIDOMFileList.idl \
 		nsIDOMFileException.idl \
 		nsIDOMFileError.idl \
 		nsIDOMFormData.idl \
 		nsIDOMParser.idl \
 		nsIDOMSerializer.idl \
-		nsISelection2.idl \
-		nsISelection3.idl \
 		nsISelectionController.idl  \
 		nsISelectionDisplay.idl  \
 		nsISelectionListener.idl  \
 		nsISelectionPrivate.idl  \
 		nsIScriptLoaderObserver.idl  \
 		nsIDroppedLinkHandler.idl \
 		nsIImageLoadingContent.idl \
 		nsIObjectLoadingContent.idl \
--- a/content/base/public/nsISelection.idl
+++ b/content/base/public/nsISelection.idl
@@ -44,17 +44,17 @@ interface nsIDOMRange;
 
 /**
  * Interface for manipulating and querying the current selected range
  * of nodes within the document.
  *
  * @version 1.0
  */
 
-[scriptable, uuid(B2C7ED59-8634-4352-9E37-5484C8B6E4E1)]
+[scriptable, uuid(5ac0cd5d-3c08-4c4c-8e70-230c433f5d5c)]
 interface nsISelection : nsISupports
 {
     /**
      * Returns the node in which the selection begins.
      */
     readonly attribute nsIDOMNode anchorNode;
 
     /**
@@ -160,9 +160,30 @@ interface nsISelection : nsISupports
      *                PR_FALSE if the new language is left-to-right.
      */
     void selectionLanguageChange(in boolean langRTL);
 
     /**
      * Returns the whole selection into a plain text string.
      */
     wstring toString();
+    
+    /**
+     * Modifies the selection.  Note that the parameters are case-insensitive.
+     *
+     * @param alter can be one of { "move", "extend" }
+     *   - "move" collapses the selection to the end of the selection and
+     *      applies the movement direction/granularity to the collapsed
+     *      selection.
+     *   - "extend" leaves the start of the selection unchanged, and applies
+     *      movement direction/granularity to the end of the selection.
+     * @param direction can be one of { "forward", "backward", "left", "right" }
+     * @param granularity can be one of { "character", "word",
+     *                                    "line", "lineboundary" }
+     *
+     * @returns NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
+     * "sentenceboundary", "paragraph", "paragraphboundary", or
+     * "documentboundary".  Returns NS_ERROR_INVALID_ARG if alter, direction,
+     * or granularity has an unrecognized value.
+     */
+    void modify(in DOMString alter, in DOMString direction,
+                in DOMString granularity);
 };
deleted file mode 100644
--- a/content/base/public/nsISelection2.idl
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** 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 Selection code.
- *
- * The Initial Developer of the Original Code is
- * Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Brett Wilson <brettw@gmail.com>
- *   Alexander Surkov <surkov.alexander@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
-
-#include "nsISelection.idl"
-
-interface nsIDOMNode;
-interface nsIDOMRange;
-
-%{C++
-#include "nsCOMArray.h"
-%}
-
-[ptr] native RangeArray(nsCOMArray<nsIDOMRange>);
-
-[scriptable, uuid(5d21d5fe-3691-4716-a334-4691eea54d29)]
-interface nsISelection2 : nsISelection
-{
-  /**
-   * Returns the type of the selection (see nsISelectionController for
-   * available constants).
-   */
-  readonly attribute short type;
-
-  /**
-   * Return array of ranges intersecting with the given DOM interval.
-   */
-  void GetRangesForInterval(
-      in nsIDOMNode beginNode, in PRInt32 beginOffset,
-      in nsIDOMNode endNode, in PRInt32 endOffset,
-      in boolean allowAdjacent,
-      out PRUint32 resultCount,
-      [retval, array, size_is(resultCount)] out nsIDOMRange results);
-
-  [noscript] void GetRangesForIntervalCOMArray(
-      in nsIDOMNode beginNode, in PRInt32 beginOffset,
-      in nsIDOMNode endNode, in PRInt32 endOffset,
-      in boolean allowAdjacent,
-      in RangeArray results);
-
-  /**
-   * Scrolls a region of the selection, so that it is visible in
-   * the scrolled view.
-   *
-   * @param aRegion - the region inside the selection to scroll into view
-   *                  (see selection region constants defined in
-   *                   nsISelectionController).
-   * @param aIsSynchronous - when true, scrolls the selection into view
-   *                         before returning. If false, posts a request which
-   *                         is processed at some point after the method returns.
-   * @param aVPercent - how to align the frame vertically. A value of 0
-   *                   means the frame's upper edge is aligned with the top edge
-   *                   of the visible area. A value of 100 means the frame's
-   *                   bottom edge is aligned with the bottom edge of
-   *                   the visible area. For values in between, the point
-   *                   "aVPercent" down the frame is placed at the point
-   *                   "aVPercent" down the visible area. A value of 50 centers
-   *                   the frame vertically. A value of -1 means move
-   *                   the frame the minimum amount necessary in order for
-   *                   the entire frame to be visible vertically (if possible).
-   * @param aHPercent - how to align the frame horizontally. A value of 0
-   *                    means the frame's left edge is aligned with the left
-   *                    edge of the visible area. A value of 100 means the
-   *                    frame's right edge is aligned with the right edge of
-   *                    the visible area. For values in between, the point
-   *                    "aHPercent" across the frame is placed at the point
-   *                    "aHPercent" across the visible area. A value of 50
-   *                    centers the frame horizontally . A value of -1 means
-   *                    move the frame the minimum amount necessary in order
-   *                    for the entire frame to be visible horizontally
-   *                    (if possible).
-   */
-  void scrollIntoView(in short aRegion, in boolean aIsSynchronous,
-                      in short aVPercent, in short aHPercent);
-};
-
deleted file mode 100644
--- a/content/base/public/nsISelection3.idl
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** 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 Selection code.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Justin Lebar <justin.lebar@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
-
-#include "nsISupports.idl"
-
-[scriptable, uuid(94ac0cb4-95b4-11df-8e13-0026b9792740)]
-interface nsISelection3 : nsISupports
-{
-  /**
-   * Modifies the selection.  Note that the parameters are case-insensitive.
-   *
-   * @param alter can be one of { "move", "extend" }
-   *   - "move" collapses the selection to the end of the selection and
-   *      applies the movement direction/granularity to the collapsed
-   *      selection.
-   *   - "extend" leaves the start of the selection unchanged, and applies
-   *      movement direction/granularity to the end of the selection.
-   * @param direction can be one of { "forward", "backward", "left", "right" }
-   * @param granularity can be one of { "character", "word",
-   *                                    "line", "lineboundary" }
-   *
-   * @returns NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
-   * "sentenceboundary", "paragraph", "paragraphboundary", or
-   * "documentboundary".  Returns NS_ERROR_INVALID_ARG if alter, direction,
-   * or granularity has an unrecognized value.
-   */
-  void modify(in DOMString alter, in DOMString direction,
-              in DOMString granularity);
-
-};
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -33,39 +33,43 @@
  * 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 "nsISelectionListener.idl"
 #include "nsIEnumerator.idl"
+#include "nsISelection.idl"
 
 interface nsIDOMRange;
+interface nsIDOMNode;
 interface nsISelectionListener;
 interface nsIContent;
 
 %{C++
 class nsFrameSelection;
 class nsIFrame;
 class nsIPresShell;
 struct nsTextRangeStyle;
 struct nsPoint;
 #include "nsIFrame.h"
+#include "nsCOMArray.h"
 %}
 
 [ptr] native nsFrameSelection(nsFrameSelection);
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native nsIPresShell(nsIPresShell);
+[ptr] native RangeArray(nsCOMArray<nsIDOMRange>);
 [ref] native constTextRangeStyleRef(const nsTextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 native nsDirection(nsDirection);
 
-[scriptable, uuid(98552206-ad7a-4d2d-8ce3-b6fa2389298b)]
-interface nsISelectionPrivate : nsISupports
+[scriptable, uuid(1820a940-6203-4e27-bc94-fa81131722a4)]
+interface nsISelectionPrivate : nsISelection
  {
     const short ENDOFPRECEDINGLINE=0;
     const short STARTOFNEXTLINE=1;
     
     attribute boolean interlinePosition;
 
     /* startBatchChanges
        match this up with endbatchChanges. will stop ui updates while multiple selection methods are called
@@ -131,10 +135,67 @@ interface nsISelectionPrivate : nsISuppo
     [noscript] void setTextRangeStyle(in nsIDOMRange range,
                       in constTextRangeStyleRef textRangeStyle);
 
     /**
      * Get the direction of the selection.
      */
     [noscript, notxpcom] nsDirection getSelectionDirection();
     [noscript, notxpcom] void setSelectionDirection(in nsDirection aDirection);
+    
+    /**
+     * Returns the type of the selection (see nsISelectionController for
+     * available constants).
+     */
+    readonly attribute short type;
+
+    /**
+     * Return array of ranges intersecting with the given DOM interval.
+     */
+    void GetRangesForInterval(
+        in nsIDOMNode beginNode, in PRInt32 beginOffset,
+        in nsIDOMNode endNode, in PRInt32 endOffset,
+        in PRBool allowAdjacent,
+        out PRUint32 resultCount,
+        [retval, array, size_is(resultCount)] out nsIDOMRange results);
+
+    [noscript] void GetRangesForIntervalCOMArray(
+        in nsIDOMNode beginNode, in PRInt32 beginOffset,
+        in nsIDOMNode endNode, in PRInt32 endOffset,
+        in PRBool allowAdjacent,
+        in RangeArray results);
+
+    /**
+     * Scrolls a region of the selection, so that it is visible in
+     * the scrolled view.
+     *
+     * @param aRegion - the region inside the selection to scroll into view
+     *                  (see selection region constants defined in
+     *                   nsISelectionController).
+     * @param aIsSynchronous - when true, scrolls the selection into view
+     *                         before returning. If false, posts a request which
+     *                         is processed at some point after the method returns.
+     * @param aVPercent - how to align the frame vertically. A value of 0
+     *                   means the frame's upper edge is aligned with the top edge
+     *                   of the visible area. A value of 100 means the frame's
+     *                   bottom edge is aligned with the bottom edge of
+     *                   the visible area. For values in between, the point
+     *                   "aVPercent" down the frame is placed at the point
+     *                   "aVPercent" down the visible area. A value of 50 centers
+     *                   the frame vertically. A value of -1 means move
+     *                   the frame the minimum amount necessary in order for
+     *                   the entire frame to be visible vertically (if possible).
+     * @param aHPercent - how to align the frame horizontally. A value of 0
+     *                    means the frame's left edge is aligned with the left
+     *                    edge of the visible area. A value of 100 means the
+     *                    frame's right edge is aligned with the right edge of
+     *                    the visible area. For values in between, the point
+     *                    "aHPercent" across the frame is placed at the point
+     *                    "aHPercent" across the visible area. A value of 50
+     *                    centers the frame horizontally . A value of -1 means
+     *                    move the frame the minimum amount necessary in order
+     *                    for the entire frame to be visible horizontally
+     *                    (if possible).
+     */
+    void scrollIntoView(in short aRegion, in boolean aIsSynchronous,
+                        in short aVPercent, in short aHPercent);
 };
 
--- a/content/events/src/nsContentEventHandler.cpp
+++ b/content/events/src/nsContentEventHandler.cpp
@@ -54,17 +54,16 @@
 #include "nsIView.h"
 #include "nsIContentIterator.h"
 #include "nsTextFragment.h"
 #include "nsTextFrame.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
-#include "nsISelection2.h"
 #include "nsIMEStateManager.h"
 #include "nsIObjectFrame.h"
 
 nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
 
 /******************************************************************/
 /* nsContentEventHandler                                          */
 /******************************************************************/
@@ -1066,18 +1065,17 @@ nsContentEventHandler::OnSelectionEvent(
   PRInt32 endOffset = range->EndOffset();
   AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
   AdjustRangeForSelection(mRootContent, &endNode, &endOffset);
 
   nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
   nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
   NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
 
-  nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(mSelection);
-  NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
+  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
   selPrivate->StartBatchChanges();
 
   // Clear selection first before setting
   rv = mSelection->RemoveAllRanges();
   // Need to call EndBatchChanges at the end even if call failed
   if (NS_SUCCEEDED(rv)) {
     if (aEvent->mReversed) {
       rv = mSelection->Collapse(endDomNode, endOffset);
@@ -1091,13 +1089,13 @@ nsContentEventHandler::OnSelectionEvent(
       } else {
         rv = mSelection->Extend(endDomNode, endOffset);
       }
     }
   }
   selPrivate->EndBatchChanges();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsISelection2>(do_QueryInterface(mSelection))->ScrollIntoView(
+  selPrivate->ScrollIntoView(
       nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, -1, -1);
   aEvent->mSucceeded = PR_TRUE;
   return NS_OK;
 }
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -1129,16 +1129,29 @@ NS_IMETHODIMP nsHTMLMediaElement::SetCur
 
 /* readonly attribute double duration; */
 NS_IMETHODIMP nsHTMLMediaElement::GetDuration(double *aDuration)
 {
   *aDuration = mDecoder ? mDecoder->GetDuration() : std::numeric_limits<double>::quiet_NaN();
   return NS_OK;
 }
 
+/* readonly attribute nsIDOMHTMLTimeRanges seekable; */
+NS_IMETHODIMP nsHTMLMediaElement::GetSeekable(nsIDOMTimeRanges** aSeekable)
+{
+  nsTimeRanges* ranges = new nsTimeRanges();
+  NS_ADDREF(*aSeekable = ranges);
+
+  if (mDecoder && mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
+    mDecoder->GetSeekable(ranges);
+  }
+  return NS_OK;
+}
+
+
 /* readonly attribute boolean paused; */
 NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
 {
   *aPaused = mPaused;
 
   return NS_OK;
 }
 
@@ -1820,17 +1833,17 @@ nsresult nsHTMLMediaElement::InitializeD
 
   if (!decoder->Init(this)) {
     return NS_ERROR_FAILURE;
   }
 
   double duration = aOriginal->GetDuration();
   if (duration >= 0) {
     decoder->SetDuration(duration);
-    decoder->SetSeekable(aOriginal->GetSeekable());
+    decoder->SetSeekable(aOriginal->IsSeekable());
   }
 
   nsMediaStream* stream = originalStream->CloneData(decoder);
   if (!stream) {
     return NS_ERROR_FAILURE;
   }
 
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -42,16 +42,17 @@
 #include "nsAudioStream.h"
 #include "nsHTMLVideoElement.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsTArray.h"
 #include "VideoUtils.h"
 #include "nsBuiltinDecoder.h"
 #include "nsBuiltinDecoderStateMachine.h"
+#include "nsTimeRanges.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gBuiltinDecoderLog;
 #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
 #else
 #define LOG(type, msg)
@@ -262,23 +263,92 @@ nsresult nsBuiltinDecoder::Play()
   }
   if (mPlayState == PLAY_STATE_ENDED)
     return Seek(0);
 
   ChangeState(PLAY_STATE_PLAYING);
   return NS_OK;
 }
 
+/**
+ * Returns PR_TRUE if aValue is inside a range of aRanges, and put the range
+ * index in aIntervalIndex if it is not null.
+ * If aValue is not inside a range, PR_FALSE is returned, and aIntervalIndex, if
+ * not null, is set to the index of the range which ends immediatly before aValue
+ * (and can be -1 if aValue is before aRanges.Start(0)).
+ */
+static PRBool IsInRanges(nsTimeRanges& aRanges, double aValue, PRInt32& aIntervalIndex) {
+  PRUint32 length;
+  aRanges.GetLength(&length);
+  for (PRUint32 i = 0; i < length; i++) {
+    double start, end;
+    aRanges.Start(i, &start);
+    if (start > aValue) {
+      aIntervalIndex = i - 1;
+      return PR_FALSE;
+    }
+    aRanges.End(i, &end);
+    if (aValue <= end) {
+      aIntervalIndex = i;
+      return PR_TRUE;
+    }
+  }
+  aIntervalIndex = length - 1;
+  return PR_FALSE;
+}
+
 nsresult nsBuiltinDecoder::Seek(double aTime)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  if (aTime < 0.0)
-    return NS_ERROR_FAILURE;
+  NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
+
+  nsTimeRanges seekable;
+  nsresult res;
+  PRUint32 length = 0;
+  res = GetSeekable(&seekable);
+  NS_ENSURE_SUCCESS(res, NS_OK);
+
+  seekable.GetLength(&length);
+  if (!length) {
+    return NS_OK;
+  }
+
+  // If the position we want to seek to is not in a seekable range, we seek
+  // to the closest position in the seekable ranges instead . If two positions
+  // are equaly close, we seek to the closest position from the currentTime.
+  // See seeking spec, point 7 :
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#seeking
+  PRInt32 range = 0;
+  if (!IsInRanges(seekable, aTime, range)) {
+    if (range != -1) {
+      double leftBound, rightBound;
+      res = seekable.End(range, &leftBound);
+      NS_ENSURE_SUCCESS(res, NS_OK);
+      double distanceLeft = NS_ABS(leftBound - aTime);
+
+      double distanceRight = -1;
+      if (range + 1 < length) {
+        res = seekable.Start(range+1, &rightBound);
+        NS_ENSURE_SUCCESS(res, NS_OK);
+        distanceRight = NS_ABS(rightBound - aTime);
+      }
+
+      if (distanceLeft == distanceRight) {
+        distanceLeft = NS_ABS(leftBound - mCurrentTime);
+        distanceRight = NS_ABS(rightBound - mCurrentTime);
+      } 
+      aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
+    } else {
+      // aTime is before the first range in |seekable|, the closest point we can
+      // seek to is the start of the first range.
+      seekable.Start(0, &aTime);
+    }
+  }
 
   mRequestedSeekTime = aTime;
   mCurrentTime = aTime;
 
   // If we are already in the seeking state, then setting mRequestedSeekTime
   // above will result in the new seek occurring when the current seek
   // completes.
   if (mPlayState != PLAY_STATE_SEEKING) {
@@ -842,22 +912,37 @@ void nsBuiltinDecoder::SetSeekable(PRBoo
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   mSeekable = aSeekable;
   if (mDecoderStateMachine) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mDecoderStateMachine->SetSeekable(aSeekable);
   }
 }
 
-PRBool nsBuiltinDecoder::GetSeekable()
+PRBool nsBuiltinDecoder::IsSeekable()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   return mSeekable;
 }
 
+nsresult nsBuiltinDecoder::GetSeekable(nsTimeRanges* aSeekable)
+{
+  //TODO : change 0.0 to GetInitialTime() when available
+  double initialTime = 0.0;
+
+  if (IsSeekable()) {
+    double end = IsInfinite() ? std::numeric_limits<double>::infinity()
+                              : initialTime + GetDuration();
+    aSeekable->Add(initialTime, end);
+    return NS_OK;
+  }
+
+  return GetBuffered(aSeekable);
+}
+
 void nsBuiltinDecoder::Suspend()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mStream) {
     mStream->Suspend(PR_TRUE);
   }
 }
 
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -311,17 +311,17 @@ public:
   // Called from the main thread to set whether the media resource can
   // seek into unbuffered ranges. The decoder monitor must be obtained
   // before calling this.
   virtual void SetSeekable(PRBool aSeekable) = 0;
 
   // Returns PR_TRUE if the media resource can seek into unbuffered ranges,
   // as set by SetSeekable(). The decoder monitor must be obtained before
   // calling this.
-  virtual PRBool GetSeekable() = 0;
+  virtual PRBool IsSeekable() = 0;
 
   // Update the playback position. This can result in a timeupdate event
   // and an invalidate of the frame being dispatched asynchronously if
   // there is no such event currently queued.
   // Only called on the decoder thread. Must be called with
   // the decode monitor held.
   virtual void UpdatePlaybackPosition(PRInt64 aTime) = 0;
 
@@ -428,17 +428,19 @@ class nsBuiltinDecoder : public nsMediaD
   // This is called via a channel listener if it can pick up the duration
   // from a content header. Must be called from the main thread only.
   virtual void SetDuration(double aDuration);
 
   // Set a flag indicating whether seeking is supported
   virtual void SetSeekable(PRBool aSeekable);
 
   // Return PR_TRUE if seeking is supported.
-  virtual PRBool GetSeekable();
+  virtual PRBool IsSeekable();
+
+  virtual nsresult GetSeekable(nsTimeRanges* aSeekable);
 
   virtual Statistics GetStatistics();
 
   // Suspend any media downloads that are in progress. Called by the
   // media element when it is sent to the bfcache. Call on the main
   // thread only.
   virtual void Suspend();
 
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -247,17 +247,17 @@ public:
     mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
   }
 
   PRInt64 GetEndMediaTime() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     return mEndTime;
   }
 
-  PRBool GetSeekable() {
+  PRBool IsSeekable() {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     return mSeekable;
   }
 
   // Sets the current frame buffer length for the MozAudioAvailable event.
   // Accessed on the main and state machine threads.
   virtual void SetFrameBufferLength(PRUint32 aLength);
 
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -286,17 +286,20 @@ public:
   // This is called via a channel listener if it can pick up the duration
   // from a content header. Must be called from the main thread only.
   virtual void SetDuration(double aDuration) = 0;
 
   // Set a flag indicating whether seeking is supported
   virtual void SetSeekable(PRBool aSeekable) = 0;
 
   // Return PR_TRUE if seeking is supported.
-  virtual PRBool GetSeekable() = 0;
+  virtual PRBool IsSeekable() = 0;
+
+  // Return the time ranges that can be seeked into.
+  virtual nsresult GetSeekable(nsTimeRanges* aSeekable) = 0;
 
   // Invalidate the frame.
   virtual void Invalidate();
 
   // Fire progress events if needed according to the time and byte
   // constraints outlined in the specification. aTimer is PR_TRUE
   // if the method is called as a result of the progress timer rather
   // than the result of downloaded data.
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -321,17 +321,17 @@ nsresult nsOggReader::ReadMetadata(nsVid
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
     nsMediaStream* stream = mDecoder->GetCurrentStream();
     if (mDecoder->GetStateMachine()->GetDuration() == -1 &&
         mDecoder->GetStateMachine()->GetState() != nsDecoderStateMachine::DECODER_STATE_SHUTDOWN &&
         stream->GetLength() >= 0 &&
-        mDecoder->GetStateMachine()->GetSeekable())
+        mDecoder->GetStateMachine()->IsSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
       PRInt64 length = stream->GetLength();
       NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
       PRInt64 endTime = 0;
       {
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -123,16 +123,17 @@ include $(topsrcdir)/config/rules.mk
 		test_networkState.html \
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
 		test_play_events.html \
 		test_play_events_2.html \
 		test_playback.html \
 		test_playback_errors.html \
+ 		test_seekable1.html \
 		test_preload_actions.html \
 		test_preload_attribute.html \
 		test_progress.html \
 		test_reactivate.html \
 		test_readyState.html \
 		test_replay_metadata.html \
 		test_seek.html \
 		test_seek2.html \
@@ -254,16 +255,19 @@ ifdef MOZ_OGG
 		test_contentDuration7.html \
 		contentDuration1.sjs \
 		contentDuration2.sjs \
 		contentDuration3.sjs \
 		contentDuration4.sjs \
 		contentDuration5.sjs \
 		contentDuration6.sjs \
 		contentDuration7.sjs \
+		noContentLength.sjs \
+		test_seekable2.html \
+		test_seekable3.html \
 		test_a4_tone.html \
 		file_audio_event_adopt_iframe.html \
 		test_audio_event_adopt.html \
 		test_framebuffer.html \
 		test_referer.html \
 		$(NULL)
 else
 _TEST_FILES += \
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -141,16 +141,29 @@ function fileUriToSrc(path, mustExist) {
     f.append(split[i]);
   }
   if (mustExist && !f.exists()) {
     ok(false, "We expected '" + path + "' to exist, but it doesn't!");
   }
   return f.path;
 }
 
+// Returns true if two nsTimeRanges are equal, false otherwise
+function range_equals(r1, r2) {
+  if (r1.length != r2.length) {
+    return false;
+  }
+  for (var i = 0; i < r1.length; i++) {
+    if (r1.start(i) != r2.start(i) || r1.end(i) != r2.end(i)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 // These are URIs to files that we use to check that we don't leak any state
 // or other information such that script can determine stuff about a user's
 // environment. Used by test_info_leak.
 var gInfoLeakTests = [
   {
     type: 'video/ogg',
     src: fileUriToSrc("tests/content/media/test/320x240.ogv", true),
   },{
new file mode 100644
--- /dev/null
+++ b/content/media/test/noContentLength.sjs
@@ -0,0 +1,23 @@
+// Serve a media file without using content-range header
+function handleRequest(request, response)
+{
+  var file = Components.classes["@mozilla.org/file/directory_service;1"].
+                        getService(Components.interfaces.nsIProperties).
+                        get("CurWorkD", Components.interfaces.nsILocalFile);
+  var fis  = Components.classes['@mozilla.org/network/file-input-stream;1'].
+                        createInstance(Components.interfaces.nsIFileInputStream);
+  var bis  = Components.classes["@mozilla.org/binaryinputstream;1"].
+                        createInstance(Components.interfaces.nsIBinaryInputStream);
+  var paths = "tests/content/media/test/320x240.ogv";
+  var split = paths.split("/");
+  for(var i = 0; i < split.length; ++i) {
+    file.append(split[i]);
+  }
+  fis.init(file, -1, -1, false);
+  bis.setInputStream(fis);
+  var bytes = bis.readBytes(bis.available());
+  response.setStatusLine(request.httpVersion, 200, "Content Follows");
+  response.setHeader("Content-Type", "video/ogg", false);
+  response.write(bytes, bytes.length);
+  bis.close();
+}
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_seekable1.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test seekable member for media elements</title>
+<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<pre id='test'>
+<script class="testbody" type='application/javascript;version=1.8'>
+
+let manager = new MediaTestManager;
+
+function finish_test(element) {
+  if (element.parentNode)
+    element.parentNode.removeChild(element);
+  element.src="";
+  manager.finished(element.token);
+}
+
+var tests = [
+// Test using a finite media stream, and a server supporting range requests
+{
+setup : function(element) {
+          is(element.seekable.length, 0, "seekable.length should be initialy 0.");
+          element.addEventListener("loadedmetadata", function() {
+              is(element.seekable.length, 1, "seekable.length should be 1 for a server supporting range requests.");
+
+              //TODO : change this by using element.initialTime
+              is(element.seekable.start(0), 0.0, "The start of the first range should be the initialTime.");
+              is(element.seekable.end(0), element.duration, "The end of the first range should be the duration.")
+              finish_test(element);
+              }, false);
+        }
+}
+];
+
+function createTestArray() {
+  var A = [];
+    for (var k=0; k < gProgressTests.length; k++) {
+      var t = new Object();
+      t.setup = tests[0].setup;
+      t.name = gProgressTests[k].name;
+      t.type = gProgressTests[k].type;
+      A.push(t);
+    }
+  return A;
+}
+
+function startTest(test, token) {
+  var elemType = /^audio/.test(test.type) ? "audio" : "video";
+  var element = document.createElement(elemType);
+  element.src = test.name;
+  element.token = token;
+  test.setup(element);
+  manager.started(token);
+}
+
+manager.runTests(createTestArray(), startTest);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_seekable2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Media test: seek test 3</title>
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="manifest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onunload="mediaTestCleanup();">
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function on_metadataloaded() {
+  var v = document.getElementById('v');
+  ok(range_equals(v.seekable, v.buffered), "seekable.length should be initialy empty, or equal to buffered");
+  v.addEventListener("playing", function() {
+    ok(v.seekable.length > 0, "seekable.length should not be empty while playing.");
+  }, false);
+  v.addEventListener("ended", function() {
+    is(v.seekable.length, 1, "seekable.length should be 1 at end of playback.");
+    is(v.seekable.start(0), 0.0, "start of first range should 0.0");
+    is(v.seekable.end(0), v.duration, "end of first range should be equal to duration");
+    SimpleTest.finish();
+  }, false);
+
+  v.play();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<video id='v'
+src='contentDuration6.sjs'
+onloadedmetadata='on_metadataloaded();'></video>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_seekable3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Media test: seek test 3</title>
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="manifest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onunload="mediaTestCleanup();">
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function on_metadataloaded() {
+  var v = document.getElementById('v');
+  ok(range_equals(v.seekable, v.buffered), "seekable.length should be initialy empty");
+  v.addEventListener("playing", function() {
+    ok(v.seekable.length > 0, "seekable.length should not empty while playing.");
+  }, false);
+  v.addEventListener("ended", function() {
+    is(v.seekable.length, 1, "seekable.length should be 1 at end of playback.");
+    is(v.seekable.start(0), 0.0, "start of first range should 0.0");
+    is(v.seekable.end(0), v.duration, "end of first range should be equal to duration");
+    SimpleTest.finish();
+  }, false);
+
+  v.play();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<video id='v'
+src='noContentLength.sjs'
+onloadedmetadata='on_metadataloaded();'></video>
+</body>
+</html>
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -337,17 +337,16 @@
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMCrypto.h"
 #include "nsIDOMCRMFObject.h"
 #include "nsIControllers.h"
 #include "nsISelection.h"
-#include "nsISelection3.h"
 #include "nsIBoxObject.h"
 #ifdef MOZ_XUL
 #include "nsITreeSelection.h"
 #include "nsITreeContentView.h"
 #include "nsITreeView.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsTreeColumns.h"
 #endif
@@ -2914,17 +2913,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(TreeWalker, nsIDOMTreeWalker)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMTreeWalker)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Selection, nsISelection)
     DOM_CLASSINFO_MAP_ENTRY(nsISelection)
-    DOM_CLASSINFO_MAP_ENTRY(nsISelection3)
   DOM_CLASSINFO_MAP_END
 
 #ifdef MOZ_XUL
   if (nsDOMTouchEvent::PrefEnabled()) {
     DOM_CLASSINFO_MAP_BEGIN(XULDocument, nsIDOMXULDocument)
       DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocument)
       DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULDocument)
       DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentTouch)
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -52,17 +52,17 @@
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[scriptable, uuid(d8213322-46d8-47ca-a15c-2abae47ddfde)]
+[scriptable, uuid(9441effd-fbe1-4f48-aa90-1741c1e390a1)]
 interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
   readonly attribute DOMString currentSrc;
@@ -84,16 +84,17 @@ interface nsIDOMHTMLMediaElement : nsIDO
   const unsigned short HAVE_ENOUGH_DATA = 4;
   readonly attribute unsigned short readyState;
   readonly attribute boolean seeking;
 
   // playback state
            attribute double currentTime;
   readonly attribute double duration;
   readonly attribute boolean paused;
+  readonly attribute nsIDOMTimeRanges seekable;
   readonly attribute boolean ended;
   readonly attribute boolean mozAutoplayEnabled;
            attribute boolean autoplay;
   void play();
   void pause();
 
   // controls
            attribute boolean controls;
--- a/editor/libeditor/html/tests/Makefile.in
+++ b/editor/libeditor/html/tests/Makefile.in
@@ -41,16 +41,18 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = editor/libeditor/html/tests
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		green.png \
+		test_bug290026.html \
+		test_bug291780.html \
 		test_bug332636.html \
 		test_bug332636.html^headers^ \
 		test_bug366682.html \
 		test_bug372345.html \
 		test_bug410986.html \
 		test_bug414526.html \
 		test_bug417418.html \
 		test_bug432225.html \
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug290026.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=290026
+-->
+<head>
+  <title>Test for Bug 290026</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=290026">Mozilla Bug 290026</a>
+<p id="display"></p>
+<div id="editor" contenteditable></div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 290026 **/
+SimpleTest.waitForExplicitFinish();
+
+var editor = document.getElementById("editor");
+editor.innerHTML = '<p></p><ul><li>Item 1</li><li>Item 2</li></ul><p></p>';
+editor.focus();
+
+addLoadEvent(function() {
+
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  var lis = document.getElementsByTagName("li");
+  var range = document.createRange();
+  range.setStart(lis[0], 0);
+  range.setEnd(lis[1], lis[1].childNodes.length);
+  sel.addRange(range);
+  document.execCommand("indent", false, false);
+  var oneindent = '<p></p><ul style="margin-left: 40px;"><li>Item 1</li><li>Item 2</li></ul><p></p>';
+  is(editor.innerHTML, oneindent, "a once indented bulleted list");
+  document.execCommand("indent", false, false);
+  var twoindent = '<p></p><ul style="margin-left: 80px;"><li>Item 1</li><li>Item 2</li></ul><p></p>';
+  is(editor.innerHTML, twoindent, "a twice indented bulleted list");
+  document.execCommand("outdent", false, false);
+  todo_is(editor.innerHTML, oneindent, "outdenting a twice indented bulleted list");
+
+  // done
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug291780.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=291780
+-->
+<head>
+  <title>Test for Bug 291780</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=291780">Mozilla Bug 291780</a>
+<p id="display"></p>
+<div id="editor" contenteditable></div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 291780 **/
+SimpleTest.waitForExplicitFinish();
+
+var original = '<ul style="margin-left: 40px;"><li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li></ul>';
+var editor = document.getElementById("editor");
+editor.innerHTML = original;
+editor.focus();
+
+addLoadEvent(function() {
+
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  var lis = document.getElementsByTagName("li");
+  var range = document.createRange();
+  range.setStart(lis[1], 0);
+  range.setEnd(lis[2], lis[2].childNodes.length);
+  sel.addRange(range);
+  document.execCommand("indent", false, false);
+  var expected = '<ul style="margin-left: 40px;"><li>Item 1</li><ul><li>Item 2</li><li>Item 3</li></ul><li>Item 4</li></ul>';
+  is(editor.innerHTML, expected, "indenting part of an already indented bulleted list");
+  document.execCommand("outdent", false, false);
+  todo_is(editor.innerHTML, original, "outdenting the partially indented part of an already indented bulleted list"); 
+
+  // done
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -80,17 +80,17 @@
 #include "nsIDOMNSRange.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMText.h"
 #include "nsIPlaintextEditor.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIRunnable.h"
 #include "nsISelection.h"
-#include "nsISelection2.h"
+#include "nsISelectionPrivate.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextServicesFilter.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIContent.h"
 #include "nsEventListenerManager.h"
@@ -1276,18 +1276,17 @@ nsresult mozInlineSpellChecker::DoSpellC
     return NS_ERROR_FAILURE;
 
   PRBool iscollapsed;
   nsresult rv = aStatus->mRange->GetCollapsed(&iscollapsed);
   NS_ENSURE_SUCCESS(rv, rv);
   if (iscollapsed)
     return NS_OK;
 
-  nsCOMPtr<nsISelection2> sel2 = do_QueryInterface(aSpellCheckSelection, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsISelectionPrivate> privSel = do_QueryInterface(aSpellCheckSelection);
 
   // see if the selection has any ranges, if not, then we can optimize checking
   // range inclusion later (we have no ranges when we are initially checking or
   // when there are no misspelled words yet).
   PRInt32 originalRangeCount;
   rv = aSpellCheckSelection->GetRangeCount(&originalRangeCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1342,19 +1341,19 @@ nsresult mozInlineSpellChecker::DoSpellC
     // there were no ranges when we started out.
     if (originalRangeCount > 0) {
       // likewise, if this word is inside new text, we won't bother testing
       PRBool inCreatedRange = PR_FALSE;
       if (createdRange)
         createdRange->IsPointInRange(beginNode, beginOffset, &inCreatedRange);
       if (! inCreatedRange) {
         nsCOMArray<nsIDOMRange> ranges;
-        rv = sel2->GetRangesForIntervalCOMArray(beginNode, beginOffset,
-                                                endNode, endOffset,
-                                                PR_TRUE, &ranges);
+        rv = privSel->GetRangesForIntervalCOMArray(beginNode, beginOffset,
+                                                   endNode, endOffset,
+                                                   PR_TRUE, &ranges);
         NS_ENSURE_SUCCESS(rv, rv);
         for (PRInt32 i = 0; i < ranges.Count(); i ++)
           RemoveRange(aSpellCheckSelection, ranges[i]);
       }
     }
 
     // some words are special and don't need checking
     if (dontCheckWord)
@@ -1473,23 +1472,21 @@ mozInlineSpellChecker::ResumeCheck(mozIn
 nsresult
 mozInlineSpellChecker::IsPointInSelection(nsISelection *aSelection,
                                           nsIDOMNode *aNode,
                                           PRInt32 aOffset,
                                           nsIDOMRange **aRange)
 {
   *aRange = nsnull;
 
-  nsresult rv;
-  nsCOMPtr<nsISelection2> sel2 = do_QueryInterface(aSelection, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
 
   nsCOMArray<nsIDOMRange> ranges;
-  rv = sel2->GetRangesForIntervalCOMArray(aNode, aOffset, aNode, aOffset,
-                                          PR_TRUE, &ranges);
+  nsresult rv = privSel->GetRangesForIntervalCOMArray(aNode, aOffset, aNode, aOffset,
+                                                      PR_TRUE, &ranges);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (ranges.Count() == 0)
     return NS_OK; // no matches
 
   // there may be more than one range returned, and we don't know what do
   // do with that, so just get the first one
   NS_ADDREF(*aRange = ranges[0]);
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -646,22 +646,25 @@ void gfxFontFamily::LocalizedName(nsAStr
     // just return the primary name; subclasses should override
     aLocalizedName = mName;
 }
 
 
 void
 gfxFontFamily::FindFontForChar(FontSearch *aMatchData)
 {
-    if (!mHasStyles)
+    if (!mHasStyles) {
         FindStyleVariations();
-
-    // xxx - optimization point - keep a bit vector with the union of supported unicode ranges
-    // by all fonts for this family and bail immediately if the character is not in any of
-    // this family's cmaps
+    }
+
+    if (!TestCharacterMap(aMatchData->mCh)) {
+        // none of the faces in the family support the required char,
+        // so bail out immediately
+        return;
+    }
 
     // iterate over fonts
     PRUint32 numFonts = mAvailableFonts.Length();
     for (PRUint32 i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i];
 
         // skip certain fonts during system fallback
         if (!fe || fe->SkipDuringSystemFallback())
@@ -2605,16 +2608,32 @@ gfxFontGroup::FindFontForChar(PRUint32 a
 
     // 1. check fonts in the font group
     for (PRUint32 i = 0; i < FontListLength(); i++) {
         nsRefPtr<gfxFont> font = GetFontAt(i);
         if (font->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return font.forget();
         }
+        // check other faces of the family
+        gfxFontFamily *family = font->GetFontEntry()->Family();
+        if (family && family->TestCharacterMap(aCh)) {
+            FontSearch matchData(aCh, font);
+            family->FindFontForChar(&matchData);
+            gfxFontEntry *fe = matchData.mBestMatch;
+            if (fe) {
+                PRBool needsBold =
+                    font->GetStyle()->weight >= 600 && !fe->IsBold();
+                selectedFont =
+                    fe->FindOrMakeFont(font->GetStyle(), needsBold);
+                if (selectedFont) {
+                    return selectedFont.forget();
+                }
+            }
+        }
     }
 
     // if character is in Private Use Area, don't do matching against pref or system fonts
     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
         return nsnull;
 
     // 2. search pref fonts
     if ((selectedFont = WhichPrefFontSupportsChar(aCh))) {
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -477,17 +477,18 @@ public:
 
     gfxFontFamily(const nsAString& aName) :
         mName(aName),
         mOtherFamilyNamesInitialized(PR_FALSE),
         mHasOtherFamilyNames(PR_FALSE),
         mFaceNamesInitialized(PR_FALSE),
         mHasStyles(PR_FALSE),
         mIsSimpleFamily(PR_FALSE),
-        mIsBadUnderlineFamily(PR_FALSE)
+        mIsBadUnderlineFamily(PR_FALSE),
+        mCharacterMapInitialized(PR_FALSE)
         { }
 
     virtual ~gfxFontFamily() { }
 
     const nsString& Name() { return mName; }
 
     virtual void LocalizedName(nsAString& aLocalizedName);
     virtual PRBool HasOtherFamilyNames();
@@ -539,20 +540,38 @@ public:
     virtual void FindStyleVariations() { }
 
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
     // read in cmaps for all the faces
     void ReadCMAP() {
         PRUint32 i, numFonts = mAvailableFonts.Length();
-        // called from RunLoader BEFORE CheckForSimpleFamily so that there cannot
-        // be any NULL entries in mAvailableFonts
-        for (i = 0; i < numFonts; i++)
-            mAvailableFonts[i]->ReadCMAP();
+        for (i = 0; i < numFonts; i++) {
+            gfxFontEntry *fe = mAvailableFonts[i];
+            if (!fe) {
+                continue;
+            }
+            fe->ReadCMAP();
+            mCharacterMap.Union(fe->mCharacterMap);
+        }
+        mCharacterMap.Compact();
+        mCharacterMapInitialized = PR_TRUE;
+    }
+
+    PRBool TestCharacterMap(PRUint32 aCh) {
+        if (!mCharacterMapInitialized) {
+            ReadCMAP();
+        }
+        return mCharacterMap.test(aCh);
+    }
+
+    void ResetCharacterMap() {
+        mCharacterMap.reset();
+        mCharacterMapInitialized = PR_FALSE;
     }
 
     // mark this family as being in the "bad" underline offset blacklist
     void SetBadUnderlineFamily() {
         mIsBadUnderlineFamily = PR_TRUE;
         if (mHasStyles) {
             SetBadUnderlineFonts();
         }
@@ -585,22 +604,24 @@ protected:
             if (mAvailableFonts[i]) {
                 mAvailableFonts[i]->mIsBadUnderlineFont = PR_TRUE;
             }
         }
     }
 
     nsString mName;
     nsTArray<nsRefPtr<gfxFontEntry> >  mAvailableFonts;
+    gfxSparseBitSet mCharacterMap;
     PRPackedBool mOtherFamilyNamesInitialized;
     PRPackedBool mHasOtherFamilyNames;
     PRPackedBool mFaceNamesInitialized;
     PRPackedBool mHasStyles;
     PRPackedBool mIsSimpleFamily;
     PRPackedBool mIsBadUnderlineFamily;
+    PRPackedBool mCharacterMapInitialized;
 
     enum {
         // for "simple" families, the faces are stored in mAvailableFonts
         // with fixed positions:
         kRegularFaceIndex    = 0,
         kBoldFaceIndex       = 1,
         kItalicFaceIndex     = 2,
         kBoldItalicFaceIndex = 3,
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -308,17 +308,17 @@ gfxFontUtils::ReadCMAPTableFormat12(cons
         NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
                        startCharCode <= endCharCode &&
                        endCharCode <= CMAP_MAX_CODEPOINT, 
                        NS_ERROR_GFX_CMAP_MALFORMED);
         aCharacterMap.SetRange(startCharCode, endCharCode);
         prevEndCharCode = endCharCode;
     }
 
-    aCharacterMap.mBlocks.Compact();
+    aCharacterMap.Compact();
 
     return NS_OK;
 }
 
 nsresult 
 gfxFontUtils::ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
                                    gfxSparseBitSet& aCharacterMap)
 {
@@ -386,17 +386,17 @@ gfxFontUtils::ReadCMAPTableFormat4(const
                     // glyph = (ReadShortAt16(idDeltas, i) + *gdata) % 65536;
 
                     aCharacterMap.set(c);
                 }
             }
         }
     }
 
-    aCharacterMap.mBlocks.Compact();
+    aCharacterMap.Compact();
 
     return NS_OK;
 }
 
 nsresult
 gfxFontUtils::ReadCMAPTableFormat14(const PRUint8 *aBuf, PRUint32 aLength,
                                     PRUint8*& aTable)
 {
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -160,18 +160,16 @@ public:
         if (blockIndex >= mBlocks.Length()) {
             nsAutoPtr<Block> *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
             if (NS_UNLIKELY(!blocks)) // OOM
                 return;
         }
         Block *block = mBlocks[blockIndex];
         if (!block) {
             block = new Block;
-            if (NS_UNLIKELY(!block)) // OOM
-                return;
             mBlocks[blockIndex] = block;
         }
         block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
     }
 
     void set(PRUint32 aIndex, PRBool aValue) {
         if (aValue)
             set(aIndex);
@@ -196,19 +194,16 @@ public:
 
             Block *block = mBlocks[i];
             if (!block) {
                 PRBool fullBlock = PR_FALSE;
                 if (aStart <= blockFirstBit && aEnd >= blockLastBit)
                     fullBlock = PR_TRUE;
 
                 block = new Block(fullBlock ? 0xFF : 0);
-
-                if (NS_UNLIKELY(!block)) // OOM
-                    return;
                 mBlocks[i] = block;
 
                 if (fullBlock)
                     continue;
             }
 
             const PRUint32 start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
             const PRUint32 end = NS_MIN<PRUint32>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
@@ -274,17 +269,54 @@ public:
     }
 
     // clear out all blocks in the array
     void reset() {
         PRUint32 i;
         for (i = 0; i < mBlocks.Length(); i++)
             mBlocks[i] = nsnull;    
     }
-    
+
+    // set this bitset to the union of its current contents and another
+    void Union(const gfxSparseBitSet& aBitset) {
+        // ensure mBlocks is large enough
+        PRUint32 blockCount = aBitset.mBlocks.Length();
+        if (blockCount > mBlocks.Length()) {
+            PRUint32 needed = blockCount - mBlocks.Length();
+            nsAutoPtr<Block> *blocks = mBlocks.AppendElements(needed);
+            if (NS_UNLIKELY(!blocks)) { // OOM
+                return;
+            }
+        }
+        // for each block that may be present in aBitset...
+        for (PRUint32 i = 0; i < blockCount; ++i) {
+            // if it is missing (implicitly empty), just skip
+            if (!aBitset.mBlocks[i]) {
+                continue;
+            }
+            // if the block is missing in this set, just copy the other
+            if (!mBlocks[i]) {
+                mBlocks[i] = new Block(*aBitset.mBlocks[i]);
+                continue;
+            }
+            // else set existing block to the union of both
+            PRUint32 *dst = reinterpret_cast<PRUint32*>(mBlocks[i]->mBits);
+            const PRUint32 *src =
+                reinterpret_cast<const PRUint32*>(aBitset.mBlocks[i]->mBits);
+            for (PRUint32 j = 0; j < BLOCK_SIZE / 4; ++j) {
+                dst[j] |= src[j];
+            }
+        }
+    }
+
+    void Compact() {
+        mBlocks.Compact();
+    }
+
+private:
     nsTArray< nsAutoPtr<Block> > mBlocks;
 };
 
 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
 
 namespace mozilla {
 
 // Byte-swapping types and name table structure definitions moved from
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -101,49 +101,53 @@ class gfxMixedFontFamily : public gfxFon
 public:
     friend class gfxUserFontSet;
 
     gfxMixedFontFamily(const nsAString& aName)
         : gfxFontFamily(aName) { }
 
     virtual ~gfxMixedFontFamily() { }
 
-    void AddFontEntry(gfxFontEntry *aFontEntry) {
+    void AddFontEntry(gfxFontEntry *aFontEntry)
+    {
         nsRefPtr<gfxFontEntry> fe = aFontEntry;
         mAvailableFonts.AppendElement(fe);
         aFontEntry->SetFamily(this);
+        ResetCharacterMap();
     }
 
     void ReplaceFontEntry(gfxFontEntry *aOldFontEntry, gfxFontEntry *aNewFontEntry) 
     {
         PRUint32 numFonts = mAvailableFonts.Length();
         for (PRUint32 i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
             if (fe == aOldFontEntry) {
                 aOldFontEntry->SetFamily(nsnull);
                 // note that this may delete aOldFontEntry, if there's no
                 // other reference to it except from its family
                 mAvailableFonts[i] = aNewFontEntry;
                 aNewFontEntry->SetFamily(this);
-                return;
+                break;
             }
         }
+        ResetCharacterMap();
     }
 
     void RemoveFontEntry(gfxFontEntry *aFontEntry) 
     {
         PRUint32 numFonts = mAvailableFonts.Length();
         for (PRUint32 i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
             if (fe == aFontEntry) {
                 aFontEntry->SetFamily(nsnull);
                 mAvailableFonts.RemoveElementAt(i);
-                return;
+                break;
             }
         }
+        ResetCharacterMap();
     }
 
     // temp method to determine if all proxies are loaded
     PRBool AllLoaded() 
     {
         PRUint32 numFonts = mAvailableFonts.Length();
         for (PRUint32 i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -40,19 +40,19 @@
 # config.mk
 #
 # Determines the platform and builds the macros needed to load the
 # appropriate platform-specific .mk file, then defines all (most?)
 # of the generic macros.
 #
 
 # Define an include-at-most-once flag
-#ifdef INCLUDED_CONFIG_MK
-#$(error Don't include config.mk twice!)
-#endif
+ifdef INCLUDED_CONFIG_MK
+$(error Don't include config.mk twice!)
+endif
 INCLUDED_CONFIG_MK = 1
 
 EXIT_ON_ERROR = set -e; # Shell loops continue past errors without this.
 
 ifndef topsrcdir
 topsrcdir	= $(DEPTH)
 endif
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -329,17 +329,17 @@ case "$target" in
     elif test "$target" != "arm-android-eabi"; then
        dnl fail if we're not building with NDKr4
        AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
     fi
 
     CPPFLAGS="-I$android_platform/usr/include $STLPORT_CPPFLAGS $CPPFLAGS"
     CFLAGS="-mandroid -I$android_platform/usr/include -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -I$android_platform/usr/include -fno-short-enums -fno-exceptions $CXXFLAGS"
-    LIBS="$LIBS $STLPORT_LIBS -static-libstdc++"
+    LIBS="$LIBS $STLPORT_LIBS"
 
     dnl Add -llog by default, since we use it all over the place.
     dnl Add --allow-shlib-undefined, because libGLESv2 links to an
     dnl undefined symbol (present on the hardware, just not in the
     dnl NDK.)
     LDFLAGS="-mandroid $STLPORT_LDFLAGS -L$android_platform/usr/lib -Wl,-rpath-link=$android_platform/usr/lib --sysroot=$android_platform -llog -Wl,--allow-shlib-undefined $LDFLAGS"
 
     dnl prevent cross compile section from using these flags as host flags
@@ -352,18 +352,18 @@ case "$target" in
     if test -z "$HOST_CXXFLAGS" ; then
         HOST_CXXFLAGS=" "
     fi
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
 
     ANDROID_NDK="${android_ndk}"
-    ANDROID_TOOLCHAIN="{android_toolchain}"
-    ANDROID_PLATFORM="{android_platform}"
+    ANDROID_TOOLCHAIN="${android_toolchain}"
+    ANDROID_PLATFORM="${android_platform}"
     ANDROID_SDK="${android_sdk}"
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
     ANDROID_VERSION="${android_version}"
 
     # save these for libffi's subconfigure,
     # which doesn't know how to figure this stuff out on its own
     ANDROID_CFLAGS="$CFLAGS"
     ANDROID_CPPFLAGS="$CPPFLAGS"
--- a/layout/base/tests/test_scroll_selection_into_view.html
+++ b/layout/base/tests/test_scroll_selection_into_view.html
@@ -45,17 +45,17 @@
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var ANCHOR = 0;
 var FOCUS = 1;
 
 function testCollapsed(id, vPercent, startAt, expected) {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-  var selection = window.getSelection().QueryInterface(Components.interfaces.nsISelection2);
+  var selection = window.getSelection().QueryInterface(Components.interfaces.nsISelectionPrivate);
 
   var c = document.getElementById("c" + id);
   var target = document.getElementById("target" + id);
   if (target.contentDocument) {
     target = target.contentDocument.getElementById("target" + id);
   }
   selection.collapse(target.parentNode, 0);
   c.scrollTop = startAt;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -43,19 +43,16 @@
 
 #include "nsCOMPtr.h"
 #include "nsWeakReference.h"
 #include "nsIFactory.h"
 #include "nsIEnumerator.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsFrameSelection.h"
-#include "nsISelection.h"
-#include "nsISelection2.h"
-#include "nsISelection3.h"
 #include "nsISelectionPrivate.h"
 #include "nsISelectionListener.h"
 #include "nsIComponentManager.h"
 #include "nsContentCID.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsRange.h"
@@ -176,31 +173,27 @@ static RangeData sEmptyData(nsnull);
 
 // Note, the ownership of nsTypedSelection depends on which way the object is
 // created. When nsFrameSelection has created nsTypedSelection,
 // addreffing/releasing nsTypedSelection object is aggregated to
 // nsFrameSelection. Otherwise normal addref/release is used.
 // This ensures that nsFrameSelection is never deleted before its
 // nsTypedSelections.
 
-class nsTypedSelection : public nsISelection2,
-                         public nsISelection3,
-                         public nsISelectionPrivate,
+class nsTypedSelection : public nsISelectionPrivate,
                          public nsSupportsWeakReference
 {
 public:
   nsTypedSelection();
   nsTypedSelection(nsFrameSelection *aList);
   virtual ~nsTypedSelection();
   
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTypedSelection, nsISelection)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTypedSelection, nsISelectionPrivate)
   NS_DECL_NSISELECTION
-  NS_DECL_NSISELECTION2
-  NS_DECL_NSISELECTION3
   NS_DECL_NSISELECTIONPRIVATE
 
   // utility methods for scrolling the selection into view
   nsresult      GetPresContext(nsPresContext **aPresContext);
   nsresult      GetPresShell(nsIPresShell **aPresShell);
   // Returns a rect containing the selection region, and frame that that
   // position is relative to. For SELECTION_ANCHOR_REGION or
   // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
@@ -3446,18 +3439,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mSelectionListeners)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 DOMCI_DATA(Selection, nsTypedSelection)
 
 // QueryInterface implementation for nsTypedSelection
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypedSelection)
   NS_INTERFACE_MAP_ENTRY(nsISelection)
-  NS_INTERFACE_MAP_ENTRY(nsISelection2)
-  NS_INTERFACE_MAP_ENTRY(nsISelection3)
   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Selection)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypedSelection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypedSelection)
--- a/layout/generic/test/test_bug348681.html
+++ b/layout/generic/test/test_bug348681.html
@@ -327,204 +327,204 @@ https://bugzilla.mozilla.org/show_bug.cg
        selection.addRange(range2);
        selection.addRange(range3);
        var range8 = document.createRange();
        range8.setStart(testNode, 10);
        range8.setEnd(testNode, 12);
        selection.addRange(range8);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 8, 10);
-       var sel2 = selection.QueryInterface(Components.interfaces.nsISelection2);
-       ok(sel2, "Test 17 - QIed to instance of nsISelection2 interface");
+       var privSel = selection.QueryInterface(Components.interfaces.nsISelectionPrivate);
+       ok(privSel, "Test 17 - QIed to instance of nsISelection2 interface");
        var numResults = {};
-       var results = sel2.GetRangesForInterval(testNode, 8, testNode, 10,
-                                               false, numResults);
+       var results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
+                                                  false, numResults);
        intervalChecker.check(17, results);
 
        // Test 18. Check GetRangesForInterval returns correct results.
        // Part 2 - Test interval matches a range, adjacent ranges allowed.
        intervalChecker.addExpected(testNode, 6, 8);
        intervalChecker.addExpected(testNode, 10, 12);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 8, testNode, 10,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
+                                              true, numResults);
        intervalChecker.check(18, results);
 
        // Test 19. Check GetRangesForInterval returns correct results.
        // Part 3 - Test interval not selected.
        intervalChecker.reset();
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 14, testNode, 16,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 14, testNode, 16,
+                                              true, numResults);
        intervalChecker.check(19, results);
 
        // Test 20. Check GetRangesForInterval returns correct results.
        // Part 4 - Test interval is not equal to, and entirely contained in
        //          an existing range.
        selection.removeAllRanges();
        selection.addRange(range6);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 0, 10);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 5, testNode, 7,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 5, testNode, 7,
+                                              true, numResults);
        intervalChecker.check(20, results);
 
        // Test 21. Check GetRangesForInterval returns correct results.
        // Part 5 - Test interval is not equal to, and entirely contains an
        //          existing range.
        selection.removeAllRanges();
        selection.addRange(range3);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 6, 8);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 5, testNode, 9,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 5, testNode, 9,
+                                              true, numResults);
        intervalChecker.check(21, results);
 
        // Test 22. Check GetRangesForInterval returns correct results.
        // Part 6 - Test interval is end-adjacent to range at position 0 in
        //          the range array, and adjacencies permitted.
        selection.removeAllRanges();
        selection.addRange(range2);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 8, 10);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 6, testNode, 8,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
+                                              true, numResults);
        intervalChecker.check(22, results);
 
        // Test 23. Check GetRangesForInterval returns correct results.
        // Part 7 - Test interval is end-adjacent to range at position 0 in
        //          the range array, and adjacencies not permitted.
        intervalChecker.reset();
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 6, testNode, 8,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
+                                              false, numResults);
        intervalChecker.check(23, results);
 
        // Test 24. Check GetRangesForInterval returns correct results.
        // Part 8 - Test interval is start-adjacent to last range in array,
        //          and adjacencies permitted.
        intervalChecker.addExpected(testNode, 8, 10);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 10, testNode, 12,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
+                                              true, numResults);
        intervalChecker.check(24, results);
 
        // Test 25. Check GetRangesForInterval returns correct results.
        // Part 9 - Test interval is start-adjacent to last range in array,
        //          and adjacencies not permitted.
        intervalChecker.reset();
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 10, testNode, 12,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
+                                              false, numResults);
        intervalChecker.check(25, results);
 
        // Test 26.  Check GetRangesForInterval returns correct results.
        // Part 10 - Test interval is equal to a collapsed range at position 0
        //           in the range array, and adjacencies permitted.
        selection.removeAllRanges();
        selection.addRange(range4);
        intervalChecker.addExpected(testNode, 11, 11);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 11, testNode, 11,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+                                              true, numResults);
        intervalChecker.check(26, results);
 
        // Test 27.  Check GetRangesForInterval returns correct results.
        // Part 11 - Test interval is equal to a collapsed range at position 0
        //           in the range array, and adjacencies not permitted.
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 11, testNode, 11,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+                                              false, numResults);
        intervalChecker.check(27, results);
 
        // Test 28.  Check GetRangesForInterval returns correct results.
        // Part 12 - Test interval is equal to a collapsed range at end of the
        //           range array, and adjacencies permitted.
        selection.removeAllRanges();
        selection.addRange(range2);
        selection.addRange(range4);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 11, testNode, 11,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+                                              true, numResults);
        intervalChecker.check(28, results);
 
        // Test 29.  Check GetRangesForInterval returns correct results.
        // Part 13 - Test interval is equal to a collapsed range at end of the
        //           range array, and adjacencies not permitted.
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 11, testNode, 11,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
+                                              false, numResults);
        intervalChecker.check(29, results);
 
        // Test 30.  Check GetRangesForInterval returns correct results.
        // Part 14 - Test interval is a collapsed range contained in an
        //           existing range.
        selection.removeAllRanges();
        selection.addRange(range3);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 6, 8);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 7, testNode, 7,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 7, testNode, 7,
+                                              true, numResults);
        intervalChecker.check(30, results);
 
        // Test 31.  Check GetRangesForInterval returns correct results.
        // Part 15 - Test interval equals a collapsed range which is not stored
        //           at either endpoint of the selection's range array,
        //           adjacencies not allowed.
        selection.removeAllRanges();
        range3.collapse(false);
        selection.addRange(range5);
        selection.addRange(range3);
        selection.addRange(range8);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 8, 8);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 8, testNode, 8,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
+                                              false, numResults);
        intervalChecker.check(31, results);
 
        // Test 32.  Check GetRangesForInterval returns correct results.
        // Part 16 - Test interval equals a collapsed range which is not stored
        //           at either endpoint of the selection's range array,
        //           adjacencies allowed.
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 8, testNode, 8,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
+                                              true, numResults);
        intervalChecker.check(32, results);
 
        // Test 33.  Check GetRangesForInterval returns correct results.
        // Part 17 - Test interval contains a collapsed range which is not
        //           stored at either endpoint of the selection's range array.
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 7, testNode, 9,
-                                           false, numResults);
+       results = privSel.GetRangesForInterval(testNode, 7, testNode, 9,
+                                              false, numResults);
        intervalChecker.check(33, results);
 
        // Test 34.  Check GetRangesForInterval returns correct results.
        // Part 18 - Test interval left-ovelaps an existing range.
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 5, 7);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 2, testNode, 6,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 2, testNode, 6,
+                                              true, numResults);
        intervalChecker.check(34, results);
 
        // Test 35.  Check GetRangesForInterval returns correct results.
        // Part 19 - Test interval right-ovelaps an existing range.
        selection.removeAllRanges();
        selection.addRange(range8);
        intervalChecker.reset();
        intervalChecker.addExpected(testNode, 10, 12);
        numResults = {};
-       results = sel2.GetRangesForInterval(testNode, 11, testNode, 13,
-                                           true, numResults);
+       results = privSel.GetRangesForInterval(testNode, 11, testNode, 13,
+                                              true, numResults);
        intervalChecker.check(35, results);
 
        SimpleTest.finish();
      }
    </script>
   </pre>
 
   <p id="testparagraph">
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -535,19 +535,20 @@ nsMathMLmtableOuterFrame::GetRowFrameAt(
                                         PRInt32         aRowIndex)
 {
   PRInt32 rowCount, colCount;
   GetTableSize(rowCount, colCount);
 
   // Negative indices mean to find upwards from the end.
   if (aRowIndex < 0) {
     aRowIndex = rowCount + aRowIndex;
+  } else {
+    // aRowIndex is 1-based, so convert it to a 0-based index
+    --aRowIndex;
   }
-  // aRowIndex is 1-based, so convert it to a 0-based index
-  --aRowIndex;
 
   // if our inner table says that the index is valid, find the row now
   if (0 <= aRowIndex && aRowIndex <= rowCount) {
     nsIFrame* tableFrame = mFrames.FirstChild();
     if (!tableFrame || tableFrame->GetType() != nsGkAtoms::tableFrame)
       return nsnull;
     nsIFrame* rgFrame = tableFrame->GetFirstChild(nsnull);
     if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mtable-align-negative-rownumber-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<head>
+<title>mtable align attribute: negative rownumber</title>
+</head>
+
+<body>
+
+<div>
+<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><mrow>
+<mtable align="axis 3"><mtr><mtd><mi>a</mi></mtd> <mtd><mi>b</mi></mtd> <mtd><mi>c</mi></mtd></mtr> <mtr><mtd><mi>d</mi></mtd> <mtd><mi>e</mi></mtd> <mtd><mi>f</mi></mtd></mtr> <mtr><mtd><mi>g</mi></mtd> <mtd><mi>h</mi></mtd> <mtd><mi>i</mi></mtd></mtr></mtable><mo>=</mo>
+<mtable align="axis 1"><mtr><mtd><mi>a</mi></mtd> <mtd><mi>b</mi></mtd> <mtd><mi>c</mi></mtd></mtr> <mtr><mtd><mi>d</mi></mtd> <mtd><mi>e</mi></mtd> <mtd><mi>f</mi></mtd></mtr> <mtr><mtd><mi>g</mi></mtd> <mtd><mi>h</mi></mtd> <mtd><mi>i</mi></mtd></mtr></mtable></mrow></math>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mtable-align-negative-rownumber.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<head>
+<title>mtable align attribute: negative rownumber</title>
+</head>
+
+<body>
+
+<div>
+<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><mrow>
+<mtable align="axis -1"><mtr><mtd><mi>a</mi></mtd> <mtd><mi>b</mi></mtd> <mtd><mi>c</mi></mtd></mtr> <mtr><mtd><mi>d</mi></mtd> <mtd><mi>e</mi></mtd> <mtd><mi>f</mi></mtd></mtr> <mtr><mtd><mi>g</mi></mtd> <mtd><mi>h</mi></mtd> <mtd><mi>i</mi></mtd></mtr></mtable><mo>=</mo>
+<mtable align="axis 1"><mtr><mtd><mi>a</mi></mtd> <mtd><mi>b</mi></mtd> <mtd><mi>c</mi></mtd></mtr> <mtr><mtd><mi>d</mi></mtd> <mtd><mi>e</mi></mtd> <mtd><mi>f</mi></mtd></mtr> <mtr><mtd><mi>g</mi></mtd> <mtd><mi>h</mi></mtd> <mtd><mi>i</mi></mtd></mtr></mtable></mrow></math>
+</div>
+
+</body>
+</html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -21,16 +21,17 @@ fails-if(winWidget) == mfenced-10.html m
 == mi-mathvariant-2.xhtml mi-mathvariant-2-ref.xhtml
 != non-spacing-accent-1.xhtml non-spacing-accent-1-ref.xhtml
 == overbar-width-1.xhtml overbar-width-1-ref.xhtml
 == quotes-1.xhtml quotes-1-ref.xhtml
 != stretchy-underbar-1.xhtml stretchy-underbar-1-ref.xhtml 
 == table-width-1.xhtml table-width-1-ref.xhtml
 == underbar-width-1.xhtml underbar-width-1-ref.xhtml
 == mathml-type-supported.xhtml mathml-type-supported-ref.xml
+== mtable-align-negative-rownumber.html mtable-align-negative-rownumber-ref.html
 != embellished-op-1-1.html embellished-op-1-1-ref.html
 != embellished-op-1-2.html embellished-op-1-2-ref.html
 != embellished-op-1-3.html embellished-op-1-3-ref.html
 != embellished-op-1-4.html embellished-op-1-4-ref.html
 != embellished-op-1-5.html embellished-op-1-5-ref.html
 != embellished-op-2-1.html embellished-op-2-1-ref.html
 != embellished-op-2-2.html embellished-op-2-2-ref.html
 != embellished-op-2-3.html embellished-op-2-3-ref.html
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -87,16 +87,17 @@ static PRLogModuleInfo *gFontDownloaderL
 #define LOG_ENABLED() PR_LOG_TEST(gFontDownloaderLog, PR_LOG_DEBUG)
 
 
 nsFontFaceLoader::nsFontFaceLoader(gfxProxyFontEntry *aProxy, nsIURI *aFontURI,
                                    nsUserFontSet *aFontSet, nsIChannel *aChannel)
   : mFontEntry(aProxy), mFontURI(aFontURI), mFontSet(aFontSet),
     mChannel(aChannel)
 {
+  mFontFamily = aProxy->Family();
 }
 
 nsFontFaceLoader::~nsFontFaceLoader()
 {
   if (mLoadTimer) {
     mLoadTimer->Cancel();
     mLoadTimer = nsnull;
   }
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -140,16 +140,17 @@ public:
   static void LoadTimerCallback(nsITimer *aTimer, void *aClosure);
 
   static nsresult CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
                                    nsIURI* aTargetURI,
                                    nsISupports* aContext);
 
 private:
   nsRefPtr<gfxProxyFontEntry>  mFontEntry;
+  nsRefPtr<gfxFontFamily>      mFontFamily;
   nsCOMPtr<nsIURI>        mFontURI;
   nsRefPtr<nsUserFontSet> mFontSet;
   nsCOMPtr<nsIChannel>    mChannel;
   nsCOMPtr<nsITimer>      mLoadTimer;
 
   nsIStreamLoader        *mStreamLoader;
 };
 
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -568,17 +568,17 @@ WebSocketChannel::WebSocketChannel() :
   mTCPClosed(0),
   mOpenBlocked(0),
   mOpenRunning(0),
   mChannelWasOpened(0),
   mMaxMessageSize(16000000),
   mStopOnClose(NS_OK),
   mServerCloseCode(CLOSE_ABNORMAL),
   mScriptCloseCode(0),
-  mFragmentOpcode(0),
+  mFragmentOpcode(kContinuation),
   mFragmentAccumulator(0),
   mBuffered(0),
   mBufferSize(16384),
   mCurrentOut(nsnull),
   mCurrentOutSent(0),
   mCompressor(nsnull),
   mDynamicOutputSize(0),
   mDynamicOutput(nsnull)
@@ -858,24 +858,32 @@ WebSocketChannel::ProcessInput(PRUint8 *
     }
 
     if (!finBit || opcode == kContinuation) {
       // This is part of a fragment response
 
       // Only the first frame has a non zero op code: Make sure we don't see a
       // first frame while some old fragments are open
       if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
-        LOG(("WebSocketHeandler:: nested fragments\n"));
+        LOG(("WebSocketChannel:: nested fragments\n"));
         AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       LOG(("WebSocketChannel:: Accumulating Fragment %lld\n", payloadLength));
 
       if (opcode == kContinuation) {
+
+        // Make sure this continuation fragment isn't the first fragment
+        if (mFragmentOpcode == kContinuation) {
+          LOG(("WebSocketHeandler:: continuation code in first fragment\n"));
+          AbortSession(NS_ERROR_ILLEGAL_VALUE);
+          return NS_ERROR_ILLEGAL_VALUE;
+        }
+
         // For frag > 1 move the data body back on top of the headers
         // so we have contiguous stream of data
         NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
                           "payload offset from frameptr wrong");
         ::memmove(mFramePtr, payload, avail);
         payload = mFramePtr;
         if (mBuffered)
           mBuffered -= framingLength;
@@ -885,16 +893,18 @@ WebSocketChannel::ProcessInput(PRUint8 *
 
       if (finBit) {
         LOG(("WebSocketChannel:: Finalizing Fragment\n"));
         payload -= mFragmentAccumulator;
         payloadLength += mFragmentAccumulator;
         avail += mFragmentAccumulator;
         mFragmentAccumulator = 0;
         opcode = mFragmentOpcode;
+        // reset to detect if next message illegally starts with continuation
+        mFragmentOpcode = kContinuation;
       } else {
         opcode = kContinuation;
         mFragmentAccumulator += payloadLength;
       }
     } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
       // This frame is not part of a fragment sequence but we
       // have an open fragment.. it must be a control code or else
       // we have a problem
--- a/security/manager/Makefile.in
+++ b/security/manager/Makefile.in
@@ -252,21 +252,21 @@ DEFAULT_GMAKE_FLAGS += \
 	AR='$(AR) $(AR_FLAGS:$@=$$@)' \
 	RANLIB="$(RANLIB)" \
 	RC="$(RC) $(RCFLAGS)" \
 	OS_ARCH="$(OS_ARCH)" \
 	OS_TEST="$(OS_TEST)" \
 	CPU_ARCH="$(TARGET_CPU)" \
 	$(NULL)
 
-# Android has pthreads integrated into -lc, so OS_LIBS is set to nothing
+# Android has pthreads integrated into -lc, so OS_PTHREAD is set to nothing
 ifeq ($(OS_TARGET), Android)
 DEFAULT_GMAKE_FLAGS += \
 	OS_RELEASE="2.6" \
-	OS_LIBS= \
+	OS_PTHREAD= \
 	STANDARDS_CFLAGS="-std=gnu89" \
 	DSO_CFLAGS="$(CFLAGS) -DCHECK_FORK_GETPID -DRTLD_NOLOAD=0 -DANDROID_VERSION=$(ANDROID_VERSION) -include $(ABS_topsrcdir)/security/manager/android_stub.h" \
 	DSO_LDOPTS="-shared $(LDFLAGS) $(WRAP_MALLOC_CFLAGS) $(WRAP_MALLOC_LIB) " \
 	$(NULL)
 endif
 
 ifndef UNIVERSAL_BINARY
 SKIP_CHK=1
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -1289,17 +1289,17 @@ BookmarksTracker.prototype = {
         break;
       case "bookmarks-restore-success":
         this._log.debug("Tracking all items on successful import.");
         this.ignoreAll = false;
         
         this._log.debug("Restore succeeded: wiping server and other clients.");
         Weave.Service.resetClient([this.name]);
         Weave.Service.wipeServer([this.name]);
-        Weave.Service.prepCommand("wipeEngine", [this.name]);
+        Clients.sendCommand("wipeEngine", [this.name]);
         break;
       case "bookmarks-restore-failed":
         this._log.debug("Tracking all items on failed import.");
         this.ignoreAll = false;
         break;
     }
   },
 
--- a/services/sync/modules/engines/clients.js
+++ b/services/sync/modules/engines/clients.js
@@ -43,16 +43,17 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/ext/StringBundle.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
+Cu.import("resource://services-sync/main.js");
 
 const CLIENTS_TTL = 1814400; // 21 days
 const CLIENTS_TTL_REFRESH = 604800; // 7 days
 
 function ClientsRec(collection, id) {
   CryptoWrapper.call(this, collection, id);
 }
 ClientsRec.prototype = {
@@ -102,51 +103,16 @@ ClientEngine.prototype = {
       stats.hasMobile = stats.hasMobile || type == "mobile";
       stats.names.push(name);
       stats.numClients++;
     }
 
     return stats;
   },
 
-  // Remove any commands for the local client and mark it for upload
-  clearCommands: function clearCommands() {
-    delete this.localCommands;
-    this._tracker.addChangedID(this.localID);
-  },
-
-  // Send a command+args pair to each remote client
-  sendCommand: function sendCommand(command, args) {
-    // Helper to determine if the client already has this command
-    let notDupe = function(other) other.command != command ||
-      JSON.stringify(other.args) != JSON.stringify(args);
-
-    // Package the command/args pair into an object
-    let action = {
-      command: command,
-      args: args,
-    };
-
-    // Send the command to each remote client
-    for (let [id, client] in Iterator(this._store._remoteClients)) {
-      // Set the action to be a new commands array if none exists
-      if (client.commands == null)
-        client.commands = [action];
-      // Add the new action if there are no duplicates
-      else if (client.commands.every(notDupe))
-        client.commands.push(action);
-      // Must have been a dupe.. skip!
-      else
-        continue;
-
-      this._log.trace("Client " + id + " got a new action: " + [command, args]);
-      this._tracker.addChangedID(id);
-    }
-  },
-
   get localID() {
     // Generate a random GUID id we don't have one
     let localID = Svc.Prefs.get("client.GUID", "");
     return localID == "" ? this.localID = Utils.makeGUID() : localID;
   },
   set localID(value) Svc.Prefs.set("client.GUID", value),
 
   get localName() {
@@ -216,16 +182,159 @@ ClientEngine.prototype = {
       return base;
 
     // It's a bad client record. Save it to be deleted at the end of the sync.
     this._log.debug("Bad client record detected. Scheduling for deletion.");
     this._deleteId(item.id);
 
     // Neither try again nor error; we're going to delete it.
     return SyncEngine.kRecoveryStrategy.ignore;
+  },
+
+  /**
+   * A hash of valid commands that the client knows about. The key is a command
+   * and the value is a hash containing information about the command such as
+   * number of arguments and description.
+   */
+  _commands: {
+    resetAll:    { args: 0, desc: "Clear temporary local data for all engines" },
+    resetEngine: { args: 1, desc: "Clear temporary local data for engine" },
+    wipeAll:     { args: 0, desc: "Delete all client data for all engines" },
+    wipeEngine:  { args: 1, desc: "Delete all client data for engine" },
+    logout:      { args: 0, desc: "Log out client" }
+  },
+
+  /**
+   * Remove any commands for the local client and mark it for upload.
+   */
+  clearCommands: function clearCommands() {
+    delete this.localCommands;
+    this._tracker.addChangedID(this.localID);
+  },
+
+  /**
+   * Sends a command+args pair to a specific client.
+   *
+   * @param command Command string
+   * @param args Array of arguments/data for command
+   * @param clientId Client to send command to
+   */
+  _sendCommandToClient: function sendCommandToClient(command, args, clientId) {
+    this._log.trace("Sending " + command + " to " + clientId);
+
+    let client = this._store._remoteClients[clientId];
+    if (!client) {
+      throw new Error("Unknown remote client ID: '" + clientId + "'.");
+    }
+
+    // notDupe compares two commands and returns if they are not equal.
+    let notDupe = function(other) {
+      return other.command != command || !Utils.deepEquals(other.args, args);
+    };
+
+    let action = {
+      command: command,
+      args: args,
+    };
+
+    if (!client.commands) {
+      client.commands = [action];
+    }
+    // Add the new action if there are no duplicates.
+    else if (client.commands.every(notDupe)) {
+      client.commands.push(action);
+    }
+    // It must be a dupe. Skip.
+    else {
+      return;
+    }
+
+    this._log.trace("Client " + clientId + " got a new action: " + [command, args]);
+    this._tracker.addChangedID(clientId);
+  },
+
+  /**
+   * Check if the local client has any remote commands and perform them.
+   *
+   * @return false to abort sync
+   */
+  processIncomingCommands: function processIncomingCommands() {
+    return this._notify("clients:process-commands", "", function() {
+      let commands = this.localCommands;
+
+      // Immediately clear out the commands as we've got them locally.
+      this.clearCommands();
+
+      // Process each command in order.
+      for each ({command: command, args: args} in commands) {
+        this._log.debug("Processing command: " + command + "(" + args + ")");
+
+        let engines = [args[0]];
+        switch (command) {
+          case "resetAll":
+            engines = null;
+            // Fallthrough
+          case "resetEngine":
+            Weave.Service.resetClient(engines);
+            break;
+          case "wipeAll":
+            engines = null;
+            // Fallthrough
+          case "wipeEngine":
+            Weave.Service.wipeClient(engines);
+            break;
+          case "logout":
+            Weave.Service.logout();
+            return false;
+          default:
+            this._log.debug("Received an unknown command: " + command);
+            break;
+        }
+      }
+
+      return true;
+    })();
+  },
+
+  /**
+   * Validates and sends a command to a client or all clients.
+   *
+   * Calling this does not actually sync the command data to the server. If the
+   * client already has the command/args pair, it won't receive a duplicate
+   * command.
+   *
+   * @param command
+   *        Command to invoke on remote clients
+   * @param args
+   *        Array of arguments to give to the command
+   * @param clientId
+   *        Client ID to send command to. If undefined, send to all remote
+   *        clients.
+   */
+  sendCommand: function sendCommand(command, args, clientId) {
+    let commandData = this._commands[command];
+    // Don't send commands that we don't know about.
+    if (!commandData) {
+      this._log.error("Unknown command to send: " + command);
+      return;
+    }
+    // Don't send a command with the wrong number of arguments.
+    else if (!args || args.length != commandData.args) {
+      this._log.error("Expected " + commandData.args + " args for '" +
+                      command + "', but got " + args);
+      return;
+    }
+
+    if (clientId) {
+      this._sendCommandToClient(command, args, clientId);
+    } else {
+      for (let id in this._store._remoteClients) {
+        this._sendCommandToClient(command, args, id);
+      }
+    }
   }
 };
 
 function ClientStore(name) {
   Store.call(this, name);
 }
 ClientStore.prototype = {
   __proto__: Store.prototype,
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -17,16 +17,17 @@
  * the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mills <thunder@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
  *   Richard Newman <rnewman@mozilla.com>
+ *   Allison Naaktgeboren <ally@mozilla.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
@@ -248,27 +249,27 @@ HistoryStore.prototype = {
     records.length = k; // truncate array
 
     // Nothing to do.
     if (!records.length) {
       return failed;
     }
 
     let cb = Async.makeSyncCallback();
-    let onPlace = function onPlace(result, placeInfo) {
-      if (!Components.isSuccessCode(result)) {
+    let updatePlacesCallback = { 
+      handleError: function handleError(resultCode, placeInfo) {
         failed.push(placeInfo.guid);
       }
     };
     let onComplete = function onComplete(subject, topic, data) {
       Svc.Obs.remove(TOPIC_UPDATEPLACES_COMPLETE, onComplete);
       cb();
     };
     Svc.Obs.add(TOPIC_UPDATEPLACES_COMPLETE, onComplete);
-    this._asyncHistory.updatePlaces(records, onPlace);
+    this._asyncHistory.updatePlaces(records, updatePlacesCallback);
     Async.waitForSyncCallback(cb);
     return failed;
   },
 
   /**
    * Converts a Sync history record to a mozIPlaceInfo.
    * 
    * Throws if an invalid record is encountered (invalid URI, etc.),
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -1456,20 +1456,19 @@ WeaveSvc.prototype = {
       case "wipeClient":
         this.wipeClient(Engines.getEnabled().map(function(e) e.name));
         break;
       case "wipeRemote":
         this.wipeRemote(Engines.getEnabled().map(function(e) e.name));
         break;
     }
 
-    // Process the incoming commands if we have any
     if (Clients.localCommands) {
       try {
-        if (!(this.processCommands())) {
+        if (!(Clients.processIncomingCommands())) {
           Status.sync = ABORT_SYNC_COMMAND;
           throw "aborting sync, process commands said so";
         }
 
         // Repeat remoteSetup in-case the commands forced us to reset
         if (!(this._remoteSetup(info)))
           throw "aborting sync, remote setup failed after processing commands";
       }
@@ -1812,30 +1811,32 @@ WeaveSvc.prototype = {
    * Wipe all remote user data by wiping the server then telling each remote
    * client to wipe itself.
    *
    * @param engines [optional]
    *        Array of engine names to wipe. If not given, all engines are used.
    */
   wipeRemote: function WeaveSvc_wipeRemote(engines)
     this._catch(this._notify("wipe-remote", "", function() {
-      // Make sure stuff gets uploaded
+      // Make sure stuff gets uploaded.
       this.resetClient(engines);
 
-      // Clear out any server data
+      // Clear out any server data.
       this.wipeServer(engines);
 
-      // Only wipe the engines provided
-      if (engines)
-        engines.forEach(function(e) this.prepCommand("wipeEngine", [e]), this);
-      // Tell the remote machines to wipe themselves
-      else
-        this.prepCommand("wipeAll", []);
+      // Only wipe the engines provided.
+      if (engines) {
+        engines.forEach(function(e) Clients.sendCommand("wipeEngine", [e]), this);
+      }
+      // Tell the remote machines to wipe themselves.
+      else {
+        Clients.sendCommand("wipeAll", []);
+      }
 
-      // Make sure the changed clients get updated
+      // Make sure the changed clients get updated.
       Clients.sync();
     }))(),
 
   /**
    * Reset local service information like logs, sync times, caches.
    */
   resetService: function WeaveSvc_resetService()
     this._catch(this._notify("reset-service", "", function() {
@@ -1867,113 +1868,25 @@ WeaveSvc.prototype = {
         engines = Engines.get(engines);
 
       // Have each engine drop any temporary meta data
       for each (let engine in engines)
         engine.resetClient();
     }))(),
 
   /**
-   * A hash of valid commands that the client knows about. The key is a command
-   * and the value is a hash containing information about the command such as
-   * number of arguments and description.
-   */
-  _commands: [
-    ["resetAll", 0, "Clear temporary local data for all engines"],
-    ["resetEngine", 1, "Clear temporary local data for engine"],
-    ["wipeAll", 0, "Delete all client data for all engines"],
-    ["wipeEngine", 1, "Delete all client data for engine"],
-    ["logout", 0, "Log out client"],
-  ].reduce(function WeaveSvc__commands(commands, entry) {
-    commands[entry[0]] = {};
-    for (let [i, attr] in Iterator(["args", "desc"]))
-      commands[entry[0]][attr] = entry[i + 1];
-    return commands;
-  }, {}),
-
-  /**
-   * Check if the local client has any remote commands and perform them.
+   * Fetch storage info from the server.
    *
-   * @return False to abort sync
-   */
-  processCommands: function WeaveSvc_processCommands()
-    this._notify("process-commands", "", function() {
-      // Immediately clear out the commands as we've got them locally
-      let commands = Clients.localCommands;
-      Clients.clearCommands();
-
-      // Process each command in order
-      for each ({command: command, args: args} in commands) {
-        this._log.debug("Processing command: " + command + "(" + args + ")");
-
-        let engines = [args[0]];
-        switch (command) {
-          case "resetAll":
-            engines = null;
-            // Fallthrough
-          case "resetEngine":
-            this.resetClient(engines);
-            break;
-          case "wipeAll":
-            engines = null;
-            // Fallthrough
-          case "wipeEngine":
-            this.wipeClient(engines);
-            break;
-          case "logout":
-            this.logout();
-            return false;
-          default:
-            this._log.debug("Received an unknown command: " + command);
-            break;
-        }
-      }
-
-      return true;
-    })(),
-
-  /**
-   * Prepare to send a command to each remote client. Calling this doesn't
-   * actually sync the command data to the server. If the client already has
-   * the command/args pair, it won't get a duplicate action.
-   *
-   * @param command
-   *        Command to invoke on remote clients
-   * @param args
-   *        Array of arguments to give to the command
-   */
-  prepCommand: function WeaveSvc_prepCommand(command, args) {
-    let commandData = this._commands[command];
-    // Don't send commands that we don't know about
-    if (commandData == null) {
-      this._log.error("Unknown command to send: " + command);
-      return;
-    }
-    // Don't send a command with the wrong number of arguments
-    else if (args == null || args.length != commandData.args) {
-      this._log.error("Expected " + commandData.args + " args for '" +
-                      command + "', but got " + args);
-      return;
-    }
-
-    // Send the command to all remote clients
-    this._log.debug("Sending clients: " + [command, args, commandData.desc]);
-    Clients.sendCommand(command, args);
-  },
-
-  /**
-   * Fetch storage info from the server.
-   * 
    * @param type
    *        String specifying what info to fetch from the server. Must be one
    *        of the INFO_* values. See Sync Storage Server API spec for details.
    * @param callback
    *        Callback function with signature (error, data) where `data' is
    *        the return value from the server already parsed as JSON.
-   * 
+   *
    * @return RESTRequest instance representing the request, allowing callers
    *         to cancel the request.
    */
   getStorageInfo: function getStorageInfo(type, callback) {
     if (STORAGE_INFO_TYPES.indexOf(type) == -1) {
       throw "Invalid value for 'type': " + type;
     }
 
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -234,13 +234,223 @@ add_test(function test_client_name_chang
   do_check_true(tracker.score > initialScore);
   do_check_true(tracker.score >= SCORE_INCREMENT_XLARGE);
 
   Svc.Obs.notify("weave:engine:stop-tracking");
 
   run_next_test();
 });
 
+add_test(function test_send_command() {
+  _("Verifies _sendCommandToClient puts commands in the outbound queue.");
+
+  let store = Clients._store;
+  let tracker = Clients._tracker;
+  let remoteId = Utils.makeGUID();
+  let rec = new ClientsRec("clients", remoteId);
+
+  store.create(rec);
+  let remoteRecord = store.createRecord(remoteId, "clients");
+
+  let action = "testCommand";
+  let args = ["foo", "bar"];
+
+  Clients._sendCommandToClient(action, args, remoteId);
+
+  let newRecord = store._remoteClients[remoteId];
+  do_check_neq(newRecord, undefined);
+  do_check_eq(newRecord.commands.length, 1);
+
+  let command = newRecord.commands[0];
+  do_check_eq(command.command, action);
+  do_check_eq(command.args.length, 2);
+  do_check_eq(command.args, args);
+
+  do_check_neq(tracker.changedIDs[remoteId], undefined);
+
+  run_next_test();
+});
+
+add_test(function test_command_validation() {
+  _("Verifies that command validation works properly.");
+
+  let store = Clients._store;
+
+  let testCommands = [
+    ["resetAll",    [],       true ],
+    ["resetAll",    ["foo"],  false],
+    ["resetEngine", ["tabs"], true ],
+    ["resetEngine", [],       false],
+    ["wipeAll",     [],       true ],
+    ["wipeAll",     ["foo"],  false],
+    ["wipeEngine",  ["tabs"], true ],
+    ["wipeEngine",  [],       false],
+    ["logout",      [],       true ],
+    ["logout",      ["foo"],  false],
+    ["__UNKNOWN__", [],       false]
+  ];
+
+  for each (let [action, args, expectedResult] in testCommands) {
+    let remoteId = Utils.makeGUID();
+    let rec = new ClientsRec("clients", remoteId);
+
+    store.create(rec);
+    store.createRecord(remoteId, "clients");
+
+    Clients.sendCommand(action, args, remoteId);
+
+    let newRecord = store._remoteClients[remoteId];
+    do_check_neq(newRecord, undefined);
+
+    if (expectedResult) {
+      _("Ensuring command is sent: " + action);
+      do_check_eq(newRecord.commands.length, 1);
+
+      let command = newRecord.commands[0];
+      do_check_eq(command.command, action);
+      do_check_eq(command.args, args);
+
+      do_check_neq(Clients._tracker, undefined);
+      do_check_neq(Clients._tracker.changedIDs[remoteId], undefined);
+    } else {
+      _("Ensuring command is scrubbed: " + action);
+      do_check_eq(newRecord.commands, undefined);
+
+      if (store._tracker) {
+        do_check_eq(Clients._tracker[remoteId], undefined);
+      }
+    }
+
+  }
+  run_next_test();
+});
+
+add_test(function test_command_duplication() {
+  _("Ensures duplicate commands are detected and not added");
+
+  let store = Clients._store;
+  let remoteId = Utils.makeGUID();
+  let rec = new ClientsRec("clients", remoteId);
+  store.create(rec);
+  store.createRecord(remoteId, "clients");
+
+  let action = "resetAll";
+  let args = [];
+
+  Clients.sendCommand(action, args, remoteId);
+  Clients.sendCommand(action, args, remoteId);
+
+  let newRecord = store._remoteClients[remoteId];
+  do_check_eq(newRecord.commands.length, 1);
+
+  _("Check variant args length");
+  newRecord.commands = [];
+
+  action = "resetEngine";
+  Clients.sendCommand(action, [{ x: "foo" }], remoteId);
+  Clients.sendCommand(action, [{ x: "bar" }], remoteId);
+
+  _("Make sure we spot a real dupe argument.");
+  Clients.sendCommand(action, [{ x: "bar" }], remoteId);
+
+  do_check_eq(newRecord.commands.length, 2);
+
+  run_next_test();
+});
+
+add_test(function test_command_invalid_client() {
+  _("Ensures invalid client IDs are caught");
+
+  let id = Utils.makeGUID();
+  let error;
+
+  try {
+    Clients.sendCommand("wipeAll", [], id);
+  } catch (ex) {
+    error = ex;
+  }
+
+  do_check_eq(error.message.indexOf("Unknown remote client ID: "), 0);
+
+  run_next_test();
+});
+
+add_test(function test_process_incoming_commands() {
+  _("Ensures local commands are executed");
+
+  Clients.localCommands = [{ command: "logout", args: [] }];
+
+  let ev = "weave:service:logout:finish";
+
+  var handler = function() {
+    Svc.Obs.remove(ev, handler);
+    run_next_test();
+  };
+
+  Svc.Obs.add(ev, handler);
+
+  // logout command causes processIncomingCommands to return explicit false.
+  do_check_false(Clients.processIncomingCommands());
+});
+
+add_test(function test_command_sync() {
+  _("Ensure that commands are synced across clients.");
+  Svc.Prefs.set("clusterURL", "http://localhost:8080/");
+  Svc.Prefs.set("username", "foo");
+
+  generateNewKeys();
+
+  let global = new ServerWBO('global',
+                             {engines: {clients: {version: Clients.version,
+                                                  syncID: Clients.syncID}}});
+  let coll = new ServerCollection();
+  let clientwbo = coll.wbos[Clients.localID] = new ServerWBO(Clients.localID);
+  let server = httpd_setup({
+      "/1.1/foo/storage/meta/global": global.handler(),
+      "/1.1/foo/storage/clients": coll.handler()
+  });
+  let remoteId = Utils.makeGUID();
+  let remotewbo = coll.wbos[remoteId] = new ServerWBO(remoteId);
+  server.registerPathHandler(
+    "/1.1/foo/storage/clients/" + Clients.localID, clientwbo.handler());
+  server.registerPathHandler(
+    "/1.1/foo/storage/clients/" + remoteId, remotewbo.handler());
+
+  _("Create remote client record");
+  let rec = new ClientsRec("clients", remoteId);
+  Clients._store.create(rec);
+  let remoteRecord = Clients._store.createRecord(remoteId, "clients");
+  Clients.sendCommand("wipeAll", []);
+
+  let clientRecord = Clients._store._remoteClients[remoteId];
+  do_check_neq(clientRecord, undefined);
+  do_check_eq(clientRecord.commands.length, 1);
+
+  try {
+    Clients.sync();
+    do_check_neq(clientwbo.payload, undefined);
+    do_check_true(Clients.lastRecordUpload > 0);
+
+    do_check_neq(remotewbo.payload, undefined);
+
+    Svc.Prefs.set("client.GUID", remoteId);
+    Clients._resetClient();
+    do_check_eq(Clients.localID, remoteId);
+    Clients.sync();
+    do_check_neq(Clients.localCommands, undefined);
+    do_check_eq(Clients.localCommands.length, 1);
+
+    let command = Clients.localCommands[0];
+    do_check_eq(command.command, "wipeAll");
+    do_check_eq(command.args.length, 0);
+
+  } finally {
+    Svc.Prefs.resetBranch("");
+    Records.clearCache();
+    server.stop(run_next_test);
+  }
+});
+
 function run_test() {
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.Engine.Clients").level = Log4Moz.Level.Trace;
   run_next_test();
 }
old mode 100644
new mode 100755
--- a/testing/tps/INSTALL.sh
+++ b/testing/tps/INSTALL.sh
@@ -50,22 +50,41 @@ cd ${TARGET}
 if [ -z "${VIRTUAL_ENV}" ]
 then
     echo "virtualenv wasn't installed correctly, aborting"
     exit 1
 fi
 
 # install TPS
 cd ${CWD}
-python setup.py develop
+python setup.py install
 
 if [ "$?" -gt 0 ]
 then
   exit 1
 fi
 
+CONFIG="`find ${VIRTUAL_ENV} -name config.json.in`"
+NEWCONFIG=${CONFIG:0:${#CONFIG}-3}
+
+cd "../../services/sync/tests/tps"
+TESTDIR="`pwd`"
+
+cd "../../tps"
+EXTDIR="`pwd`"
+
+sed 's|__TESTDIR__|'"${TESTDIR}"'|' "${CONFIG}" | sed 's|__EXTENSIONDIR__|'"${EXTDIR}"'|' > "${NEWCONFIG}"
+rm ${CONFIG}
+
+echo
+echo "***********************************************************************"
 echo
 echo "To run TPS, activate the virtualenv using:"
 echo "  source ${TARGET}/${BIN_NAME}"
 echo "then execute tps using:"
 echo "  runtps --binary=/path/to/firefox"
 echo
 echo "See runtps --help for all options"
+echo
+echo "To change your TPS config, please edit the file: "
+echo "${NEWCONFIG}"
+echo
+echo "***********************************************************************"
deleted file mode 100644
--- a/testing/tps/config.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{ 
-  "account": {
-    "serverURL": "",
-    "admin-secret": "",
-    "username": "crossweaveservices@mozilla.com",
-    "password": "crossweaveservicescrossweaveservices",
-    "passphrase": "r-jwcbc-zgf42-fjn72-p5vpp-iypmi"
-  },
-  "resultstore": {
-    "host": "brasstacks.mozilla.com",
-    "path": "/resultserv/post/"
-  },
-  "email": {
-    "username": "crossweave@mozilla.com",
-    "password": "",
-    "passednotificationlist": ["crossweave@mozilla.com"],
-    "notificationlist": ["crossweave@mozilla.com"]
-  },
-  "platform": "win32",
-  "os": "win7",
-  "es": "localhost:9200"
-}
-
new file mode 100644
--- /dev/null
+++ b/testing/tps/config/README.txt
@@ -0,0 +1,7 @@
+To edit the TPS configuration, do not edit config.json.in in the tree. 
+Instead, edit config.json inside your virtualenv; it will be located at
+something like:
+
+  (linux): /path/to/virtualenv/lib/python2.6/site-packages/tps-0.2.40-py2.6.egg/tps/config.json
+  (win): /path/to/virtualenv/Lib/site-packages/tps-0.2.40-py2.6.egg/tps/config.json
+
new file mode 100644
--- /dev/null
+++ b/testing/tps/config/config.json.in
@@ -0,0 +1,25 @@
+{ 
+  "account": {
+    "serverURL": "",
+    "admin-secret": "",
+    "username": "crossweaveservices@mozilla.com",
+    "password": "crossweaveservicescrossweaveservices",
+    "passphrase": "r-jwcbc-zgf42-fjn72-p5vpp-iypmi"
+  },
+  "resultstore": {
+    "host": "brasstacks.mozilla.com",
+    "path": "/resultserv/post/"
+  },
+  "email": {
+    "username": "crossweave@mozilla.com",
+    "password": "",
+    "passednotificationlist": ["crossweave@mozilla.com"],
+    "notificationlist": ["crossweave@mozilla.com"]
+  },
+  "platform": "win32",
+  "os": "win7",
+  "es": "localhost:9200",
+  "testdir": "__TESTDIR__",
+  "extensiondir": "__EXTENSIONDIR__"
+}
+
--- a/testing/tps/setup.py
+++ b/testing/tps/setup.py
@@ -53,23 +53,26 @@ setup(name='tps',
       version=version,
       description='run automated multi-profile sync tests',
       long_description="""\
 """,
       classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
       keywords='',
       author='Jonathan Griffin',
       author_email='jgriffin@mozilla.com',
-      url='http://hg.mozilla.org/services/tps',
+      url='http://hg.mozilla.org/services/services-central',
       license='MPL',
       dependency_links = [
          "http://people.mozilla.org/~jgriffin/packages/"
       ],
       packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
       include_package_data=True,
       zip_safe=False,
       install_requires=deps,
       entry_points="""
       # -*- Entry points: -*-
       [console_scripts]
       runtps = tps.cli:main
       """,
+      data_files=[
+        ('tps', ['config/config.json.in']),
+      ],
       )
--- a/testing/tps/tps/cli.py
+++ b/testing/tps/tps/cli.py
@@ -33,16 +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 *****
 
 import json
 import optparse
 import os
+import sys
 import logging
 
 from threading import RLock
 
 from tps import TPSFirefoxRunner, TPSPulseMonitor, TPSTestRunner
 
 def main():
   parser = optparse.OptionParser()
@@ -71,35 +72,53 @@ def main():
   parser.add_option("--binary",
                     action = "store", type = "string", dest = "binary",
                     default = None,
                     help = "path to the Firefox binary, specified either as "
                            "a local file or a url; if omitted, the PATH "
                            "will be searched;")
   parser.add_option("--configfile",
                     action = "store", type = "string", dest = "configfile",
-                    default = "config.json",
+                    default = None,
                     help = "path to the config file to use "
                            "[default: %default]")
   parser.add_option("--pulsefile",
                     action = "store", type = "string", dest = "pulsefile",
                     default = None,
                     help = "path to file containing a pulse message in "
                            "json format that you want to inject into the monitor")
   (options, args) = parser.parse_args()
 
+  configfile = options.configfile
+  if configfile is None:
+    if os.environ.get('VIRTUAL_ENV'):
+      configfile = os.path.join(os.path.dirname(__file__), 'config.json')
+    else:
+      raise Exception("Unable to find config.json in a VIRTUAL_ENV; you must "
+                      "specify a config file using the --configfile option")
+
   # load the config file
-  f = open(options.configfile, 'r')
+  f = open(configfile, 'r')
   configcontent = f.read()
   f.close()
   config = json.loads(configcontent)
 
   rlock = RLock()
  
-  extensionDir = os.path.join(os.getcwd(), "..", "..", "services", "sync", "tps")
+  extensionDir = config.get("extensiondir")
+  if not extensionDir or extensionDir == '__EXTENSIONDIR__':
+    extensionDir = os.path.join(os.getcwd(), "..", "..", "services", "sync", "tps")
+  else:
+    if sys.platform == 'win32':
+      # replace msys-style paths with proper Windows paths
+      import re
+      m = re.match('^\/\w\/', extensionDir)
+      if m:
+        extensionDir = "%s:/%s" % (m.group(0)[1:2], extensionDir[3:])
+        extensionDir = extensionDir.replace("/", "\\")
 
   if options.binary is None:
     # If no binary is specified, start the pulse build monitor, and wait
     # until we receive build notifications before running tests.
     monitor = TPSPulseMonitor(extensionDir,
                               config=config,
                               autolog=options.autolog,
                               emailresults=options.emailresults,
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -48,17 +48,17 @@
 #else
 
 #include "nsISupportsArray.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMWindow.h"
 #include "nsIWindowWatcher.h"
-#include "nsDependentString.h"
+#include "nsPromiseFlatString.h"
 #include "nsWidgetsCID.h"
 #include "nsILookAndFeel.h"
 #include "nsToolkitCompsCID.h"
 
 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
 
 #define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
 
@@ -80,17 +80,17 @@ NS_IMETHODIMP nsAlertsService::ShowAlert
                                                      const nsAString & aAlertCookie,
                                                      nsIObserver * aAlertListener,
                                                      const nsAString & aAlertName)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
 
     if (aAlertListener)
-      cpc->AddRemoteAlertObserver(nsDependentString(aAlertCookie), aAlertListener);
+      cpc->AddRemoteAlertObserver(PromiseFlatString(aAlertCookie), aAlertListener);
 
     cpc->SendShowAlertNotification(nsAutoString(aImageUrl),
                                    nsAutoString(aAlertTitle),
                                    nsAutoString(aAlertText),
                                    aAlertTextClickable,
                                    nsAutoString(aAlertCookie),
                                    nsAutoString(aAlertName));
     return NS_OK;
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Allison Naaktgeboren <ally@mozilla.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
@@ -561,18 +562,23 @@ public:
 
     nsCOMPtr<nsIURI> uri;
     (void)NS_NewURI(getter_AddRefs(uri), mPlace.spec);
 
     // We do not notify about the frecency of the place.
     nsCOMPtr<mozIPlaceInfo> place =
       new PlaceInfo(mPlace.placeId, mPlace.guid, uri.forget(), mPlace.title,
                     -1, visits);
+    if (NS_SUCCEEDED(mResult)) {
+      (void)mCallback->HandleResult(place);
+    }
+    else {
+      (void)mCallback->HandleError(mResult, place);
+    }
 
-    (void)mCallback->OnComplete(mResult, place);
     return NS_OK;
   }
 
 private:
   /**
    * Callers MUST hold a strong reference to this that outlives us because we
    * may be created off of the main thread, and therefore cannot call AddRef on
    * this object (and therefore cannot hold a strong reference to it).
--- a/toolkit/components/places/mozIAsyncHistory.idl
+++ b/toolkit/components/places/mozIAsyncHistory.idl
@@ -117,49 +117,59 @@ interface mozIPlaceInfo : nsISupports
    */
   [implicit_jscontext]
   readonly attribute jsval visits;
 };
 
 /**
  * @status EXPERIMENTAL
  */
-[scriptable,function, uuid(3b97ca3c-5ea8-424f-b429-797477c52302)]
+[scriptable, uuid(eb0b406f-8f57-4f2b-b0da-8883684b138a)]
 interface mozIVisitInfoCallback : nsISupports
 {
   /**
-   * Called for each visit added, title change, or guid change when passed to
-   * mozIAsyncHistory::updatePlaces.
+   * Called when the given mozIPlaceInfo object could not be processed.
    *
    * @param aResultCode
-   *        nsresult of the change indicating success or failure reason.
+   *        nsresult indicating the failure reason.
    * @param aPlaceInfo
    *        The information that was being entered into the database.
    */
-  void onComplete(in nsresult aResultCode,
-                  in mozIPlaceInfo aPlaceInfo);
+  void handleError(in nsresult aResultCode,
+                   in mozIPlaceInfo aPlaceInfo);
+
+  /**
+   * Called for each visit added, title change, or guid change when passed to
+   * mozIAsyncHistory::updatePlaces.  If more than one operation is done for 
+   * a given visit, only one callback will be given (i.e. title change and 
+   * add visit).
+   *
+   * @param aPlaceInfo
+   *        The information that was being entered into the database.
+   */
+  void handleResult(in mozIPlaceInfo aPlaceInfo);
+
 };
 
 /**
  * @status EXPERIMENTAL
  */
 [scriptable, uuid(f79ca67c-7e57-4511-a400-ea31001c762f)]
 interface mozIAsyncHistory : nsISupports
 {
   /**
    * Adds a set of visits for one or more mozIPlaceInfo objects, and updates
    * each mozIPlaceInfo's title or guid.
    *
    * @param aPlaceInfo
    *        The mozIPlaceInfo object[s] containing the information to store or
    *        update.  This can be a single object, or an array of objects.
    * @param [optional] aCallback
-   *        Callback to be notified for each visit addition, title change, or
-   *        guid change.  If more than one operation is done for a given visit,
-   *        only one callback will be given (i.e. title change and add visit).
+   *        A mozIVisitInfoCallback object which consists of callbacks to be 
+   *        notified for successful and/or failed changes.
    *
    * @throws NS_ERROR_INVALID_ARG
    *         - Passing in NULL for aPlaceInfo.
    *         - Not providing at least one valid guid, or uri for all
    *           mozIPlaceInfo object[s].
    *         - Not providing an array or nothing for the visits property of
    *           mozIPlaceInfo.
    *         - Not providing a visitDate and transitionType for each
--- a/toolkit/components/places/tests/unit/test_async_history_api.js
+++ b/toolkit/components/places/tests/unit/test_async_history_api.js
@@ -154,16 +154,48 @@ function do_check_title_for_uri(aURI,
     "WHERE url = :url "
   );
   stmt.params.url = aURI.spec;
   do_check_true(stmt.executeStep(), stack);
   do_check_eq(stmt.row.title, aTitle, stack);
   stmt.finalize();
 }
 
+/**
+ * Default callback handler throws when success is unexpected.
+ *
+ * @param handleErrorFunc
+ *        The error handling function
+ */
+function expectHandleError(handleErrorFunc)
+{
+  return {
+    handleError: handleErrorFunc,
+    handleResult: function handleResult(aPlaceInfo) {
+      do_throw("Unexpected success.");
+    }
+  };
+}
+/**
+ * Default callback handler throws when failure is unexpected.
+ *
+ * @param handleResultFunc
+ *        The success handling function
+ */
+
+function expectHandleResult(handleResultFunc)
+{
+  return {
+    handleError: function handleError(aResultCode, aPlacesInfo) {
+      do_throw("Unexpected error: " + aResultCode);
+    },
+    handleResult: handleResultFunc
+  };
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Functions
 
 function test_interface_exists()
 {
   let history = Cc["@mozilla.org/browser/history;1"].getService(Ci.nsISupports);
   do_check_true(history instanceof Ci.mozIAsyncHistory);
   run_next_test();
@@ -417,77 +449,75 @@ function test_non_addable_uri_errors()
       // NetUtil.newURI() can throw if e.g. our app knows about imap://
       // but the account is not set up and so the URL is invalid for us.
       // Note this in the log but ignore as it's not the subject of this test.
       do_log_info("Could not construct URI for '" + url + "'; ignoring");
     }
   });
 
   let callbackCount = 0;
-  gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
+  gHistory.updatePlaces(places, expectHandleError(function(aResultCode, aPlaceInfo) {
     do_log_info("Checking '" + aPlaceInfo.uri.spec + "'");
     do_check_eq(aResultCode, Cr.NS_ERROR_INVALID_ARG);
     do_check_false(gGlobalHistory.isVisited(aPlaceInfo.uri));
 
     // If we have had all of our callbacks, continue running tests.
     if (++callbackCount == places.length) {
       waitForAsyncUpdates(run_next_test);
     }
-  });
+  }));
 }
 
 function test_duplicate_guid_errors()
 {
   // This test ensures that trying to add a visit, with a guid already found in
   // another visit, fails.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_duplicate_guid_fails_first"),
     visits: [
       new VisitInfo(),
     ],
   };
 
   do_check_false(gGlobalHistory.isVisited(place.uri));
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     do_check_true(gGlobalHistory.isVisited(place.uri));
 
     let badPlace = {
       uri: NetUtil.newURI(TEST_DOMAIN + "test_duplicate_guid_fails_second"),
       visits: [
         new VisitInfo(),
       ],
       guid: aPlaceInfo.guid,
     };
 
     do_check_false(gGlobalHistory.isVisited(badPlace.uri));
-    gHistory.updatePlaces(badPlace, function(aResultCode, aPlaceInfo) {
+    gHistory.updatePlaces(badPlace, expectHandleError(function(aResultCode, aPlaceInfo) {
       do_check_eq(aResultCode, Cr.NS_ERROR_STORAGE_CONSTRAINT);
       do_check_false(gGlobalHistory.isVisited(badPlace.uri));
 
       waitForAsyncUpdates(run_next_test);
-    });
-  });
+    }));
+  }));
 }
 
 function test_invalid_referrerURI_ignored()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN +
                         "test_invalid_referrerURI_ignored"),
     visits: [
       new VisitInfo(),
     ],
   };
   place.visits[0].referrerURI = NetUtil.newURI(place.uri.spec + "_unvisistedURI");
   do_check_false(gGlobalHistory.isVisited(place.uri));
   do_check_false(gGlobalHistory.isVisited(place.visits[0].referrerURI));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
 
     // Check to make sure we do not visit the invalid referrer.
     do_check_false(gGlobalHistory.isVisited(place.visits[0].referrerURI));
 
     // Check to make sure from_visit is zero in database.
     let stmt = DBConn().createStatement(
@@ -496,65 +526,63 @@ function test_invalid_referrerURI_ignore
       "WHERE id = :visit_id"
     );
     stmt.params.visit_id = aPlaceInfo.visits[0].visitId;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.from_visit, 0);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_nonnsIURI_referrerURI_ignored()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN +
                         "test_nonnsIURI_referrerURI_ignored"),
     visits: [
       new VisitInfo(),
     ],
   };
   place.visits[0].referrerURI = place.uri.spec + "_nonnsIURI";
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
 
     // Check to make sure from_visit is zero in database.
     let stmt = DBConn().createStatement(
       "SELECT from_visit " +
       "FROM moz_historyvisits " +
       "WHERE id = :visit_id"
     );
     stmt.params.visit_id = aPlaceInfo.visits[0].visitId;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.from_visit, 0);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_invalid_sessionId_ignored()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN +
                         "test_invalid_sessionId_ignored"),
     visits: [
       new VisitInfo(),
     ],
   };
   place.visits[0].sessionId = -1;
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
 
     // Check to make sure we do not persist bogus sessionId with the visit.
     let visit = aPlaceInfo.visits[0];
     do_check_neq(visit.sessionId, place.visits[0].sessionId);
 
     // Check to make sure we do not persist bogus sessionId in database.
@@ -564,17 +592,17 @@ function test_invalid_sessionId_ignored(
       "WHERE id = :visit_id"
     );
     stmt.params.visit_id = visit.visitId;
     do_check_true(stmt.executeStep());
     do_check_neq(stmt.row.session, place.visits[0].sessionId);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_unstored_sessionId_ignored()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN +
                         "test_unstored_sessionId_ignored"),
     visits: [
@@ -590,18 +618,17 @@ function test_unstored_sessionId_ignored
   do_check_true(stmt.executeStep());
   let maxSessionId = stmt.row.max_session;
   stmt.finalize();
 
   // Create bogus sessionId that is not in database.
   place.visits[0].sessionId = maxSessionId + 10;
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
 
     // Check to make sure we do not persist bogus sessionId with the visit.
     let visit = aPlaceInfo.visits[0];
     do_check_neq(visit.sessionId, place.visits[0].sessionId);
 
     // Check to make sure we do not persist bogus sessionId in the database.
@@ -613,17 +640,17 @@ function test_unstored_sessionId_ignored
 
     // Max sessionId should increase by 1 because we will generate a new
     // non-bogus sessionId.
     let newMaxSessionId = stmt.row.max_session;
     do_check_eq(maxSessionId + 1, newMaxSessionId);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 
 function test_old_referrer_ignored()
 {
   // This tests that a referrer for a visit which is not recent (specifically,
   // older than 15 minutes as per RECENT_EVENT_THRESHOLD) is not saved by
   // updatePlaces.
@@ -633,35 +660,33 @@ function test_old_referrer_ignored()
     visits: [
       new VisitInfo(TRANSITION_LINK, oldTime),
     ],
   };
 
   // First we must add our referrer to the history so that it is not ignored
   // as being invalid.
   do_check_false(gGlobalHistory.isVisited(referrerPlace.uri));
-  gHistory.updatePlaces(referrerPlace, function(aResultCode, aPlaceInfo) {
+  gHistory.updatePlaces(referrerPlace, expectHandleResult(function(aPlaceInfo) {
     // Now that the referrer is added, we can add a page with a valid
     // referrer to determine if the recency of the referrer is taken into
     // account.
-    do_check_true(Components.isSuccessCode(aResultCode));
     do_check_true(gGlobalHistory.isVisited(referrerPlace.uri));
 
     let visitInfo = new VisitInfo();
     visitInfo.referrerURI = referrerPlace.uri;
     let place = {
       uri: NetUtil.newURI(TEST_DOMAIN + "test_old_referrer_ignored_page"),
       visits: [
         visitInfo,
       ],
     };
 
     do_check_false(gGlobalHistory.isVisited(place.uri));
-    gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(place, expectHandleResult (function(aPlaceInfo) {
       do_check_true(gGlobalHistory.isVisited(place.uri));
 
       // Though the visit will not contain the referrer, we must examine the
       // database to be sure.
       do_check_eq(aPlaceInfo.visits[0].referrerURI, null);
       let stmt = DBConn().createStatement(
         "SELECT COUNT(1) AS count " +
         "FROM moz_historyvisits " +
@@ -669,55 +694,57 @@ function test_old_referrer_ignored()
         "AND from_visit = 0 "
       );
       stmt.params.page_url = place.uri.spec;
       do_check_true(stmt.executeStep());
       do_check_eq(stmt.row.count, 1);
       stmt.finalize();
 
       waitForAsyncUpdates(run_next_test);
-    });
-  });
+    }));
+  }));
 }
 
 function test_place_id_ignored()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_place_id_ignored_first"),
     visits: [
       new VisitInfo(),
     ],
   };
 
   do_check_false(gGlobalHistory.isVisited(place.uri));
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     do_check_true(gGlobalHistory.isVisited(place.uri));
 
     let placeId = aPlaceInfo.placeId;
     do_check_neq(placeId, 0);
 
     let badPlace = {
       uri: NetUtil.newURI(TEST_DOMAIN + "test_place_id_ignored_second"),
       visits: [
         new VisitInfo(),
       ],
       placeId: placeId,
     };
 
     do_check_false(gGlobalHistory.isVisited(badPlace.uri));
-    gHistory.updatePlaces(badPlace, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(badPlace, {
+      handleResult: function handleResult(aPlaceInfo) {
+        do_check_neq(aPlaceInfo.placeId, placeId);
+        do_check_true(gGlobalHistory.isVisited(badPlace.uri));
 
-      do_check_neq(aPlaceInfo.placeId, placeId);
-      do_check_true(gGlobalHistory.isVisited(badPlace.uri));
-
-      waitForAsyncUpdates(run_next_test);
+        waitForAsyncUpdates(run_next_test);
+      },
+      handleError: function handleError(aResultCode) {
+        do_throw("Unexpected error: " + aResultCode);
+      }
     });
-  });
+  }));
 }
 
 function test_observer_topic_dispatched_when_complete()
 {
   // We test a normal visit, and embeded visit, and a uri that would fail
   // the canAddURI test to make sure that the notification happens after *all*
   // of them have had a callback.
   let places = [
@@ -732,31 +759,38 @@ function test_observer_topic_dispatched_
       visits: [
         new VisitInfo(),
       ],
     },
   ];
   do_check_false(gGlobalHistory.isVisited(places[0].uri));
   do_check_false(gGlobalHistory.isVisited(places[1].uri));
 
-  const EXPECTED_COUNT = 3;
-  let callbackCount = 0;
+  const EXPECTED_COUNT_SUCCESS = 2;
+  const EXPECTED_COUNT_FAILURE = 1;
+  let callbackCountSuccess = 0;
+  let callbackCountFailure = 0;
 
-  gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
-    let checker = PlacesUtils.history.canAddURI(aPlaceInfo.uri) ?
-      do_check_true : do_check_false;
-    checker(Components.isSuccessCode(aResultCode));
-    callbackCount++;
+  gHistory.updatePlaces(places, {
+    handleResult: function handleResult(aPlaceInfo) {
+      let checker = PlacesUtils.history.canAddURI(aPlaceInfo.uri) ?
+        do_check_true : do_check_false;
+      callbackCountSuccess++;
+    },
+    handleError: function handleError(aResultCode, aPlaceInfo) {
+      callbackCountFailure++;
+    }
   });
 
   let observer = {
     observe: function(aSubject, aTopic, aData)
     {
       do_check_eq(aTopic, TOPIC_UPDATEPLACES_COMPLETE);
-      do_check_eq(callbackCount, EXPECTED_COUNT);
+      do_check_eq(callbackCountSuccess, EXPECTED_COUNT_SUCCESS);
+      do_check_eq(callbackCountFailure, EXPECTED_COUNT_FAILURE);
       Services.obs.removeObserver(observer, TOPIC_UPDATEPLACES_COMPLETE);
       waitForAsyncUpdates(run_next_test);
     },
   };
   Services.obs.addObserver(observer, TOPIC_UPDATEPLACES_COMPLETE, false);
 }
 
 function test_add_visit()
@@ -770,39 +804,38 @@ function test_add_visit()
   for (let transitionType = TRANSITION_LINK;
        transitionType <= TRANSITION_FRAMED_LINK;
        transitionType++) {
     place.visits.push(new VisitInfo(transitionType, VISIT_TIME));
   }
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
   let callbackCount = 0;
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     do_check_true(gGlobalHistory.isVisited(place.uri));
 
     // Check mozIPlaceInfo properties.
     do_check_true(place.uri.equals(aPlaceInfo.uri));
     do_check_eq(aPlaceInfo.frecency, -1); // We don't pass frecency here!
     do_check_eq(aPlaceInfo.title, place.title);
 
     // Check mozIVisitInfo properties.
     let visits = aPlaceInfo.visits;
     do_check_eq(visits.length, 1);
     let visit = visits[0];
     do_check_eq(visit.visitDate, VISIT_TIME);
     do_check_true(visit.transitionType >= TRANSITION_LINK &&
-                  visit.transitionType <= TRANSITION_FRAMED_LINK);
+                    visit.transitionType <= TRANSITION_FRAMED_LINK);
     do_check_true(visit.referrerURI === null);
 
     // For TRANSITION_EMBED visits, many properties will always be zero or
     // undefined.
     if (visit.transitionType == TRANSITION_EMBED) {
       // Check mozIPlaceInfo properties.
-      do_check_eq(aPlaceInfo.placeId, 0);
+      do_check_eq(aPlaceInfo.placeId, 0, '//');
       do_check_eq(aPlaceInfo.guid, null);
 
       // Check mozIVisitInfo properties.
       do_check_eq(visit.visitId, 0);
       do_check_eq(visit.sessionId, 0);
     }
     // But they should be valid for non-embed visits.
     else {
@@ -814,17 +847,17 @@ function test_add_visit()
       do_check_true(visit.visitId > 0);
       do_check_true(visit.sessionId > 0);
     }
 
     // If we have had all of our callbacks, continue running tests.
     if (++callbackCount == place.visits.length) {
       waitForAsyncUpdates(run_next_test);
     }
-  });
+  }));
 }
 
 function test_properties_saved()
 {
   // Check each transition type to make sure it is saved properly.
   let places = [];
   for (let transitionType = TRANSITION_LINK;
        transitionType <= TRANSITION_FRAMED_LINK;
@@ -837,18 +870,17 @@ function test_properties_saved()
         new VisitInfo(transitionType),
       ],
     };
     do_check_false(gGlobalHistory.isVisited(place.uri));
     places.push(place);
   }
 
   let callbackCount = 0;
-  gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(places, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
     let visit = aPlaceInfo.visits[0];
     print("TEST-INFO | test_properties_saved | updatePlaces callback for " +
           "transition type " + visit.transitionType);
 
     // Note that TRANSITION_EMBED should not be in the database.
     const EXPECTED_COUNT = visit.transitionType == TRANSITION_EMBED ? 0 : 1;
@@ -910,40 +942,38 @@ function test_properties_saved()
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, EXPECTED_COUNT);
     stmt.finalize();
 
     // If we have had all of our callbacks, continue running tests.
     if (++callbackCount == places.length) {
       waitForAsyncUpdates(run_next_test);
     }
-  });
+  }));
 }
 
 function test_guid_saved()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_saved"),
     guid: "__TESTGUID__",
     visits: [
       new VisitInfo(),
     ],
   };
   do_check_valid_places_guid(place.guid);
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
     do_check_eq(aPlaceInfo.guid, place.guid);
     do_check_guid_for_uri(uri, place.guid);
-
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_referrer_saved()
 {
   let places = [
     { uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_saved/referrer"),
       visits: [
         new VisitInfo(),
@@ -956,18 +986,17 @@ function test_referrer_saved()
     },
   ];
   places[1].visits[0].referrerURI = places[0].uri;
   do_check_false(gGlobalHistory.isVisited(places[0].uri));
   do_check_false(gGlobalHistory.isVisited(places[1].uri));
 
   let callbackCount = 0;
   let referrerSessionId;
-  gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(places, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
     let visit = aPlaceInfo.visits[0];
 
     // We need to insert all of our visits before we can test conditions.
     if (++callbackCount != places.length) {
       referrerSessionId = visit.sessionId;
       return;
@@ -988,32 +1017,31 @@ function test_referrer_saved()
     );
     stmt.params.page_url = uri.spec;
     stmt.params.referrer = visit.referrerURI.spec;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, 1);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_sessionId_saved()
 {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_sessionId_saved"),
     visits: [
       new VisitInfo(),
     ],
   };
   place.visits[0].sessionId = 3;
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     let uri = aPlaceInfo.uri;
     do_check_true(gGlobalHistory.isVisited(uri));
 
     let visit = aPlaceInfo.visits[0];
     do_check_eq(visit.sessionId, place.visits[0].sessionId);
 
     let stmt = DBConn().createStatement(
       "SELECT COUNT(1) AS count " +
@@ -1023,114 +1051,105 @@ function test_sessionId_saved()
     );
     stmt.params.page_url = uri.spec;
     stmt.params.session_id = visit.sessionId;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, 1);
     stmt.finalize();
 
     waitForAsyncUpdates(run_next_test);
-  });
+  }));
 }
 
 function test_guid_change_saved()
 {
   // First, add a visit for it.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_change_saved"),
     visits: [
       new VisitInfo(),
     ],
   };
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
 
     // Then, change the guid with visits.
     place.guid = "_GUIDCHANGE_";
     place.visits = [new VisitInfo()];
-    gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
       do_check_guid_for_uri(place.uri, place.guid);
 
       waitForAsyncUpdates(run_next_test);
-    });
-  });
+    }));
+  }));
 }
 
 function test_title_change_saved()
 {
   // First, add a visit for it.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_title_change_saved"),
     title: "original title",
     visits: [
       new VisitInfo(),
     ],
   };
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
 
     // Now, make sure the empty string clears the title.
     place.title = "";
     place.visits = [new VisitInfo()];
-    gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
       do_check_title_for_uri(place.uri, null);
 
       // Then, change the title with visits.
       place.title = "title change";
       place.visits = [new VisitInfo()];
-      gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-        do_check_true(Components.isSuccessCode(aResultCode));
+      gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
         do_check_title_for_uri(place.uri, place.title);
 
         // Lastly, check that the title is cleared if we set it to null.
         place.title = null;
         place.visits = [new VisitInfo()];
-        gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-          do_check_true(Components.isSuccessCode(aResultCode));
+        gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
           do_check_title_for_uri(place.uri, place.title);
 
           waitForAsyncUpdates(run_next_test);
-        });
-      });
-    });
-  });
+        }));
+      }));
+    }));
+  }));
 }
 
 function test_no_title_does_not_clear_title()
 {
   const TITLE = "test title";
   // First, add a visit for it.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_no_title_does_not_clear_title"),
     title: TITLE,
     visits: [
       new VisitInfo(),
     ],
   };
   do_check_false(gGlobalHistory.isVisited(place.uri));
 
-  gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
-
+  gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
     // Now, make sure that not specifying a title does not clear it.
     delete place.title;
     place.visits = [new VisitInfo()];
-    gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
       do_check_title_for_uri(place.uri, TITLE);
 
       waitForAsyncUpdates(run_next_test);
-    });
-  });
+    }));
+  }));
 }
 
 function test_title_change_notifies()
 {
   // There are three cases to test.  The first case is to make sure we do not
   // get notified if we do not specify a title.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_title_change_notifies"),
@@ -1224,41 +1243,77 @@ function test_referrer_sessionId_persist
     visits: [
       new VisitInfo(),
     ],
   };
 
   // First we add the referrer visit, and then the main visit with referrer
   // attached. We ensure that the sessionId is maintained across the updates.
   do_check_false(gGlobalHistory.isVisited(referrerPlace.uri));
-  gHistory.updatePlaces(referrerPlace, function(aResultCode, aPlaceInfo) {
-    do_check_true(Components.isSuccessCode(aResultCode));
+  gHistory.updatePlaces(referrerPlace, expectHandleResult(function(aPlaceInfo) {
     do_check_true(gGlobalHistory.isVisited(referrerPlace.uri));
 
     let sessionId = aPlaceInfo.visits[0].sessionId;
     do_check_neq(sessionId, null);
 
     let place = {
       uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_sessionId_persists"),
       visits: [
         new VisitInfo(),
       ],
     };
     place.visits[0].referrerURI = referrerPlace.uri;
 
     do_check_false(gGlobalHistory.isVisited(place.uri));
-    gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
-      do_check_true(Components.isSuccessCode(aResultCode));
+    gHistory.updatePlaces(place, expectHandleResult(function(aPlaceInfo) {
       do_check_true(gGlobalHistory.isVisited(place.uri));
 
       do_check_eq(aPlaceInfo.visits[0].sessionId, sessionId);
 
       waitForAsyncUpdates(run_next_test);
-    });
+    }));
+  }));
+}
+
+// test with empty mozIVisitInfoCallback object
+function test_callbacks_not_supplied()
+{
+  const URLS = [
+    "imap://cyrus.andrew.cmu.edu/archive.imap",  // bad URI
+    "http://mozilla.org/" // valid URI
+  ];
+  let places = [];
+  URLS.forEach(function(url) {
+    try {
+      let place = {
+        uri: NetUtil.newURI(url),
+        title: "test for " + url,
+        visits: [
+          new VisitInfo(),
+        ],
+      };
+      places.push(place);
+    }
+    catch (e if e.result === Cr.NS_ERROR_FAILURE) {
+      // NetUtil.newURI() can throw if e.g. our app knows about imap://
+      // but the account is not set up and so the URL is invalid for us.
+      // Note this in the log but ignore as it's not the subject of this test.
+      do_log_info("Could not construct URI for '" + url + "'; ignoring");
+    }
   });
+  
+  gHistory.updatePlaces(places, {} );
+  let observer = {
+    observe: function(aSubject, aTopic, aData)
+    {
+      Services.obs.removeObserver(observer, TOPIC_UPDATEPLACES_COMPLETE);
+      waitForAsyncUpdates(run_next_test);
+    },
+  };
+  Services.obs.addObserver(observer, TOPIC_UPDATEPLACES_COMPLETE, false);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Runner
 
 [
   test_interface_exists,
   test_invalid_uri_throws,
@@ -1285,14 +1340,15 @@ function test_referrer_sessionId_persist
   test_referrer_saved,
   test_sessionId_saved,
   test_guid_change_saved,
   test_title_change_saved,
   test_no_title_does_not_clear_title,
   test_title_change_notifies,
   test_visit_notifies,
   test_referrer_sessionId_persists,
+  test_callbacks_not_supplied,
 ].forEach(add_test);
 
 function run_test()
 {
   run_next_test();
 }
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in
@@ -53,12 +53,11 @@ CPPSRCS		=				\
   $(NULL)
 
 # need static lib
 FORCE_STATIC_LIB = 1
 FORCE_USE_PIC = 1
 
 include $(topsrcdir)/config/rules.mk
 ifeq ($(OS_ARCH),Linux)
-include $(topsrcdir)/config/config.mk
 # need this to suppress errors when compiling common/linux/eintr_wrapper.h
 OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS))
 endif
--- a/widget/src/gtk2/nsGtkIMModule.cpp
+++ b/widget/src/gtk2/nsGtkIMModule.cpp
@@ -337,17 +337,17 @@ void
 nsGtkIMModule::OnFocusWindow(nsWindow* aWindow)
 {
     if (NS_UNLIKELY(IsDestroyed())) {
         return;
     }
 
     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
         ("GtkIMModule(%p): OnFocusWindow, aWindow=%p, mLastFocusedWindow=%p",
-         this, aWindow));
+         this, aWindow, mLastFocusedWindow));
     mLastFocusedWindow = aWindow;
     Focus();
 }
 
 void
 nsGtkIMModule::OnBlurWindow(nsWindow* aWindow)
 {
     if (NS_UNLIKELY(IsDestroyed())) {
@@ -545,17 +545,18 @@ nsresult
 nsGtkIMModule::SetInputMode(nsWindow* aCaller, const IMEContext* aContext)
 {
     if (aContext->mStatus == mIMEContext.mStatus || NS_UNLIKELY(IsDestroyed())) {
         return NS_OK;
     }
 
     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
         ("GtkIMModule(%p): SetInputMode, aCaller=%p, aState=%s mHTMLInputType=%s",
-         this, aCaller, GetEnabledStateName(aContext->mStatus), aContext->mHTMLInputType.get()));
+         this, aCaller, GetEnabledStateName(aContext->mStatus),
+         NS_ConvertUTF16toUTF8(aContext->mHTMLInputType).get()));
 
     if (aCaller != mLastFocusedWindow) {
         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
             ("    FAILED, the caller isn't focused window, mLastFocusedWindow=%p",
              mLastFocusedWindow));
         return NS_OK;
     }
 
@@ -721,18 +722,17 @@ nsGtkIMModule::Focus()
         NS_ASSERTION(sLastFocusedModule == this,
                      "We're not active, but the IM was focused?");
         return;
     }
 
     GtkIMContext *im = GetContext();
     if (!im) {
         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
-            ("    FAILED, there are no context",
-             this));
+            ("    FAILED, there are no context"));
         return;
     }
 
     if (sLastFocusedModule && sLastFocusedModule != this) {
         sLastFocusedModule->Blur();
     }
 
     sLastFocusedModule = this;
@@ -1114,17 +1114,17 @@ nsGtkIMModule::DispatchCompositionStart(
 
     if (mIgnoreNativeCompositionEvent) {
         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
             ("    WARNING, mIgnoreNativeCompositionEvent is already TRUE, but we forcedly reset"));
         mIgnoreNativeCompositionEvent = PR_FALSE;
     }
 
     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
-        ("    mCompositionStart=%lu", mCompositionStart));
+        ("    mCompositionStart=%u", mCompositionStart));
     mIsComposing = PR_TRUE;
     nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_START,
                                  mLastFocusedWindow);
     InitEvent(compEvent);
     nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
     mLastFocusedWindow->DispatchEvent(&compEvent, status);
     if (static_cast<nsWindow*>(kungFuDeathGrip.get())->IsDestroyed() ||
         kungFuDeathGrip != mLastFocusedWindow) {
@@ -1324,17 +1324,17 @@ nsGtkIMModule::SetTextRangeList(nsTArray
             range.mEndOffset = range.mStartOffset + uniStrLen;
             g_free(uniStr);
             uniStr = nsnull;
         }
 
         aTextRangeList.AppendElement(range);
 
         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
-            ("    mStartOffset=%lu, mEndOffset=%lu, mRangeType=%s",
+            ("    mStartOffset=%u, mEndOffset=%u, mRangeType=%s",
              range.mStartOffset, range.mEndOffset,
              GetRangeTypeName(range.mRangeType)));
     } while (pango_attr_iterator_next(iter));
 
     nsTextRange range;
     if (cursor_pos < 0) {
         range.mStartOffset = 0;
     } else if (PRUint32(cursor_pos) > mCompositionString.Length()) {
@@ -1342,30 +1342,30 @@ nsGtkIMModule::SetTextRangeList(nsTArray
     } else {
         range.mStartOffset = PRUint32(cursor_pos);
     }
     range.mEndOffset = range.mStartOffset;
     range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
     aTextRangeList.AppendElement(range);
 
     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
-        ("    mStartOffset=%lu, mEndOffset=%lu, mRangeType=%s",
+        ("    mStartOffset=%u, mEndOffset=%u, mRangeType=%s",
          range.mStartOffset, range.mEndOffset,
          GetRangeTypeName(range.mRangeType)));
 
     pango_attr_iterator_destroy(iter);
     pango_attr_list_unref(feedback_list);
     g_free(preedit_string);
 }
 
 void
 nsGtkIMModule::SetCursorPosition(PRUint32 aTargetOffset)
 {
     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
-        ("GtkIMModule(%p): SetCursorPosition, aTargetOffset=%lu",
+        ("GtkIMModule(%p): SetCursorPosition, aTargetOffset=%u",
          this, aTargetOffset));
 
     if (aTargetOffset == PR_UINT32_MAX) {
         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
             ("    FAILED, aTargetOffset is wrong offset"));
         return;
     }
 
--- a/xpcom/build/Makefile.in
+++ b/xpcom/build/Makefile.in
@@ -50,20 +50,16 @@ MODULE		= xpcom
 LIBRARY_NAME	= xpcom_core
 SHORT_LIBNAME	= xpcomcor
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 
 GRE_MODULE	= 1
 MOZILLA_INTERNAL_API = 1
 
-CSRCS		= \
-		$(XPCOM_GLUE_SRC_LCSRCS) \
-		$(NULL)
-
 CPPSRCS		= \
 		$(XPCOM_GLUE_SRC_LCPPSRCS) \
 		$(XPCOM_GLUENS_SRC_LCPPSRCS) \
 		nsXPComInit.cpp \
 		nsXPCOMStrings.cpp \
 		Services.cpp \
 		Omnijar.cpp \
 		$(NULL)
@@ -123,17 +119,17 @@ EXPORTS_mozilla = \
   Services.h \
   ServiceList.h \
   Omnijar.h \
   $(NULL)
 
 # Force use of PIC
 FORCE_USE_PIC	= 1 
 
-GARBAGE         += $(XPCOM_GLUE_SRC_LCSRCS) $(XPCOM_GLUE_SRC_LCPPSRCS) $(XPCOM_GLUENS_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
+GARBAGE         += $(XPCOM_GLUE_SRC_LCPPSRCS) $(XPCOM_GLUENS_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 DEFINES		+= \
 		-D_IMPL_NS_COM \
 		-D_IMPL_NS_STRINGAPI \
@@ -143,10 +139,10 @@ DEFINES		+= \
 ifdef TARGET_XPCOM_ABI
 DEFINES += -DTARGET_XPCOM_ABI=\"$(TARGET_XPCOM_ABI)\"
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CXXFLAGS	+= $(TK_CFLAGS)
 endif
 
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS)
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS)
 	$(INSTALL) $^ .
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -50,20 +50,16 @@ DIRS            = standalone nomozalloc
 MODULE		= xpcom
 LIBRARY_NAME	= xpcomglue_s
 DIST_INSTALL	= 1
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../build \
 		$(NULL)
 
-CSRCS		= \
-		$(XPCOM_GLUE_SRC_LCSRCS) \
-		$(NULL)  
-
 CPPSRCS		= \
 		$(XPCOM_GLUE_SRC_LCPPSRCS) \
 		$(XPCOM_GLUENS_SRC_LCPPSRCS) \
 		nsStringAPI.cpp \
 		GenericModule.cpp \
 		$(NULL)
 
 SDK_HEADERS = \
--- a/xpcom/glue/nomozalloc/Makefile.in
+++ b/xpcom/glue/nomozalloc/Makefile.in
@@ -49,32 +49,28 @@ MODULE		= xpcom
 LIBRARY_NAME	= xpcomglue_s_nomozalloc
 DIST_INSTALL	= 1
 
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../build \
 		$(NULL)
 
-CSRCS		= \
-		$(XPCOM_GLUE_SRC_LCSRCS) \
-		$(NULL)  
-
 CPPSRCS		= \
 		$(XPCOM_GLUE_SRC_LCPPSRCS) \
 		$(XPCOM_GLUENS_SRC_LCPPSRCS) \
 		nsStringAPI.cpp \
 		GenericModule.cpp \
 		$(NULL)
 
 SDK_LIBRARY     =                        \
 		$(LIB_PREFIX)xpcomglue_s_nomozalloc.$(LIB_SUFFIX) \
 		$(NULL)
 
-GARBAGE += $(CSRCS) $(CPPSRCS) DeadlockDetector.h SSE.h arm.h
+GARBAGE += $(CPPSRCS) DeadlockDetector.h SSE.h arm.h
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 # Force use of PIC
 FORCE_USE_PIC	= 1
 
 # Pretend we're statically linking the CRT, even though we might not be: this
@@ -89,16 +85,16 @@ include $(topsrcdir)/config/rules.mk
 
 ifdef _MSC_VER
 # Don't include directives about which CRT to use
 OS_COMPILE_CXXFLAGS += -Zl
 OS_COMPILE_CFLAGS += -Zl
 DEFINES += -D_USE_ANSI_CPP
 endif
 
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp $(topsrcdir)/xpcom/glue/GenericModule.cpp $(topsrcdir)/xpcom/glue/DeadlockDetector.h $(topsrcdir)/xpcom/glue/SSE.h $(topsrcdir)/xpcom/glue/arm.h
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(XPCOM_GLUENS_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp $(topsrcdir)/xpcom/glue/GenericModule.cpp $(topsrcdir)/xpcom/glue/DeadlockDetector.h $(topsrcdir)/xpcom/glue/SSE.h $(topsrcdir)/xpcom/glue/arm.h
 	$(INSTALL) $^ .
 
 ifdef TARGET_XPCOM_ABI
 DEFINES += -DTARGET_XPCOM_ABI=\"$(TARGET_XPCOM_ABI)\"
 endif
 
 DEFINES += -DMOZ_NO_MOZALLOC
--- a/xpcom/glue/objs.mk
+++ b/xpcom/glue/objs.mk
@@ -29,22 +29,16 @@
 # 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 *****
 
-XPCOM_GLUE_SRC_LCSRCS =          \
-  pldhash.c                      \
-  $(NULL)
-
-XPCOM_GLUE_SRC_CSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCSRCS))
-
 XPCOM_GLUE_SRC_LCPPSRCS =        \
   nsArrayEnumerator.cpp          \
   nsArrayUtils.cpp               \
   nsCategoryCache.cpp            \
   nsCOMPtr.cpp                   \
   nsCOMArray.cpp                 \
   nsCRTGlue.cpp                  \
   nsClassInfoImpl.cpp            \
@@ -61,16 +55,17 @@ XPCOM_GLUE_SRC_LCPPSRCS =        \
   nsQuickSort.cpp                \
   nsVoidArray.cpp                \
   nsTArray.cpp                   \
   nsThreadUtils.cpp              \
   nsTObserverArray.cpp           \
   nsCycleCollectionParticipant.cpp \
   nsCycleCollectorUtils.cpp      \
   nsDeque.cpp \
+  pldhash.cpp \
   $(NULL)
 
 XPCOM_GLUE_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCPPSRCS))
 
 XPCOM_GLUENS_SRC_LCPPSRCS =      \
   BlockingResourceBase.cpp       \
   DeadlockDetector.cpp           \
   SSE.cpp                        \
rename from xpcom/glue/pldhash.c
rename to xpcom/glue/pldhash.cpp
--- a/xpcom/glue/standalone/Makefile.in
+++ b/xpcom/glue/standalone/Makefile.in
@@ -66,20 +66,16 @@ ifeq (OS2,$(OS_ARCH))
 LINKSRC = nsGlueLinkingOS2.cpp
 endif
 
 ifndef LINKSRC
 LINKSRC = nsGlueLinkingNull.cpp
 $(warning TinderboxPrint:<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=298044">Error: XPCOM Glue</a>)
 endif
 
-CSRCS		= \
-		$(XPCOM_GLUE_SRC_LCSRCS) \
-		$(NULL)
-
 CPPSRCS		= \
 		$(XPCOM_GLUE_SRC_LCPPSRCS)   \
 		nsStringAPI.cpp              \
 		nsXPCOMGlue.cpp              \
 		$(LINKSRC)                   \
 		$(NULL)
 
 SDK_HEADERS     = \
@@ -99,30 +95,30 @@ FORCE_USE_PIC	= 1
 # Pretend we're statically linking the CRT, even though we might not be: this
 # avoids "msvcrp" and assembly dependencies from creeping into the directives
 # for this library on Windows.
 USE_STATIC_LIBS = 1
 
 # Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
 STL_FLAGS	=
 
-GARBAGE         += $(XPCOM_GLUE_SRC_LCSRCS) $(XPCOM_GLUE_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
+GARBAGE         += $(XPCOM_GLUE_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX))
 
 SRCS_IN_OBJDIR	= 1
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef _MSC_VER
 # Don't include directives about which CRT to use
 OS_COMPILE_CXXFLAGS += -Zl
 OS_COMPILE_CFLAGS += -Zl
 DEFINES += -D_USE_ANSI_CPP
 endif
 
-export:: $(XPCOM_GLUE_SRC_CSRCS) $(XPCOM_GLUE_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp
+export:: $(XPCOM_GLUE_SRC_CPPSRCS) $(topsrcdir)/xpcom/glue/nsStringAPI.cpp
 	$(INSTALL) $^ .
 
 GARBAGE += nsStringAPI.cpp
 
 DEFINES		+= -DXPCOM_GLUE
 
 ifdef TARGET_XPCOM_ABI
 DEFINES += -DTARGET_XPCOM_ABI=\"$(TARGET_XPCOM_ABI)\"