[JAEGER] Merge from tracemonkey.
authorDavid Anderson <danderson@mozilla.com>
Mon, 19 Jul 2010 20:49:26 -0700
changeset 53125 1d68b3042bf58e0b4cd561ba61d2d399a3a269a9
parent 53124 da41e5a6de821ac221edcf7101846662cf813471 (current diff)
parent 48506 684ff9ad214e9e0bb33794c276598083c59f2bff (diff)
child 53126 d49db5482db6447a775c48ca9c1b8f15e1fd763d
push idunknown
push userunknown
push dateunknown
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[JAEGER] Merge from tracemonkey.
caps/include/nsScriptSecurityManager.h
caps/src/nsScriptSecurityManager.cpp
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsFrameMessageManager.cpp
content/canvas/src/CustomQS_Canvas2D.h
content/canvas/src/CustomQS_WebGL.h
content/canvas/src/NativeJSContext.cpp
content/canvas/src/NativeJSContext.h
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventListenerManager.h
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
content/html/document/test/test_bug570375.html
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBObjectStore.cpp
extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
gfx/layers/opengl/glDefs.h
intl/chardet/public/nsIMetaCharsetService.h
intl/chardet/public/nsIXMLEncodingService.h
intl/chardet/src/nsDetectionAdaptor.cpp
intl/chardet/src/nsDetectionAdaptor.h
intl/chardet/src/nsMetaCharsetObserver.cpp
intl/chardet/src/nsMetaCharsetObserver.h
intl/chardet/src/nsXMLEncodingObserver.cpp
intl/chardet/src/nsXMLEncodingObserver.h
intl/uconv/public/nsICharRepresentable.h
intl/unicharutil/public/nsITextTransform.h
intl/unicharutil/util/gensymmtable.pl
intl/unicharutil/util/nsCompressedCharMap.cpp
intl/unicharutil/util/symmtable.h
js/ipc/ObjectWrapperParent.cpp
js/jetpack/JetpackActorCommon.cpp
js/jetpack/JetpackChild.cpp
js/jetpack/JetpackChild.h
js/jsd/jsd.h
js/jsd/jsd_val.c
js/jsd/jsd_xpc.cpp
js/src/Makefile.in
js/src/config/rules.mk
js/src/configure.in
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
js/src/jsapi-tests/testExtendedEq.cpp
js/src/jsapi-tests/testGCChunkAlloc.cpp
js/src/jsapi-tests/testIsAboutToBeFinalized.cpp
js/src/jsapi-tests/testLookup.cpp
js/src/jsapi-tests/testNewObject.cpp
js/src/jsapi-tests/testPropCache.cpp
js/src/jsapi-tests/testSameValue.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdate.cpp
js/src/jsdate.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsdtoa.cpp
js/src/jsdtracef.cpp
js/src/jsdtracef.h
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcchunk.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jslock.cpp
js/src/jslock.h
js/src/jsmath.cpp
js/src/jsmath.h
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/json.h
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jspropertycache.cpp
js/src/jspropertytree.cpp
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsrecursion.cpp
js/src/jsregexp.cpp
js/src/jsregexp.h
js/src/jsscan.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jstl.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jstypes.h
js/src/jsutil.cpp
js/src/jsval.h
js/src/jsvalue.h
js/src/jsvector.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/jsxdrapi.cpp
js/src/jsxdrapi.h
js/src/jsxml.cpp
js/src/jsxml.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/nanojit/VMPI.h
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/tests/js1_5/GC/regress-348532.js
js/src/xpconnect/idl/nsIXPCScriptable.idl
js/src/xpconnect/idl/nsIXPConnect.idl
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/public/xpc_map_end.h
js/src/xpconnect/shell/xpcshell.cpp
js/src/xpconnect/src/XPCChromeObjectWrapper.cpp
js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
js/src/xpconnect/src/XPCDispInterface.cpp
js/src/xpconnect/src/XPCDispObject.cpp
js/src/xpconnect/src/XPCDispTearOff.cpp
js/src/xpconnect/src/XPCNativeWrapper.cpp
js/src/xpconnect/src/XPCNativeWrapper.h
js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp
js/src/xpconnect/src/XPCWrapper.cpp
js/src/xpconnect/src/XPCWrapper.h
js/src/xpconnect/src/dom_quickstubs.qsconf
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/qsgen.py
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcinlines.h
js/src/xpconnect/src/xpcjsid.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcquickstubs.h
js/src/xpconnect/src/xpcruntimesvc.cpp
js/src/xpconnect/src/xpcthreadcontext.cpp
js/src/xpconnect/src/xpcvariant.cpp
js/src/xpconnect/src/xpcwrappedjs.cpp
js/src/xpconnect/src/xpcwrappedjsclass.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativeinfo.cpp
js/src/xpconnect/src/xpcwrappednativejsops.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
js/src/xpconnect/tests/TestXPC.cpp
js/src/xpconnect/wrappers/AccessCheck.cpp
js/src/xpconnect/wrappers/XrayWrapper.cpp
modules/libpref/src/init/all.js
modules/plugin/base/src/nsJSNPRuntime.cpp
modules/plugin/base/src/nsNPAPIPlugin.cpp
modules/plugin/base/src/nsNPAPIPlugin.h
storage/src/mozStoragePrivateHelpers.cpp
storage/src/mozStorageStatementParams.cpp
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib
toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib
toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib
toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib
toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation.vcproj
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.vcproj
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj
toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.vcproj
toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.h
toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.h
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.h
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.cc
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.h
toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/language.cc
toolkit/crashreporter/google-breakpad/src/common/linux/language.h
toolkit/crashreporter/google-breakpad/src/common/linux/module.cc
toolkit/crashreporter/google-breakpad/src/common/linux/module.h
toolkit/crashreporter/google-breakpad/src/common/linux/module_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc
toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h
toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input1
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input2
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input3
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input4
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input5
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6
toolkit/crashreporter/google-breakpad/src/processor/test_assembler.cc
toolkit/crashreporter/google-breakpad/src/processor/test_assembler.h
toolkit/crashreporter/google-breakpad/src/processor/test_assembler_unittest.cc
xpcom/base/nsrootidl.idl
--- a/accessible/public/nsIAccessibleTable.idl
+++ b/accessible/public/nsIAccessibleTable.idl
@@ -40,17 +40,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIAccessible;
 interface nsIArray;
 
-[scriptable, uuid(035c0c0e-41e3-4985-8ad9-d9f14cdc667a)]
+[scriptable, uuid(cb0bf7b9-117e-40e2-9e46-189c3d43ce4a)]
 interface nsIAccessibleTable : nsISupports
 {
   /**
    * Return the caption accessible for the table. For example, html:caption
    * element of html:table element.
    */
   readonly attribute nsIAccessible caption;
 
@@ -100,16 +100,27 @@ interface nsIAccessibleTable : nsISuppor
   /**
    * Translate the given cell index into the corresponding row index.
    *
    * @param cellIndex  [in] index of the table cell to return row index for
    */
   long getRowIndexAt(in long cellIndex);
 
   /**
+   * Translate the given cell index into the corresponding row and column
+   * indices.
+   *
+   * @param cellIndex    [in] cell index to return row and column indices for
+   * @param rowIndex     [out] row index at the given cell index
+   * @param columnIndex  [out] column index at the given cell index
+   */
+  void getRowAndColumnIndicesAt(in long cellIndex,
+                                out long rowIndex, out long columnIndex);
+
+  /**
    * Return the number of columns occupied by the accessible cell at
    * the specified row and column in the table. The result differs from 1 if
    * the specified cell spans multiple columns.
    *
    * @param  row     [in] row index of the cell to return the column extent for
    * @param  column  [in] column index of the cell to return the column extent
    *                  for
    */
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/AccGroupInfo.cpp
@@ -0,0 +1,169 @@
+/* ***** 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) 2010
+ * 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 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 "AccGroupInfo.h"
+
+AccGroupInfo::AccGroupInfo(nsAccessible* aItem, PRUint32 aRole) :
+  mPosInSet(0), mSetSize(0), mParent(nsnull)
+{
+  nsAccessible* parent = aItem->GetParent();
+  if (!parent)
+    return;
+
+  PRInt32 indexInParent = aItem->GetIndexInParent();
+  PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
+
+  // Compute position in set.
+  mPosInSet = 1;
+  for (PRInt32 idx = indexInParent - 1; idx >=0 ; idx--) {
+    nsAccessible* sibling = parent->GetChildAt(idx);
+    PRUint32 siblingRole = nsAccUtils::Role(sibling);
+
+    // If the sibling is separator then the group is ended.
+    if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
+      break;
+
+    // If sibling is not visible and hasn't the same base role.
+    if (BaseRole(siblingRole) != aRole ||
+        nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
+      continue;
+
+    // Check if it's hierarchical flatten structure, i.e. if the sibling
+    // level is lesser than this one then group is ended, if the sibling level
+    // is greater than this one then the group is split by some child elements
+    // (group will be continued).
+    PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
+    if (siblingLevel < level) {
+      mParent = sibling;
+      break;
+    }
+
+    // Skip subset.
+    if (siblingLevel > level)
+      continue;
+
+    // If the previous item in the group has calculated group information then
+    // build group information for this item based on found one.
+    if (sibling->mGroupInfo) {
+      mPosInSet += sibling->mGroupInfo->mPosInSet;
+      mParent = sibling->mGroupInfo->mParent;
+      mSetSize = sibling->mGroupInfo->mSetSize;
+      return;
+    }
+
+    mPosInSet++;
+  }
+
+  // Compute set size.
+  mSetSize = mPosInSet;
+
+  PRInt32 siblingCount = parent->GetChildCount();
+  for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
+    nsAccessible* sibling = parent->GetChildAt(idx);
+
+    PRUint32 siblingRole = nsAccUtils::Role(sibling);
+
+    // If the sibling is separator then the group is ended.
+    if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
+      break;
+
+    // If sibling is visible and has the same base role
+    if (BaseRole(siblingRole) != aRole ||
+        nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
+      continue;
+
+    // and check if it's hierarchical flatten structure.
+    PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
+    if (siblingLevel < level)
+      break;
+
+    // Skip subset.
+    if (siblingLevel > level)
+      continue;
+
+    // If the next item in the group has calculated group information then
+    // build group information for this item based on found one.
+    if (sibling->mGroupInfo) {
+      mParent = sibling->mGroupInfo->mParent;
+      mSetSize = sibling->mGroupInfo->mSetSize;
+      return;
+    }
+
+    mSetSize++;
+  }
+
+  if (mParent)
+    return;
+
+  // Compute parent.
+  PRUint32 parentRole = nsAccUtils::Role(parent);
+
+  // In the case of ARIA row in treegrid, return treegrid since ARIA
+  // groups aren't used to organize levels in ARIA treegrids.
+  if (aRole == nsIAccessibleRole::ROLE_ROW &&
+      parentRole == nsIAccessibleRole::ROLE_TREE_TABLE) {
+    mParent = parent;
+    return;
+  }
+
+  // In the case of ARIA tree, a tree can be arranged by using ARIA groups
+  // to organize levels. In this case the parent of the tree item will be
+  // a group and the previous treeitem of that should be the tree item
+  // parent. Or, if the parent is something other than a tree we will
+  // return that.
+
+  if (parentRole != nsIAccessibleRole::ROLE_GROUPING) {
+    mParent = parent;
+    return;
+  }
+
+  nsAccessible* parentPrevSibling = parent->GetSiblingAtOffset(-1);
+  PRUint32 parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
+  if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_TEXT_LEAF) {
+    // XXX Sometimes an empty text accessible is in the hierarchy here,
+    // although the text does not appear to be rendered, GetRenderedText()
+    // says that it is so we need to skip past it to find the true
+    // previous sibling.
+    parentPrevSibling = parentPrevSibling->GetSiblingAtOffset(-1);
+    parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
+  }
+
+  // Previous sibling of parent group is a tree item, this is the
+  // conceptual tree item parent.
+  if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_OUTLINEITEM)
+    mParent = parentPrevSibling;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/AccGroupInfo.h
@@ -0,0 +1,96 @@
+/* ***** 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) 2010
+ * 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 the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef AccGroupInfo_h_
+#define AccGroupInfo_h_
+
+#include "nsAccessible.h"
+#include "nsAccUtils.h"
+
+/**
+ * Calculate and store group information.
+ */
+class AccGroupInfo
+{
+public:
+  AccGroupInfo(nsAccessible* aItem, PRUint32 aRole);
+  ~AccGroupInfo() { }
+
+  PRInt32 PosInSet() const { return mPosInSet; }
+  PRUint32 SetSize() const { return mSetSize; }
+  nsAccessible* GetConceptualParent() const { return mParent; }
+
+  /**
+   * Create group info.
+   */
+  static AccGroupInfo* CreateGroupInfo(nsAccessible* aAccessible)
+  {
+    PRUint32 role = nsAccUtils::Role(aAccessible);
+    if (role != nsIAccessibleRole::ROLE_ROW &&
+        role != nsIAccessibleRole::ROLE_GRID_CELL &&
+        role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
+        role != nsIAccessibleRole::ROLE_OPTION &&
+        role != nsIAccessibleRole::ROLE_LISTITEM &&
+        role != nsIAccessibleRole::ROLE_MENUITEM &&
+        role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
+        role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
+        role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
+        role != nsIAccessibleRole::ROLE_PAGETAB)
+      return nsnull;
+
+    AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
+    return info;
+  }
+
+private:
+  AccGroupInfo(const AccGroupInfo&);
+  AccGroupInfo& operator =(const AccGroupInfo&);
+
+  static PRUint32 BaseRole(PRUint32 aRole)
+  {
+    if (aRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
+        aRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
+      return nsIAccessibleRole::ROLE_MENUITEM;
+    return aRole;
+  }
+
+  PRUint32 mPosInSet;
+  PRUint32 mSetSize;
+  nsAccessible* mParent;
+};
+
+#endif
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -44,16 +44,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = accessibility
 LIBRARY_NAME = accessibility_base_s
 LIBXUL_LIBRARY = 1
 
 
 CPPSRCS = \
   AccCollector.cpp \
+  AccGroupInfo.cpp \
   AccIterator.cpp \
   filters.cpp \
   nsAccDocManager.cpp \
   nsAccessNode.cpp \
   nsAccEvent.cpp \
   nsARIAGridAccessible.cpp \
   nsARIAMap.cpp \
   nsDocAccessible.cpp \
--- a/accessible/src/base/nsARIAGridAccessible.cpp
+++ b/accessible/src/base/nsARIAGridAccessible.cpp
@@ -217,16 +217,44 @@ nsARIAGridAccessible::GetRowIndexAt(PRIn
 
   NS_ENSURE_ARG(aCellIndex < rowCount * colsCount);
 
   *aRowIndex = aCellIndex / colsCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsARIAGridAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
+                                               PRInt32* aRowIndex,
+                                               PRInt32* aColumnIndex)
+{
+  NS_ENSURE_ARG_POINTER(aRowIndex);
+  *aRowIndex = -1;
+  NS_ENSURE_ARG_POINTER(aColumnIndex);
+  *aColumnIndex = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  NS_ENSURE_ARG(aCellIndex >= 0);
+
+  PRInt32 rowCount = 0;
+  GetRowCount(&rowCount);
+
+  PRInt32 colsCount = 0;
+  GetColumnCount(&colsCount);
+
+  NS_ENSURE_ARG(aCellIndex < rowCount * colsCount);
+
+  *aColumnIndex = aCellIndex % colsCount;
+  *aRowIndex = aCellIndex / colsCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsARIAGridAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn,
                                         PRInt32 *aExtentCount)
 {
   NS_ENSURE_ARG_POINTER(aExtentCount);
   *aExtentCount = 0;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
--- a/accessible/src/base/nsAccCache.h
+++ b/accessible/src/base/nsAccCache.h
@@ -34,21 +34,20 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsAccCache_H_
 #define _nsAccCache_H_
 
+#include "nsIAccessible.h"
 #include "nsRefPtrHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 
-class nsIAccessible;
-
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible cache utils
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
  * Shutdown and removes the accessible from cache.
  */
 template <class T>
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -362,116 +362,16 @@ nsAccUtils::GetAncestorWithRole(nsAccess
       return parent;
 
     if (parent == document)
       break;
   }
   return nsnull;
 }
 
-void
-nsAccUtils::GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
-                                  nsIContent *aStartContent,
-                                  nsIAccessible **aTreeItemParentResult)
-{
-  *aTreeItemParentResult = nsnull;
-
-  nsCOMPtr<nsIAccessible> parentAccessible;
-  aStartTreeItem->GetParent(getter_AddRefs(parentAccessible));
-  if (!parentAccessible)
-    return;
-
-  PRUint32 startTreeItemRole = nsAccUtils::Role(aStartTreeItem);
-
-  // Calculate tree grid row parent only if the row inside of ARIA treegrid.
-  if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
-    PRUint32 role = nsAccUtils::Role(parentAccessible);
-    if (role != nsIAccessibleRole::ROLE_TREE_TABLE)
-      return;
-  }
-
-  // This is a tree or treegrid that uses aria-level to define levels, so find
-  // the first previous sibling accessible where level is defined to be less
-  // than the current level.
-  nsAutoString levelStr;
-  if (nsAccUtils::HasDefinedARIAToken(aStartContent, nsAccessibilityAtoms::aria_level) &&
-      aStartContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, levelStr)) {
-
-    PRInt32 success;
-    PRInt32 level = levelStr.ToInteger(&success);
-    if (level > 1 && NS_SUCCEEDED(success)) {
-      nsCOMPtr<nsIAccessible> currentAccessible = aStartTreeItem, prevAccessible;
-      while (PR_TRUE) {
-        currentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
-        currentAccessible.swap(prevAccessible);
-        nsCOMPtr<nsIAccessNode> accessNode = do_QueryInterface(currentAccessible);
-        if (!accessNode) {
-          break; // Reached top of tree, no higher level found
-        }
-        PRUint32 role = nsAccUtils::Role(currentAccessible);
-        if (role != startTreeItemRole)
-          continue;
-
-        nsCOMPtr<nsIDOMNode> treeItemNode;
-        accessNode->GetDOMNode(getter_AddRefs(treeItemNode));
-        nsCOMPtr<nsIContent> treeItemContent = do_QueryInterface(treeItemNode);
-        if (treeItemContent &&
-            nsAccUtils::HasDefinedARIAToken(treeItemContent,
-                                     nsAccessibilityAtoms::aria_level) &&
-            treeItemContent->GetAttr(kNameSpaceID_None,
-                                     nsAccessibilityAtoms::aria_level, levelStr)) {
-          if (levelStr.ToInteger(&success) < level && NS_SUCCEEDED(success)) {
-            NS_ADDREF(*aTreeItemParentResult = currentAccessible);
-            return;
-          }
-        }
-      }
-    }
-  }
-
-  // In the case of ARIA treegrid, return its parent since ARIA group isn't
-  // used to organize levels in ARIA treegrids.
-
-  if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
-    NS_ADDREF(*aTreeItemParentResult = parentAccessible);
-    return; // The container for the tree grid rows
-  }
-
-  // In the case of ARIA tree, a tree can be arranged by using role="group" to
-  // organize levels. In this case the parent of the tree item will be a group
-  // and the previous sibling of that should be the tree item parent. Or, if
-  // the parent is something other than a tree we will return that.
-
-  PRUint32 role = nsAccUtils::Role(parentAccessible);
-  if (role != nsIAccessibleRole::ROLE_GROUPING) {
-    NS_ADDREF(*aTreeItemParentResult = parentAccessible);
-    return; // The container for the tree items
-  }
-
-  nsCOMPtr<nsIAccessible> prevAccessible;
-  parentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
-  if (!prevAccessible)
-    return;
-  role = nsAccUtils::Role(prevAccessible);
-  if (role == nsIAccessibleRole::ROLE_TEXT_LEAF) {
-    // XXX Sometimes an empty text accessible is in the hierarchy here,
-    // although the text does not appear to be rendered, GetRenderedText() says that it is
-    // so we need to skip past it to find the true previous sibling
-    nsCOMPtr<nsIAccessible> tempAccessible = prevAccessible;
-    tempAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
-    if (!prevAccessible)
-      return;
-    role = nsAccUtils::Role(prevAccessible);
-  }
-  if (role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
-    // Previous sibling of parent group is a tree item -- this is the conceptual tree item parent
-    NS_ADDREF(*aTreeItemParentResult = prevAccessible);
-  }
-}
-
 nsAccessible *
 nsAccUtils::GetSelectableContainer(nsAccessible *aAccessible, PRUint32 aState)
 {
   if (!aAccessible)
     return nsnull;
 
   if (!(aState & nsIAccessibleStates::STATE_SELECTABLE))
     return nsnull;
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -193,29 +193,16 @@ public:
     * @param  aDescendant  [in] descendant to start search with
     * @param  aRole        [in] role to find matching ancestor for
     * @return               the ancestor accessible with the given role, or
     *                       nsnull if no match is found
     */
    static nsAccessible * GetAncestorWithRole(nsAccessible *aDescendant,
                                              PRUint32 aRole);
 
-   /**
-     * For an ARIA tree item , get the accessible that represents its conceptual parent.
-     * This method will use the correct method for the given way the tree is constructed.
-     * The conceptual parent is what the user sees as the parent, not the DOM or accessible parent.
-     * @param aStartTreeItem  The tree item to get the parent for
-     * @param aStartTreeItemContent  The content node for the tree item
-     * @param The tree item's parent, or null if none
-     */
-   static void
-     GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
-                           nsIContent *aStartTreeItemContent,
-                           nsIAccessible **aTreeItemParent);
-
   /**
    * Return single or multi selectable container for the given item.
    *
    * @param  aAccessible  [in] the item accessible
    * @param  aState       [in] the state of the item accessible
    */
   static nsAccessible *GetSelectableContainer(nsAccessible *aAccessible,
                                               PRUint32 aState);
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -126,17 +126,17 @@ nsAccessNode::~nsAccessNode()
 void nsAccessNode::LastRelease()
 {
   // First cleanup if needed...
   if (mWeakShell) {
     Shutdown();
     NS_ASSERTION(!mWeakShell, "A Shutdown() impl forgot to call its parent's Shutdown?");
   }
   // ... then die.
-  NS_DELETEXPCOM(this);
+  delete this;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public
 
 PRBool
 nsAccessNode::Init()
 {
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -36,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAccessible.h"
 
 #include "nsIXBLAccessible.h"
 
+#include "AccGroupInfo.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "nsARIAMap.h"
 #include "nsDocAccessible.h"
 #include "nsEventShell.h"
 
 #include "nsAccessibilityService.h"
 #include "nsAccTreeWalker.h"
@@ -2120,21 +2121,22 @@ nsAccessible::GetRelationByType(PRUint32
         return NS_OK; // XXX bug 381599, avoid performance problems
 
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry &&
           (mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
            mRoleMapEntry->role == nsIAccessibleRole::ROLE_ROW)) {
 
-        nsCOMPtr<nsIAccessible> accTarget;
-        nsAccUtils::GetARIATreeItemParent(this, mContent,
-                                          getter_AddRefs(accTarget));
-
-        return nsRelUtils::AddTarget(aRelationType, aRelation, accTarget);
+        AccGroupInfo* groupInfo = GetGroupInfo();
+        if (!groupInfo)
+          return NS_OK_NO_RELATION_TARGET;
+
+        return nsRelUtils::AddTarget(aRelationType, aRelation,
+                                     groupInfo->GetConceptualParent());
       }
 
       // If accessible is in its own Window, or is the root of a document,
       // then we should provide NODE_CHILD_OF relation so that MSAA clients
       // can easily get to true parent instead of getting to oleacc's
       // ROLE_WINDOW accessible which will prevent us from going up further
       // (because it is system generated and has no idea about the hierarchy
       // above it).
@@ -3086,108 +3088,34 @@ nsAccessible::GetActionRule(PRUint32 aSt
   // Get an action based on ARIA attribute.
   if (nsAccUtils::HasDefinedARIAToken(mContent,
                                       nsAccessibilityAtoms::aria_expanded))
     return eExpandAction;
 
   return eNoAction;
 }
 
+AccGroupInfo*
+nsAccessible::GetGroupInfo()
+{
+  if (mGroupInfo)
+    return mGroupInfo;
+
+  mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
+  return mGroupInfo;
+}
+
 void
 nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
 {
-  PRUint32 role = nsAccUtils::Role(this);
-  if (role != nsIAccessibleRole::ROLE_LISTITEM &&
-      role != nsIAccessibleRole::ROLE_MENUITEM &&
-      role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
-      role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
-      role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
-      role != nsIAccessibleRole::ROLE_PAGETAB &&
-      role != nsIAccessibleRole::ROLE_OPTION &&
-      role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
-      role != nsIAccessibleRole::ROLE_ROW &&
-      role != nsIAccessibleRole::ROLE_GRID_CELL)
-    return;
-
-  PRUint32 baseRole = role;
-  if (role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
-      role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
-    baseRole = nsIAccessibleRole::ROLE_MENUITEM;
-
-  nsAccessible* parent = GetParent();
-  NS_ENSURE_TRUE(parent,);
-
-  PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(this);
-
-  // Compute 'posinset'.
-  PRInt32 positionInGroup = 1;
-  for (PRInt32 idx = mIndexInParent - 1; idx >= 0; idx--) {
-    nsAccessible* sibling = parent->GetChildAt(idx);
-
-    PRUint32 siblingRole = nsAccUtils::Role(sibling);
-
-    // If the sibling is separator then the group is ended.
-    if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
-      break;
-
-    PRUint32 siblingBaseRole = siblingRole;
-    if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
-        siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
-      siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
-
-    // If sibling is visible and has the same base role
-    if (siblingBaseRole == baseRole &&
-        !(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
-
-      // and check if it's hierarchical flatten structure, i.e. if the sibling
-      // level is lesser than this one then group is ended, if the sibling level
-      // is greater than this one then the group is splited by some child
-      // elements (group will be continued).
-      PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
-      if (siblingLevel < level)
-        break;
-      else if (level == siblingLevel)
-        ++ positionInGroup;
-    }
+  AccGroupInfo* groupInfo = GetGroupInfo();
+  if (groupInfo) {
+    *aPosInSet = groupInfo->PosInSet();
+    *aSetSize = groupInfo->SetSize();
   }
-
-  // Compute 'setsize'.
-  PRInt32 setSize = positionInGroup;
-
-  PRInt32 siblingCount = parent->GetChildCount();
-  for (PRInt32 idx = mIndexInParent + 1; idx < siblingCount; idx++) {
-    nsAccessible* sibling = parent->GetChildAt(idx);
-    NS_ENSURE_TRUE(sibling,);
-
-    PRUint32 siblingRole = nsAccUtils::Role(sibling);
-
-    // If the sibling is separator then the group is ended.
-    if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
-      break;
-
-    PRUint32 siblingBaseRole = siblingRole;
-    if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
-        siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
-      siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
-
-    // If sibling is visible and has the same base role
-    if (siblingBaseRole == baseRole &&
-        !(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
-
-      // and check if it's hierarchical flatten structure.
-      PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
-      if (siblingLevel < level)
-        break;
-      else if (level == siblingLevel)
-        ++ setSize;
-    }
-  }
-
-  *aPosInSet = positionInGroup;
-  *aSetSize = setSize;
 }
 
 PRInt32
 nsAccessible::GetLevelInternal()
 {
   PRInt32 level = nsAccUtils::GetDefaultLevel(this);
 
   PRUint32 role = nsAccUtils::Role(this);
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -47,16 +47,17 @@
 #include "nsIAccessibleValue.h"
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleStates.h"
 
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 
+class AccGroupInfo;
 class nsAccessible;
 class nsAccEvent;
 struct nsRoleMapEntry;
 
 struct nsRect;
 class nsIContent;
 class nsIFrame;
 class nsIAtom;
@@ -319,17 +320,22 @@ protected:
    * Cache accessible children.
    */
   virtual void CacheChildren();
 
   /**
    * Set accessible parent and index in parent.
    */
   void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
-  void UnbindFromParent() { mParent = nsnull; mIndexInParent = -1; }
+  void UnbindFromParent()
+  {
+    mParent = nsnull;
+    mIndexInParent = -1;
+    mGroupInfo = nsnull;
+  }
 
   /**
    * Return sibling accessible at the given offset.
    */
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull);
 
   //////////////////////////////////////////////////////////////////////////////
@@ -420,31 +426,39 @@ protected:
    * Return the action rule based on ARIA enum constants EActionRule
    * (see nsARIAMap.h). Used by GetNumActions() and GetActionName().
    *
    * @param aStates  [in] states of the accessible
    */
   PRUint32 GetActionRule(PRUint32 aStates);
 
   /**
+   * Return group info.
+   */
+  AccGroupInfo* GetGroupInfo();
+
+  /**
    * Fires platform accessible event. It's notification method only. It does
    * change nothing on Gecko side. Don't use it until you're sure what you do
    * (see example in XUL tree accessible), use nsEventShell::FireEvent()
    * instead. MUST be overridden in wrap classes.
    *
    * @param aEvent  the accessible event to fire.
    */
   virtual nsresult FirePlatformEvent(nsAccEvent *aEvent) = 0;
 
   // Data Members
   nsRefPtr<nsAccessible> mParent;
   nsTArray<nsRefPtr<nsAccessible> > mChildren;
   PRBool mAreChildrenInitialized;
   PRInt32 mIndexInParent;
 
+  nsAutoPtr<AccGroupInfo> mGroupInfo;
+  friend class AccGroupInfo;
+
   nsRoleMapEntry *mRoleMapEntry; // Non-null indicates author-supplied role; possibly state & value as well
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessible,
                               NS_ACCESSIBLE_IMPL_IID)
 
 #endif  
 
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -949,16 +949,36 @@ nsHTMLTableAccessible::GetRowIndexAt(PRI
   PRInt32 column;
   nsresult rv = tableLayout->GetRowAndColumnByIndex(aIndex, aRow, &column);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return (*aRow == -1 || column == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
 }
 
 NS_IMETHODIMP
+nsHTMLTableAccessible::GetRowAndColumnIndicesAt(PRInt32 aIndex,
+                                                PRInt32* aRowIdx,
+                                                PRInt32* aColumnIdx)
+{
+  NS_ENSURE_ARG_POINTER(aRowIdx);
+  *aRowIdx = -1;
+  NS_ENSURE_ARG_POINTER(aColumnIdx);
+  *aColumnIdx = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsITableLayout* tableLayout = GetTableLayout();
+  if (tableLayout)
+    tableLayout->GetRowAndColumnByIndex(aIndex, aRowIdx, aColumnIdx);
+
+  return (*aRowIdx == -1 || *aColumnIdx == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
+}
+
+NS_IMETHODIMP
 nsHTMLTableAccessible::GetColumnExtentAt(PRInt32 aRowIndex,
                                          PRInt32 aColumnIndex,
                                          PRInt32 *aExtentCount)
 {
   nsITableLayout *tableLayout = GetTableLayout();
   NS_ENSURE_STATE(tableLayout);
 
   nsCOMPtr<nsIDOMElement> domElement;
--- a/accessible/src/msaa/CAccessibleTable.cpp
+++ b/accessible/src/msaa/CAccessibleTable.cpp
@@ -661,43 +661,38 @@ CAccessibleTable::get_rowColumnExtentsAt
   *aColumnExtents = 0;
   *aIsSelected = false;
 
   nsCOMPtr<nsIAccessibleTable> tableAcc(do_QueryObject(this));
   NS_ASSERTION(tableAcc, CANT_QUERY_ASSERTION_MSG);
   if (!tableAcc)
     return E_FAIL;
 
-  PRInt32 row = -1;
-  nsresult rv = tableAcc->GetRowIndexAt(aIndex, &row);
-  if (NS_FAILED(rv))
-    return GetHRESULT(rv);
-
-  PRInt32 column = -1;
-  rv = tableAcc->GetColumnIndexAt(aIndex, &column);
+  PRInt32 rowIdx = -1, columnIdx = -1;
+  nsresult rv = tableAcc->GetRowAndColumnIndicesAt(aIndex, &rowIdx, &columnIdx);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   PRInt32 rowExtents = 0;
-  rv = tableAcc->GetRowExtentAt(row, column, &rowExtents);
+  rv = tableAcc->GetRowExtentAt(rowIdx, columnIdx, &rowExtents);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   PRInt32 columnExtents = 0;
-  rv = tableAcc->GetColumnExtentAt(row, column, &columnExtents);
+  rv = tableAcc->GetColumnExtentAt(rowIdx, columnIdx, &columnExtents);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   PRBool isSelected = PR_FALSE;
-  rv = tableAcc->IsCellSelected(row, column, &isSelected);
+  rv = tableAcc->IsCellSelected(rowIdx, columnIdx, &isSelected);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
-  *aRow = row;
-  *aColumn = column;
+  *aRow = rowIdx;
+  *aColumn = columnIdx;
   *aRowExtents = rowExtents;
   *aColumnExtents = columnExtents;
   *aIsSelected = isSelected;
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -408,16 +408,38 @@ nsXULListboxAccessible::GetRowIndexAt(PR
   nsresult rv = GetColumnCount(&columnCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aRow = aIndex / columnCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsXULListboxAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
+                                                 PRInt32* aRowIndex,
+                                                 PRInt32* aColumnIndex)
+{
+  NS_ENSURE_ARG_POINTER(aRowIndex);
+  *aRowIndex = -1;
+  NS_ENSURE_ARG_POINTER(aColumnIndex);
+  *aColumnIndex = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  PRInt32 columnCount = 0;
+  nsresult rv = GetColumnCount(&columnCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aColumnIndex = aCellIndex % columnCount;
+  *aRowIndex = aCellIndex / columnCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXULListboxAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn,
                                           PRInt32 *aCellSpans)
 {
   NS_ENSURE_ARG_POINTER(aCellSpans);
   *aCellSpans = 1;
 
   return NS_OK;
 }
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -407,16 +407,38 @@ nsXULTreeGridAccessible::GetRowIndexAt(P
   nsresult rv = GetColumnCount(&columnCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aRowIndex = aCellIndex / columnCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsXULTreeGridAccessible::GetRowAndColumnIndicesAt(PRInt32 aCellIndex,
+                                                  PRInt32* aRowIndex,
+                                                  PRInt32* aColumnIndex)
+{
+  NS_ENSURE_ARG_POINTER(aRowIndex);
+  *aRowIndex = -1;
+  NS_ENSURE_ARG_POINTER(aColumnIndex);
+  *aColumnIndex = -1;
+
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  PRInt32 columnCount = 0;
+  nsresult rv = GetColumnCount(&columnCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aColumnIndex = aCellIndex % columnCount;
+  *aRowIndex = aCellIndex / columnCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXULTreeGridAccessible::GetColumnExtentAt(PRInt32 aRowIndex,
                                            PRInt32 aColumnIndex,
                                            PRInt32 *aExtentCount)
 {
   NS_ENSURE_ARG_POINTER(aExtentCount);
   *aExtentCount = 1;
 
   return NS_OK;
--- a/accessible/tests/mochitest/table.js
+++ b/accessible/tests/mochitest/table.js
@@ -219,45 +219,59 @@ function testTableIndexes(aIdentifier, a
         cellAcc = tableAcc.getCellAt(rowIdx, colIdx);
       } catch (e) { }
 
       ok(idx != -1 && cellAcc || idx == -1 && !cellAcc,
          id + ": Can't get cell accessible at row = " + rowIdx + ", column = " + colIdx);
 
       if (idx != - 1) {
 
-        // getRowAtIndex
+        // getRowIndexAt
         var origRowIdx = rowIdx;
         while (origRowIdx > 0 &&
                aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx])
           origRowIdx--;
 
         try {
           obtainedRowIdx = tableAcc.getRowIndexAt(idx);
         } catch (e) {
           ok(false, id + ": can't get row index for cell index " + idx + "," + e);
         }
 
         is(obtainedRowIdx, origRowIdx,
-           id + ": row for index " + idx +" is not correct");
+           id + ": row for index " + idx + " is not correct (getRowIndexAt)");
 
-        // getColumnAtIndex
+        // getColumnIndexAt
         var origColIdx = colIdx;
         while (origColIdx > 0 &&
                aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1])
           origColIdx--;
 
         try {
           obtainedColIdx = tableAcc.getColumnIndexAt(idx);
         } catch (e) {
           ok(false, id + ": can't get column index for cell index " + idx + "," + e);
         }
 
         is(obtainedColIdx, origColIdx,
-           id + ": column  for index " + idx +" is not correct");
+           id + ": column  for index " + idx + " is not correct (getColumnIndexAt)");
+
+        // getRowAndColumnIndicesAt
+        var obtainedRowIdxObj = { }, obtainedColIdxObj = { };
+        try {
+          tableAcc.getRowAndColumnIndicesAt(idx, obtainedRowIdxObj,
+                                            obtainedColIdxObj);
+        } catch (e) {
+          ok(false, id + ": can't get row and column indices for cell index " + idx + "," + e);
+        }
+
+        is(obtainedRowIdxObj.value, origRowIdx,
+           id + ": row for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
+        is(obtainedColIdxObj.value, origColIdx,
+           id + ": column  for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
 
         if (cellAcc) {
 
           var cellId = prettyName(cellAcc);
           cellAcc = getAccessible(cellAcc, [nsIAccessibleTableCell]);
 
           // cell: 'table-cell-index' attribute
           var attrs = cellAcc.attributes;
--- a/browser/app/macbuild/Contents/Info.plist.in
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -129,10 +129,12 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
 	<string>%APP_VERSION%</string>
 	<key>NSAppleScriptEnabled</key>
 	<true/>
 	<key>CGDisableCoalescedUpdates</key>
 	<true/>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.5</string>
 </dict>
 </plist>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -315,18 +315,16 @@ pref("browser.tabs.closeWindowWithLastTa
 pref("browser.tabs.insertRelatedAfterCurrent", true);
 pref("browser.tabs.warnOnClose", true);
 pref("browser.tabs.warnOnOpen", true);
 pref("browser.tabs.maxOpenBeforeWarn", 15);
 pref("browser.tabs.loadInBackground", true);
 pref("browser.tabs.opentabfor.middleclick", true);
 pref("browser.tabs.loadDivertedInBackground", false);
 pref("browser.tabs.loadBookmarksInBackground", false);
-pref("browser.tabs.tabMinWidth", 100);
-pref("browser.tabs.tabMaxWidth", 250);
 pref("browser.tabs.tabClipWidth", 140);
 
 // Where to show tab close buttons:
 // 0  on active tab only
 // 1  on all tabs until tabClipWidth is reached, then active tab only
 // 2  no close buttons at all
 // 3  at the end of the tabstrip
 pref("browser.tabs.closeButtons", 1);
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -125,17 +125,17 @@ var FullZoom = {
 
     // Retrieve the initial status of the Private Browsing mode.
     this._inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
                               getService(Ci.nsIPrivateBrowsingService).
                               privateBrowsingEnabled;
 
     this._siteSpecificPref =
       gPrefService.getBoolPref("browser.zoom.siteSpecific");
-    this.updateBackgroundTabs = 
+    this.updateBackgroundTabs =
       gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
     // Listen for changes to the browser.zoom branch so we can enable/disable
     // updating background tabs and per-site saving and restoring of zoom levels.
     gPrefService.addObserver("browser.zoom.", this, true);
   },
 
   destroy: function FullZoom_destroy() {
     let os = Cc["@mozilla.org/observer-service;1"].
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -17,22 +17,28 @@ tabbrowser {
 #tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #navigator-toolbox[customizing="true"] > #TabsToolbar > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
   visibility: collapse;
 }
 
 .tabbrowser-tab {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
+}
+
+.tabbrowser-tab:not([pinned]) {
   -moz-box-flex: 100;
+  max-width: 250px;
+  min-width: 100px;
+  width: 0;
 }
 
 .tabbrowser-tab:not([fadein]) {
-  max-width: 1px !important;
-  min-width: 1px !important;
+  max-width: 1px;
+  min-width: 1px;
 }
 
 .tabbrowser-tab[fadein]:not([pinned]) {
   -moz-transition: min-width .2s ease-out, max-width .25s ease-out;
 }
 
 .tabbrowser-tab:not([fadein]) > .tab-text,
 .tabbrowser-tab:not([fadein]) > .tab-icon-image,
@@ -43,19 +49,16 @@ tabbrowser {
 .tabbrowser-tab[fadein] > .tab-text,
 .tabbrowser-tab[fadein] > .tab-icon-image,
 .tabbrowser-tab[fadein] > .tab-close-button {
   -moz-transition: opacity .25s;
 }
 
 .tabbrowser-tab[pinned] {
   position: fixed;
-  -moz-box-flex: 0;
-  min-width: 0 !important;
-  max-width: none !important;
 }
 
 .tabbrowser-tab[pinned] > .tab-text {
   display: none;
 }
 
 #alltabs-popup {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
@@ -76,16 +79,22 @@ toolbar[printpreview="true"] {
 #TabsToolbar {
   -moz-box-ordinal-group: 100;
 }
 
 #navigator-toolbox[tabsontop="true"] > #TabsToolbar {
   -moz-box-ordinal-group: 10;
 }
 
+%ifdef MENUBAR_CAN_AUTOHIDE
+#main-window[inFullscreen] > #appmenu-button-container {
+  display: none;
+}
+%endif
+
 toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] {
   display: none;
 }
 
 toolbar[mode="icons"] > #reload-button:not([displaystop]) + #stop-button,
 toolbar[mode="icons"] > #reload-button[displaystop] {
   visibility: collapse;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4009,17 +4009,17 @@ var XULBrowserWindow = {
     // check the current value so we don't trigger an attribute change
     // and cause needless (slow!) UI updates
     if (this.statusText != text) {
       this.statusTextField.label = text;
       this.statusText = text;
     }
   },
 
-  onLinkIconAvailable: function (aBrowser, aIconURL) {
+  onLinkIconAvailable: function (aIconURL) {
     if (gProxyFavIcon && gBrowser.userTypedValue === null)
       PageProxySetIcon(aIconURL); // update the favicon in the URL bar
   },
 
   onProgressChange: function (aWebProgress, aRequest,
                               aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress) {
     // Check this._busyUI to be safe, because we don't want to update
@@ -4485,41 +4485,33 @@ var CombinedStopReload = {
     if (this._timer) {
       clearTimeout(this._timer);
       this._timer = 0;
     }
   }
 };
 
 var TabsProgressListener = {
-  onProgressChange: function (aBrowser, aWebProgress, aRequest,
-                              aCurSelfProgress, aMaxSelfProgress,
-                              aCurTotalProgress, aMaxTotalProgress) {
-  },
-
+#ifdef MOZ_CRASHREPORTER
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-#ifdef MOZ_CRASHREPORTER
     if (aRequest instanceof Ci.nsIChannel &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT &&
         gCrashReporter.enabled) {
       gCrashReporter.annotateCrashReport("URL", aRequest.URI.spec);
     }
+  },
 #endif
-  },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
     // Filter out any sub-frame loads
     if (aBrowser.contentWindow == aWebProgress.DOMWindow)
       FullZoom.onLocationChange(aLocationURI, false, aBrowser);
   },
 
-  onStatusChange: function (aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
-  },
-
   onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
     if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       let refreshButtonText =
         gNavigatorBundle.getString("refreshBlocked.goButton");
       let refreshButtonAccesskey =
         gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
@@ -4556,19 +4548,16 @@ var TabsProgressListener = {
                                              buttons);
         notification.refreshURI = aURI;
         notification.delay = aDelay;
         notification.docShell = docShell;
       }
       return false;
     }
     return true;
-  },
-
-  onSecurityChange: function (aBrowser, aWebProgress, aRequest, aState) {
   }
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
@@ -4722,22 +4711,22 @@ var TabsOnTop = {
     this.syncCommand();
 
     return val;
   }
 }
 
 #ifdef MENUBAR_CAN_AUTOHIDE
 function updateAppButtonDisplay() {
-  var menubarHidden =
+  var displayAppButton = window.menubar.visible &&
     document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
 
-  document.getElementById("appmenu-button-container").hidden = !menubarHidden;
-
-  if (menubarHidden)
+  document.getElementById("appmenu-button-container").hidden = !displayAppButton;
+
+  if (displayAppButton)
     document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
   else
     document.documentElement.removeAttribute("chromemargin");
 }
 #endif
 
 function displaySecurityInfo()
 {
@@ -7767,16 +7756,20 @@ var TabContextMenu = {
 
     // Session store
     // XXXzeniko should't we just disable this item as we disable
     // the tabbrowser-multiple items above - for consistency?
     document.getElementById("context_undoCloseTab").hidden =
       Cc["@mozilla.org/browser/sessionstore;1"].
       getService(Ci.nsISessionStore).
       getClosedTabCount(window) == 0;
+      
+    // Only one of pin/unpin should be visible
+    document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
+    document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
   }
 };
 
 XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
   Cu.import("resource://gre/modules/HUDService.jsm");
   try {
     return HUDService.consoleUI;
   }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -121,16 +121,22 @@
       <menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
                 tbattr="tabbrowser-multiple"
                 oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
       <menuseparator/>
       <menuitem id="context_openTabInWindow" label="&openTabInNewWindow.label;"
                 accesskey="&openTabInNewWindow.accesskey;"
                 tbattr="tabbrowser-multiple"
                 oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
+      <menuitem id="context_pinTab" label="&pinTab.label;"
+                accesskey="&pinTab.accesskey;"
+                oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
+      <menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
+                accesskey="&unpinTab.accesskey;"
+                oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
       <menuseparator/>
       <menuitem id="context_bookmarkTab"
                 label="&bookmarkThisTab.label;"
                 accesskey="&bookmarkThisTab.accesskey;"
                 oncommand="BookmarkThisTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_bookmarkAllTabs"
                 label="&bookmarkAllTabs.label;"
                 accesskey="&bookmarkAllTabs.accesskey;"
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -117,16 +117,22 @@ Sanitizer.prototype = {
         const Ci = Components.interfaces;
         var cacheService = Cc["@mozilla.org/network/cache-service;1"].
                           getService(Ci.nsICacheService);
         try {
           // Cache doesn't consult timespan, nor does it have the
           // facility for timespan-based eviction.  Wipe it.
           cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
         } catch(er) {}
+
+        var imageCache = Cc["@mozilla.org/image/cache;1"].
+                         getService(Ci.imgICache);
+        try {
+          imageCache.clearCache(false); // true=chrome, false=content
+        } catch(er) {}
       },
       
       get canClear()
       {
         return true;
       }
     },
     
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -258,16 +258,63 @@
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
             return (aBrowser || this.mCurrentBrowser).parentNode;
           ]]>
         </body>
       </method>
 
+      <method name="_callProgressListeners">
+        <parameter name="aBrowser"/>
+        <parameter name="aMethod"/>
+        <parameter name="aArguments"/>
+        <parameter name="aCallGlobalListeners"/>
+        <parameter name="aCallTabsListeners"/>
+        <body><![CDATA[
+          var rv = true;
+
+          if (!aBrowser)
+            aBrowser = this.mCurrentBrowser;
+
+          if (aCallGlobalListeners != false &&
+              aBrowser == this.mCurrentBrowser) {
+            this.mProgressListeners.forEach(function (p) {
+              if (aMethod in p) {
+                try {
+                  if (!p[aMethod].apply(p, aArguments))
+                    rv = false;
+                } catch (e) {
+                  // don't inhibit other listeners
+                  Components.utils.reportError(e);
+                }
+              }
+            });
+          }
+
+          if (aCallTabsListeners != false) {
+            aArguments.unshift(aBrowser);
+
+            this.mTabsProgressListeners.forEach(function (p) {
+              if (aMethod in p) {
+                try {
+                  if (!p[aMethod].apply(p, aArguments))
+                    rv = false;
+                } catch (e) {
+                  // don't inhibit other listeners
+                  Components.utils.reportError(e);
+                }
+              }
+            });
+          }
+
+          return rv;
+        ]]></body>
+      </method>
+
       <!-- A web progress listener object definition for a given tab. -->
       <method name="mTabProgressListener">
         <parameter name="aTab"/>
         <parameter name="aBrowser"/>
         <parameter name="aStartsBlank"/>
         <body>
         <![CDATA[
           return ({
@@ -288,75 +335,54 @@
             destroy: function () {
               this._cancelStalledTimer();
               this.mTab.removeAttribute("stalled");
               delete this.mTab;
               delete this.mBrowser;
               delete this.mTabBrowser;
             },
 
-            onProgressChange : function (aWebProgress, aRequest,
-                                         aCurSelfProgress, aMaxSelfProgress,
-                                         aCurTotalProgress, aMaxTotalProgress)
-            {
+            _callProgressListeners: function () {
+              Array.unshift(arguments, this.mBrowser);
+              return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
+            },
+
+            onProgressChange: function (aWebProgress, aRequest,
+                                        aCurSelfProgress, aMaxSelfProgress,
+                                        aCurTotalProgress, aMaxTotalProgress) {
               this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
 
               if (this.mBlank)
                 return;
 
               if (this.mTotalProgress) {
                 const STATES = 8;
                 let state = Math.ceil(STATES * this.mTotalProgress);
                 if (state != this.mTab.getAttribute("progress")) {
                   this.mTab.setAttribute("progress", state);
                   this.mTab.removeAttribute("stalled");
                   this._startStalledTimer();
                 }
               }
 
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
-                  if (p)
-                    try {
-                      p.onProgressChange(aWebProgress, aRequest,
-                                         aCurSelfProgress, aMaxSelfProgress,
-                                         aCurTotalProgress, aMaxTotalProgress);
-                    } catch (e) {
-                      // don't inhibit other listeners
-                      Components.utils.reportError(e);
-                    }
-                }
-              }
-
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p)
-                  try {
-                    p.onProgressChange(this.mBrowser, aWebProgress, aRequest,
-                                       aCurSelfProgress, aMaxSelfProgress,
-                                       aCurTotalProgress, aMaxTotalProgress);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
+              this._callProgressListeners("onProgressChange",
+                                          [aWebProgress, aRequest,
+                                           aCurSelfProgress, aMaxSelfProgress,
+                                           aCurTotalProgress, aMaxTotalProgress]);
             },
 
-            onProgressChange64 : function (aWebProgress, aRequest,
-                                         aCurSelfProgress, aMaxSelfProgress,
-                                         aCurTotalProgress, aMaxTotalProgress)
-            {
+            onProgressChange64: function (aWebProgress, aRequest,
+                                          aCurSelfProgress, aMaxSelfProgress,
+                                          aCurTotalProgress, aMaxTotalProgress) {
               return this.onProgressChange(aWebProgress, aRequest,
                 aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
                 aMaxTotalProgress);
             },
 
-            onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
-            {
+            onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
               if (!aRequest)
                 return;
 
               var oldBlank = this.mBlank;
 
               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
               const nsIChannel = Components.interfaces.nsIChannel;
 
@@ -431,56 +457,41 @@
 
                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
                   this.mTabBrowser.setTabTitle(this.mTab);
 
                 if (this.mTabBrowser.mCurrentTab == this.mTab)
                   this.mTabBrowser.mIsBusy = false;
               }
 
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
-                  if (p)
-                    try {
-                      if (!oldBlank)
-                        p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
-                      // make sure that the visible status of new blank tabs is correctly set
-                      else if ("onUpdateCurrentBrowser" in p)
-                        p.onUpdateCurrentBrowser(aStateFlags, aStatus, "", 0);
-                    } catch (e) {
-                      // don't inhibit other listeners
-                      Components.utils.reportError(e);
-                    }
-                }
+              if (oldBlank) {
+                this._callProgressListeners("onUpdateCurrentBrowser",
+                                            [aStateFlags, aStatus, "", 0],
+                                            true, false);
+              } else {
+                this._callProgressListeners("onStateChange",
+                                            [aWebProgress, aRequest, aStateFlags, aStatus],
+                                            true, false);
               }
 
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p)
-                  try {
-                    p.onStateChange(this.mBrowser, aWebProgress, aRequest, aStateFlags, aStatus);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
+              this._callProgressListeners("onStateChange",
+                                          [aWebProgress, aRequest, aStateFlags, aStatus],
+                                          false);
 
               if (aStateFlags & (nsIWebProgressListener.STATE_START |
                                  nsIWebProgressListener.STATE_STOP)) {
                 // reset cached temporary values at beginning and end
                 this.mMessage = "";
                 this.mTotalProgress = 0;
               }
               this.mStateFlags = aStateFlags;
               this.mStatus = aStatus;
             },
 
-            onLocationChange : function(aWebProgress, aRequest, aLocation)
-            {
+            onLocationChange: function (aWebProgress, aRequest, aLocation) {
               // The document loaded correctly, clear the value if we should
               if (this.mBrowser.userTypedClear > 0)
                 this.mBrowser.userTypedValue = null;
 
               // Don't clear the favicon if this onLocationChange was triggered
               // by a pushState or a replaceState.  See bug 550565.
               if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
                   aWebProgress.isLoadingDocument &&
@@ -492,139 +503,45 @@
               this.mBrowser.missingPlugins = null;
 
               var browserHistory = this.mTabBrowser.mBrowserHistory;
               if ("lastURI" in this.mBrowser && this.mBrowser.lastURI)
                 browserHistory.unregisterOpenPage(this.mBrowser.lastURI);
               browserHistory.registerOpenPage(aLocation);
 
               if (!this.mBlank) {
-                if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                  for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                    let p = this.mTabBrowser.mProgressListeners[i];
-                    if (p)
-                      try {
-                        p.onLocationChange(aWebProgress, aRequest, aLocation);
-                      } catch (e) {
-                        // don't inhibit other listeners
-                        Components.utils.reportError(e);
-                      }
-                  }
-                }
-
-                for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mTabsProgressListeners[i];
-                  if (p)
-                    try {
-                      p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
-                    } catch (e) {
-                      // don't inhibit other listeners
-                      Components.utils.reportError(e);
-                    }
-                }
+                this._callProgressListeners("onLocationChange",
+                                            [aWebProgress, aRequest, aLocation]);
               }
 
               this.mBrowser.lastURI = aLocation;
 
             },
 
-            onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
-            {
+            onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
               if (this.mBlank)
                 return;
 
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
-                  if (p)
-                    try {
-                      p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
-                    } catch (e) {
-                      // don't inhibit other listeners
-                      Components.utils.reportError(e);
-                    }
-                }
-              }
-
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p)
-                  try {
-                    p.onStatusChange(this.mBrowser, aWebProgress, aRequest, aStatus, aMessage);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
+              this._callProgressListeners("onStatusChange",
+                                          [aWebProgress, aRequest, aStatus, aMessage]);
 
               this.mMessage = aMessage;
             },
 
-            onSecurityChange : function(aWebProgress, aRequest, aState)
-            {
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
-                  if (p)
-                    try {
-                      p.onSecurityChange(aWebProgress, aRequest, aState);
-                    } catch (e) {
-                      // don't inhibit other listeners
-                      Components.utils.reportError(e);
-                    }
-                }
-              }
-
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p)
-                  try {
-                    p.onSecurityChange(this.mBrowser, aWebProgress, aRequest, aState);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
+            onSecurityChange: function (aWebProgress, aRequest, aState) {
+              this._callProgressListeners("onSecurityChange",
+                                          [aWebProgress, aRequest, aState]);
             },
 
-            onRefreshAttempted : function(aWebProgress, aURI, aDelay, aSameURI)
-            {
-              var allowRefresh = true;
-              if (this.mTabBrowser.mCurrentTab == this.mTab) {
-                for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
-                  let p = this.mTabBrowser.mProgressListeners[i];
-                  if (p && "onRefreshAttempted" in p) {
-                    try {
-                      if (!p.onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI))
-                        allowRefresh = false;
-                     } catch (e) {
-                       // don't inhibit other listeners
-                       Components.utils.reportError(e);
-                     }
-                  }
-                }
-              }
-
-              for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
-                let p = this.mTabBrowser.mTabsProgressListeners[i];
-                if (p && "onRefreshAttempted" in p) {
-                  try {
-                    if (!p.onRefreshAttempted(this.mBrowser, aWebProgress, aURI, aDelay, aSameURI))
-                      allowRefresh = false;
-                   } catch (e) {
-                     // don't inhibit other listeners
-                     Components.utils.reportError(e);
-                   }
-                }
-              }
-              return allowRefresh;
+            onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) {
+              return this._callProgressListeners("onRefreshAttempted",
+                                                 [aWebProgress, aURI, aDelay, aSameURI]);
             },
 
-            QueryInterface : function(aIID)
-            {
+            QueryInterface: function (aIID) {
               if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
                   aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
                   aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
                   aIID.equals(Components.interfaces.nsISupports))
                 return this;
               throw Components.results.NS_NOINTERFACE;
             },
 
@@ -658,39 +575,17 @@
               if (!(aURI instanceof Ci.nsIURI))
                 aURI = makeURI(aURI);
               this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
                                                             aURI, false);
             }
 
             this.updateIcon(aTab);
 
-            if (browser == this.mCurrentBrowser) {
-              for (let i = 0; i < this.mProgressListeners.length; i++) {
-                let p = this.mProgressListeners[i];
-                if ('onLinkIconAvailable' in p)
-                  try {
-                    p.onLinkIconAvailable(browser, browser.mIconURL);
-                  } catch (e) {
-                    // don't inhibit other listeners
-                    Components.utils.reportError(e);
-                  }
-              }
-            }
-
-            for (let i = 0; i < this.mTabsProgressListeners.length; i++) {
-              let p = this.mTabsProgressListeners[i];
-              if ('onLinkIconAvailable' in p)
-                try {
-                  p.onLinkIconAvailable(browser, browser.mIconURL);
-                } catch (e) {
-                  // don't inhibit other listeners
-                  Components.utils.reportError(e);
-                }
-            }
+            this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
           ]]>
         </body>
       </method>
 
       <method name="getIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
@@ -859,76 +754,60 @@
               this.mCurrentBrowser.updatePageReport();
 
             // Update the URL bar.
             var loc = this.mCurrentBrowser.currentURI;
 
             var webProgress = this.mCurrentBrowser.webProgress;
             var securityUI = this.mCurrentBrowser.securityUI;
 
-            var i, p;
-            for (i = 0; i < this.mProgressListeners.length; i++) {
-              p = this.mProgressListeners[i];
-              if (p)
-                try {
-                  p.onLocationChange(webProgress, null, loc);
-                  if (securityUI)
-                    p.onSecurityChange(webProgress, null, securityUI.state);
-
-                  // make sure that all status indicators are properly updated
-                  if ("onUpdateCurrentBrowser" in p) {
-                    let listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
-                    if (listener && listener.mStateFlags)
-                      p.onUpdateCurrentBrowser(listener.mStateFlags, listener.mStatus,
-                                               listener.mMessage, listener.mTotalProgress);
-                  }
-                } catch (e) {
-                  // don't inhibit other listeners or following code
-                  Components.utils.reportError(e);
-                }
+            this._callProgressListeners(null, "onLocationChange",
+                                        [webProgress, null, loc], true, false);
+
+            if (securityUI) {
+              this._callProgressListeners(null, "onSecurityChange",
+                                          [webProgress, null, securityUI.state], true, false);
+            }
+
+            var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
+            if (listener && listener.mStateFlags) {
+              this._callProgressListeners(null, "onUpdateCurrentBrowser",
+                                          [listener.mStateFlags, listener.mStatus,
+                                           listener.mMessage, listener.mTotalProgress],
+                                          true, false);
             }
 
             // Don't switch the fast find or update the titlebar (bug 540248) - this tab switch is temporary
             if (!this._previewMode) {
               this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
 
               this.updateTitlebar();
             }
 
             // If the new tab is busy, and our current state is not busy, then
             // we need to fire a start to all progress listeners.
             const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
             if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
               this.mIsBusy = true;
-              for (i = 0; i < this.mProgressListeners.length; i++) {
-                p = this.mProgressListeners[i];
-                if (p)
-                  try {
-                    p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
-                  } catch (e) {
-                    // don't inhibit other listeners or following code
-                    Components.utils.reportError(e);
-                  }
-              }
+              this._callProgressListeners(null, "onStateChange",
+                                          [webProgress, null,
+                                           nsIWebProgressListener.STATE_START |
+                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
+                                          true, false);
             }
 
             // If the new tab is not busy, and our current state is busy, then
             // we need to fire a stop to all progress listeners.
             if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
               this.mIsBusy = false;
-              for (i = 0; i < this.mProgressListeners.length; i++) {
-                p = this.mProgressListeners[i];
-                if (p)
-                  try {
-                    p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
-                  } catch (e) {
-                    // don't inhibit other listeners or following code
-                    Components.utils.reportError(e);
-                  }
-              }
+              this._callProgressListeners(null, "onStateChange",
+                                          [webProgress, null,
+                                           nsIWebProgressListener.STATE_STOP |
+                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
+                                          true, false);
             }
 
             // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
             // that might rely upon the other changes suppressed.
             // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
             if (!this._previewMode) {
               // We've selected the new tab, so go ahead and notify listeners.
               var event = document.createEvent("Events");
@@ -1049,21 +928,17 @@
               // create a filter and hook it up to our first browser
               filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                  .createInstance(Components.interfaces.nsIWebProgress);
               this.mTabFilters[0] = filter;
               this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             }
 
             // Remove all our progress listeners from the active browser's filter.
-            for (var i = 0; i < this.mProgressListeners.length; i++) {
-              var p = this.mProgressListeners[i];
-              if (p)
-                filter.removeProgressListener(p);
-            }
+            this.mProgressListeners.forEach(filter.removeProgressListener, filter);
 
             // Wire up a progress listener to our filter.
             const listener = this.mTabProgressListener(this.mCurrentTab,
                                                        this.mCurrentBrowser,
                                                        false);
             filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this.mTabListeners[0] = listener;
           ]]>
@@ -1205,19 +1080,16 @@
             var blank = !aURI || (aURI == "about:blank");
 
             if (blank)
               t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
             else
               t.setAttribute("label", aURI);
 
             t.setAttribute("crop", "end");
-            t.style.maxWidth = this.tabContainer.mTabMaxWidth + "px";
-            t.style.minWidth = this.tabContainer.mTabMinWidth + "px";
-            t.width = 0;
             t.setAttribute("validate", "never");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
             // transition in that case.
             if (aSkipAnimation ||
@@ -1761,22 +1633,18 @@
           ]]>
         </body>
       </method>
 
       <method name="removeProgressListener">
         <parameter name="aListener"/>
         <body>
           <![CDATA[
-            for (var i = 0; i < this.mProgressListeners.length; i++) {
-              if (this.mProgressListeners[i] == aListener) {
-                this.mProgressListeners.splice(i, 1);
-                break;
-              }
-            }
+            this.mProgressListeners =
+              this.mProgressListeners.filter(function (l) l != aListener);
 
             if (!this.mTabbedMode)
               // Don't forget to remove it from the filter we hooked it up to
               this.mTabFilters[0].removeProgressListener(aListener);
          ]]>
         </body>
       </method>
 
@@ -1787,19 +1655,18 @@
           this.mTabsProgressListeners.push(aListener);
         </body>
       </method>
 
       <method name="removeTabsProgressListener">
         <parameter name="aListener"/>
         <body>
         <![CDATA[
-          var pos = this.mTabsProgressListeners.indexOf(aListener);
-          if (pos >= 0)
-            this.mTabsProgressListeners.splice(pos, 1);
+          this.mTabsProgressListeners =
+            this.mTabsProgressListeners.filter(function (l) l != aListener);
         ]]>
         </body>
       </method>
 
       <method name="getBrowserForTab">
         <parameter name="aTab"/>
         <body>
         <![CDATA[
@@ -2500,28 +2367,23 @@
                            onclick="checkForMiddleClick(this, event);"
                            tooltiptext="&newTabButton.tooltip;"/>
       </xul:arrowscrollbox>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor>
         <![CDATA[
-          this.mTabMinWidth = Services.prefs.getIntPref("browser.tabs.tabMinWidth");
-          this.mTabMaxWidth = Services.prefs.getIntPref("browser.tabs.tabMaxWidth");
           this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
           this.mCloseButtons = Services.prefs.getIntPref("browser.tabs.closeButtons");
           this._closeWindowWithLastTab = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
 
           var tab = this.firstChild;
           tab.setAttribute("label",
                            this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"));
-          tab.style.minWidth = this.mTabMinWidth + "px";
-          tab.style.maxWidth = this.mTabMaxWidth + "px";
-          tab.width = 0;
           tab.setAttribute("crop", "end");
           tab.setAttribute("validate", "never");
           tab.setAttribute("onerror", "this.removeAttribute('image');");
           this.adjustTabstrip();
 
           Services.prefs.addObserver("browser.tabs.closeButtons", this._prefObserver, false);
           Services.prefs.addObserver("browser.tabs.autoHide", this._prefObserver, false);
           Services.prefs.addObserver("browser.tabs.closeWindowWithLastTab", this._prefObserver, false);
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -120,16 +120,17 @@ endif
                  browser_bug477014.js \
                  browser_bug479408.js \
                  browser_bug479408_sample.html \
                  browser_bug481560.js \
                  browser_bug484315.js \
                  browser_bug491431.js \
                  browser_bug495058.js \
                  browser_bug517902.js \
+                 browser_bug519216.js \
                  browser_bug520538.js \
                  browser_bug521216.js \
                  browser_bug537474.js \
                  browser_bug550565.js \
                  browser_bug553455.js \
                  browser_bug555224.js \
                  browser_bug555767.js \
                  browser_bug556061.js \
--- a/browser/base/content/test/browser_alltabslistener.js
+++ b/browser/base/content/test/browser_alltabslistener.js
@@ -33,21 +33,16 @@ var gFrontProgressListener = {
     info("FrontProgress: " + state + " 0x" + aState.toString(16));
     ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
     is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
     gFrontNotificationsPos++;
   }
 }
 
 var gAllProgressListener = {
-  onProgressChange: function (aBrowser, aWebProgress, aRequest,
-                              aCurSelfProgress, aMaxSelfProgress,
-                              aCurTotalProgress, aMaxTotalProgress) {
-  },
-
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
     var state = "onStateChange";
     info("AllProgress: " + state + " 0x" + aStateFlags.toString(16));
     ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
     ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
     is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
     gAllNotificationsPos++;
 
--- a/browser/base/content/test/browser_bug356571.js
+++ b/browser/base/content/test/browser_bug356571.js
@@ -52,22 +52,17 @@ var gProgressListener = {
       if (++this._runCount != kURIs.length)
         return;
       // Check we failed on unknown protocol (received an alert from docShell)
       ok(didFail, "Correctly failed on unknown protocol");
       // Check we opened all tabs
       ok(gBrowser.tabs.length == kURIs.length, "Correctly opened all expected tabs");
       finishTest();
     }
-  },
-
-  onProgressChange: function () {},
-  onLocationChange: function () {},
-  onStatusChange: function () {},
-  onSecurityChange: function () {}
+  }
 }
 
 function test() {
   todo(false, "temp. disabled");
   return; /* FIXME */
   waitForExplicitFinish();
   // Wait for all tabs to finish loading
   gBrowser.addTabsProgressListener(gProgressListener);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug519216.js
@@ -0,0 +1,48 @@
+function test() {
+  waitForExplicitFinish();
+  gBrowser.stop();
+  gBrowser.addProgressListener(progressListener1);
+  gBrowser.addProgressListener(progressListener2);
+  gBrowser.addProgressListener(progressListener3);
+  gBrowser.loadURI("data:text/plain,bug519216");
+}
+
+var calledListener1 = false;
+var progressListener1 = {
+  onLocationChange: function onLocationChange() {
+    calledListener1 = true;
+    gBrowser.removeProgressListener(this);
+  }
+};
+
+var calledListener2 = false;
+var progressListener2 = {
+  onLocationChange: function onLocationChange() {
+    ok(calledListener1, "called progressListener1 before progressListener2");
+    calledListener2 = true;
+    gBrowser.removeProgressListener(this);
+  }
+};
+
+var progressListener3 = {
+  onLocationChange: function onLocationChange() {
+    ok(calledListener2, "called progressListener2 before progressListener3");
+    gBrowser.removeProgressListener(this);
+    gBrowser.addProgressListener(progressListener4);
+    executeSoon(function () {
+      expectListener4 = true;
+      gBrowser.reload();
+    });
+  }
+};
+
+var expectListener4 = false;
+var progressListener4 = {
+  onLocationChange: function onLocationChange() {
+    ok(expectListener4, "didn't call progressListener4 for the first location change");
+    gBrowser.removeProgressListener(this);
+    gBrowser.addTab();
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+};
--- a/browser/base/content/test/browser_bug521216.js
+++ b/browser/base/content/test/browser_bug521216.js
@@ -30,20 +30,17 @@ function TabOpen(aEvent) {
     record(arguments.callee.name);
 }
 
 var progressListener = {
   onLocationChange: function onLocationChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
-  onProgressChange: function () {},
-  onSecurityChange: function () {},
   onStateChange: function onStateChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
-  onStatusChange: function () {},
   onLinkIconAvailable: function onLinkIconAvailable(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   }
 };
--- a/browser/base/content/test/browser_overflowScroll.js
+++ b/browser/base/content/test/browser_overflowScroll.js
@@ -20,17 +20,17 @@ function test() {
   // there may be some pending animations. That can cause a failure of
   // this tests, so, we should test this in another stack.
   setTimeout(doTest, 0);
 }
 
 function doTest() {
   tabstrip.smoothScroll = false;
 
-  var tabMinWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
+  var tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
   var tabCountForOverflow = Math.ceil(width(tabstrip) / tabMinWidth * 3);
   while (tabContainer.childNodes.length < tabCountForOverflow)
     gBrowser.addTab("about:blank", {skipAnimation: true});
 
   tabstrip.addEventListener("overflow", runOverflowTests, false);
 }
 
 function runOverflowTests(aEvent) {
--- a/browser/components/feeds/content/subscribe.js
+++ b/browser/components/feeds/content/subscribe.js
@@ -50,12 +50,12 @@ var SubscribeHandler = {
   writeContent: function SH_writeContent() {
     this._feedWriter.writeContent();
   },
 
   uninit: function SH_uninit() {
     this._feedWriter.close();
   },
   
-  subscribe: function FH_subscribe() {
+  subscribe: function SH_subscribe() {
     this._feedWriter.subscribe();
   }
 };
--- a/browser/components/places/tests/browser/browser_library_middleclick.js
+++ b/browser/components/places/tests/browser/browser_library_middleclick.js
@@ -90,31 +90,16 @@ var gTabsListener = {
       // Close all tabs.
       while (gBrowser.tabs.length > 1)
         gBrowser.removeCurrentTab();
       this._openTabsCount = 0;
 
       // Test finished.  This will move to the next one.
       waitForFocus(gCurrentTest.finish, gBrowser.ownerDocument.defaultView);
     }
-  },
-
-  onProgressChange: function(aBrowser, aWebProgress, aRequest,
-                             aCurSelfProgress, aMaxSelfProgress,
-                             aCurTotalProgress, aMaxTotalProgress) {
-  },
-  onStateChange: function(aBrowser, aWebProgress, aRequest,
-                          aStateFlags, aStatus) {
-  },  
-  onStatusChange: function(aBrowser, aWebProgress, aRequest,
-                           aStatus, aMessage) {
-  },
-  onSecurityChange: function(aBrowser, aWebProgress, aRequest, aState) {
-  },
-  noLinkIconAvailable: function(aBrowser) {
   }
 }
 
 //------------------------------------------------------------------------------
 // Open bookmark in a new tab.
 
 gTests.push({
   desc: "Open bookmark in a new tab.",
--- a/browser/components/preferences/cookies.xul
+++ b/browser/components/preferences/cookies.xul
@@ -136,11 +136,11 @@
               label="&button.removeallcookies.label;" accesskey="&button.removeallcookies.accesskey;"
               oncommand="gCookiesWindow.deleteAllCookies();"/>
       <spacer flex="1"/>
 #ifndef XP_MACOSX
       <button oncommand="close();" icon="close"
               label="&button.close.label;" accesskey="&button.close.accesskey;"/>
 #endif
     </hbox>
-    <resizer dir="bottomend"/>
+    <resizer type="window" dir="bottomend"/>
   </hbox>
 </window>
--- a/browser/components/preferences/permissions.xul
+++ b/browser/components/preferences/permissions.xul
@@ -106,11 +106,11 @@
               accesskey="&removeallpermissions.accesskey;" 
               oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
       <spacer flex="1"/>
 #ifndef XP_MACOSX
       <button oncommand="close();" icon="close"
               label="&button.close.label;" accesskey="&button.close.accesskey;"/>
 #endif
     </hbox>
-    <resizer dir="bottomend"/>
+    <resizer type="window" dir="bottomend"/>
   </hbox>
 </window>
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -566,16 +566,27 @@ PrivateBrowsingService.prototype = {
       try {
         cs.evictEntries(Ci.nsICache.STORE_ANYWHERE);
       } catch (ex) {
         Cu.reportError("Exception thrown while clearing the cache: " +
           ex.toString());
       }
     }
 
+    // Image Cache
+    let (imageCache = Cc["@mozilla.org/image/cache;1"].
+                      getService(Ci.imgICache)) {
+      try {
+        imageCache.clearCache(false); // true=chrome, false=content
+      } catch (ex) {
+        Cu.reportError("Exception thrown while clearing the image cache: " +
+          ex.toString());
+      }
+    }
+
     // Cookies
     let (cm = Cc["@mozilla.org/cookiemanager;1"].
               getService(Ci.nsICookieManager2)) {
       let enumerator = cm.getCookiesFromHost(aDomain);
       while (enumerator.hasMoreElements()) {
         let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
         cm.remove(cookie.host, cookie.name, cookie.path, false);
       }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js
@@ -87,21 +87,16 @@ function test() {
           step2();
         }
         else if (aTopic == "domwindowopened")
           ok(false, "Entering the private browsing mode should not open any view source window");
       }
       Services.ww.registerNotification(observer);
 
       gBrowser.addTabsProgressListener({
-        onLocationChange: function() {},
-        onProgressChange: function() {},
-        onSecurityChange: function() {},
-        onStatusChange: function() {},
-        onRefreshAttempted: function() {},
         onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
           if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP |
                              Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
             gBrowser.removeTabsProgressListener(this);
 
             step3();
           }
         }
--- a/browser/components/sessionstore/test/browser/browser_480148.js
+++ b/browser/components/sessionstore/test/browser/browser_480148.js
@@ -78,18 +78,17 @@ function test() {
     }
     return expected;
   }
 
   // the number of tests we're running
   let numTests = 4;
   let completedTests = 0;
 
-  // access the pref service just once
-  let tabMinWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
+  let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
 
   function runTest(testNum, totalTabs, selectedTab, shownTabs, order) {
     let test = {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                              Ci.nsISupportsWeakReference]),
 
       state: buildTestState(totalTabs, selectedTab),
       numTabsToShow: shownTabs,
--- a/browser/components/sessionstore/test/browser/browser_522545.js
+++ b/browser/components/sessionstore/test/browser/browser_522545.js
@@ -57,21 +57,17 @@ function test() {
   function waitForBrowserState(aState, aSetStateCallback) {
     var locationChanges = 0;
     gBrowser.addTabsProgressListener({
       onLocationChange: function (aBrowser) {
         if (++locationChanges == aState.windows[0].tabs.length) {
           gBrowser.removeTabsProgressListener(this);
           executeSoon(aSetStateCallback);
         }
-      },
-      onProgressChange: function () {},
-      onSecurityChange: function () {},
-      onStateChange: function () {},
-      onStatusChange: function () {}
+      }
     });
     ss.setBrowserState(JSON.stringify(aState));
   }
 
   // This tests the following use case:
   // User opens a new tab which gets focus. The user types something into the
   // address bar, then crashes or quits.
   function test_newTabFocused() {
@@ -206,21 +202,17 @@ function test() {
     // be in a non-userTypedValue case, while others should still have
     // userTypedValue and userTypedClear set.
     gBrowser.addTabsProgressListener({
       onLocationChange: function (aBrowser) {
         if (uris.indexOf(aBrowser.currentURI.spec) > -1) {
           gBrowser.removeTabsProgressListener(this);
           firstLocationChange();
         }
-      },
-      onProgressChange: function () {},
-      onSecurityChange: function () {},
-      onStateChange: function () {},
-      onStatusChange: function () {}
+      }
     });
 
     function firstLocationChange() {
       let state = JSON.parse(ss.getBrowserState());
       let hasUTV = state.windows[0].tabs.some(function(aTab) {
         return aTab.userTypedValue && aTab.userTypedClear && !aTab.entries.length;
       });
 
--- a/browser/components/wintaskbar/WindowsPreviewPerTab.jsm
+++ b/browser/components/wintaskbar/WindowsPreviewPerTab.jsm
@@ -525,26 +525,16 @@ TabWindow.prototype = {
         this.previews.splice(oldPos, 1);
         this.previews.splice(newPos, 0, preview);
         this.updateTabOrdering();
         break;
     }
   },
 
   //// Browser progress listener
-  onLocationChange: function () {
-  },
-  onProgressChange: function () {
-  },
-  onSecurityChange: function () {
-  },
-  onStateChange: function () {
-  },
-  onStatusChange: function () {
-  },
   onLinkIconAvailable: function (aBrowser, aIconURL) {
     let self = this;
     getFaviconAsImage(aIconURL, function (img) {
       let index = self.tabbrowser.browsers.indexOf(aBrowser);
       // Only add it if we've found the index.  The tab could have closed!
       if (index != -1)
         self.previews[index].icon = img;
     });
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -17,16 +17,20 @@
 <!ENTITY  reloadTab.label                    "Reload Tab">
 <!ENTITY  reloadTab.accesskey                "R">
 <!ENTITY  reloadAllTabs.label                "Reload All Tabs">
 <!ENTITY  reloadAllTabs.accesskey            "A">
 <!ENTITY  closeOtherTabs.label               "Close Other Tabs">
 <!ENTITY  closeOtherTabs.accesskey           "o">
 <!ENTITY  openTabInNewWindow.label           "Open in a New Window">
 <!ENTITY  openTabInNewWindow.accesskey       "W">
+<!ENTITY  pinTab.label                       "Make into App Tab">
+<!ENTITY  pinTab.accesskey                   "p">
+<!ENTITY  unpinTab.label                     "Make into Normal Tab">
+<!ENTITY  unpinTab.accesskey                 "k">
 <!ENTITY  bookmarkThisTab.label              "Bookmark This Tab">
 <!ENTITY  bookmarkThisTab.accesskey          "B">
 <!ENTITY  bookmarkAllTabs.label              "Bookmark All Tabs…">
 <!ENTITY  bookmarkAllTabs.accesskey          "T">
 <!ENTITY  undoCloseTab.label                 "Undo Close Tab">
 <!ENTITY  undoCloseTab.accesskey             "U">
 <!ENTITY  closeTab.label                     "Close Tab">
 <!ENTITY  closeTab.accesskey                 "c">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -47,17 +47,17 @@ addonInstallManage=Open Add-ons Manager
 addonInstallManage.accesskey=O
 
 # LOCALIZATION NOTE (addonError-1, addonError-2, addonError-3, addonError-4, addonErrorIncompatible, addonErrorBlocklisted):
 # #1 is the add-on name, #2 is the host name, #3 is the application name
 # #4 is the application version
 addonError-1=The add-on could not be downloaded because of a connection failure on #2.
 addonError-2=The add-on from #2 could not be installed because it does not match the add-on #3 expected.
 addonError-3=The add-on downloaded from #2 could not be installed because it appears to be corrupt.
-addonError-4=#1 could not be installed because Firefox cannot modify the needed file.
+addonError-4=#1 could not be installed because #3 cannot modify the needed file.
 addonErrorIncompatible=#1 could not be installed because it is not compatible with #3 #4.
 addonErrorBlocklisted=#1 could not be installed because it has a high risk of causing stability or security problems.
 
 # LOCALIZATION NOTE (lwthemeInstallRequest.message): %S will be replaced with
 # the host name of the site.
 lwthemeInstallRequest.message=This site (%S) attempted to install a theme.
 lwthemeInstallRequest.allowButton=Allow
 lwthemeInstallRequest.allowButton.accesskey=a
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -46,17 +46,16 @@ browser/components/certerror/Makefile
 browser/components/dirprovider/Makefile
 browser/components/feeds/Makefile
 browser/components/feeds/public/Makefile
 browser/components/feeds/src/Makefile
 browser/components/migration/Makefile
 browser/components/migration/public/Makefile
 browser/components/migration/src/Makefile
 browser/components/places/Makefile
-browser/components/places/public/Makefile
 browser/components/places/src/Makefile
 browser/components/preferences/Makefile
 browser/components/privatebrowsing/Makefile
 browser/components/privatebrowsing/src/Makefile
 browser/components/safebrowsing/Makefile
 browser/components/safebrowsing/src/Makefile
 browser/components/search/Makefile
 browser/components/sessionstore/Makefile
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -176,17 +176,17 @@ statusbarpanel#statusbar-display {
   #main-window[tabsontop="true"] > #appmenu-button-container > #appmenu-button {
     position: relative !important;
     margin-bottom: -1.6em !important;
   }
   #navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] {
     position: relative !important;
     background-color: -moz-dialog !important;
   }
-  #navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] ~ #TabsToolbar {
+  #navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] ~ #TabsToolbar:not([inFullscreen]) {
     -moz-padding-start: 10em !important;
   }
 %ifdef WINSTRIPE_AERO
 }
 %endif
 
 /* ::::: bookmark buttons ::::: */
 
--- a/browser/themes/winstripe/browser/pageInfo.css
+++ b/browser/themes/winstripe/browser/pageInfo.css
@@ -35,22 +35,38 @@
  * 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 "chrome://global/skin/";
 
 /* View buttons */
+#viewGroup {
+  -moz-padding-start: 10px;
+}
+
 #viewGroup > radio {
   list-style-image: url("chrome://browser/skin/pageInfo.png");
   -moz-box-orient: vertical;
   -moz-box-align: center;
   -moz-appearance: none;
   padding: 5px 3px 1px 3px;
+  margin: 0 1px;
+  min-width: 4.5em;
+}
+
+#viewGroup > radio:hover {
+  background-color: #E0E8F6;
+  color: black;
+}
+
+#viewGroup > radio[selected="true"] {
+  background-color: #C1D2EE;
+  color: black;
 }
 
 #topBar {
   border-bottom: 2px groove ThreeDFace;
   -moz-padding-start: 10px;
   background-color: -moz-Field;
   color: -moz-FieldText;
 }
--- a/build/pymake/.hg_archival.txt
+++ b/build/pymake/.hg_archival.txt
@@ -1,2 +1,2 @@
 repo: f5ab154deef2ffa97f1b2139589ae4a1962090a4
-node: 3476582628db128ad061c30cab1a74a0c5d14b9b
+node: 7ae0b4af32617677698f9de3ab76bcb154bbf085
--- a/build/pymake/pymake/data.py
+++ b/build/pymake/pymake/data.py
@@ -1397,17 +1397,17 @@ class Makefile(object):
                                Variables.FLAVOR_SIMPLE,
                                Variables.SOURCE_IMPLICIT, val)
 
     def foundtarget(self, t):
         """
         Inform the makefile of a target which is a candidate for being the default target,
         if there isn't already a default target.
         """
-        if self.defaulttarget is None:
+        if self.defaulttarget is None and t != '.PHONY':
             self.defaulttarget = t
 
     def getpatternvariables(self, pattern):
         assert isinstance(pattern, Pattern)
 
         for p, v in self._patternvariables:
             if p == pattern:
                 return v
--- a/build/pymake/pymake/parser.py
+++ b/build/pymake/pymake/parser.py
@@ -279,17 +279,17 @@ def ifeq(d, offset):
     if token not in _eqargstokenlist:
         raise SyntaxError("No arguments after conditional", d.getloc(offset))
 
     offset += 1
 
     if token == '(':
         arg1, t, offset = parsemakesyntax(d, offset, (',',), itermakefilechars)
         if t is None:
-            raise SyntaxError("Expected two arguments in conditional", d.getloc(offset))
+            raise SyntaxError("Expected two arguments in conditional", d.getloc(d.lend))
 
         arg1.rstrip()
 
         offset = d.skipwhitespace(offset)
         arg2, t, offset = parsemakesyntax(d, offset, (')',), itermakefilechars)
         if t is None:
             raise SyntaxError("Unexpected text in conditional", d.getloc(offset))
 
@@ -599,16 +599,19 @@ class ParseStackFrame(object):
         self.parent = parent
         self.expansion = expansion
         self.tokenlist = tokenlist
         self.openbrace = openbrace
         self.closebrace = closebrace
         self.function = function
         self.loc = loc
 
+    def __str__(self):
+        return "<state=%i expansion=%s tokenlist=%s openbrace=%s closebrace=%s>" % (self.parsestate, self.expansion, self.tokenlist, self.openbrace, self.closebrace)
+
 _matchingbrace = {
     '(': ')',
     '{': '}',
     }
 
 def parsemakesyntax(d, offset, stopon, iterfunc):
     """
     Given Data, parse it into a data.Expansion.
@@ -684,17 +687,17 @@ def parsemakesyntax(d, offset, stopon, i
                 e = data.Expansion.fromstring(c, loc)
                 stacktop.expansion.appendfunc(functions.VariableRef(loc, e))
         elif token in ('(', '{'):
             assert token == stacktop.openbrace
 
             stacktop.expansion.appendstr(token)
             stacktop = ParseStackFrame(_PARSESTATE_PARENMATCH, stacktop,
                                        stacktop.expansion,
-                                       (token, stacktop.closebrace),
+                                       (token, stacktop.closebrace, '$'),
                                        openbrace=token, closebrace=stacktop.closebrace, loc=d.getloc(tokenoffset))
         elif parsestate == _PARSESTATE_PARENMATCH:
             assert token == stacktop.closebrace
             stacktop.expansion.appendstr(token)
             stacktop = stacktop.parent
         elif parsestate == _PARSESTATE_TOPLEVEL:
             assert stacktop.parent is None
             return stacktop.expansion.finish(), token, offset
--- a/build/pymake/tests/default-target.mk
+++ b/build/pymake/tests/default-target.mk
@@ -1,12 +1,14 @@
 test: VAR = value
 
 %.do:
 	@echo TEST-FAIL: ran target "$@", should have run "all"
 
+.PHONY: test
+
 all:
 	@echo TEST-PASS: the default target is all
 
 test:
 	@echo TEST-FAIL: ran target "$@", should have run "all"
 
 test.do:
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/if-syntaxerr.mk
@@ -0,0 +1,6 @@
+#T returncode: 2
+
+ifeq ($(FOO,VAR))
+all:
+	@echo TEST_FAIL
+endif
--- a/build/pymake/tests/include-dynamic.mk
+++ b/build/pymake/tests/include-dynamic.mk
@@ -1,10 +1,10 @@
 $(shell \
-if test ! -f include-dynamic.inc; then \
+if ! test -f include-dynamic.inc; then \
   echo "TESTVAR = oldval" > include-dynamic.inc; \
   sleep 2; \
   echo "TESTVAR = newval" > include-dynamic.inc.in; \
 fi \
 )
 
 # before running the 'all' rule, we should be rebuilding include-dynamic.inc,
 # because there is a rule to do so
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/path-length.mk
@@ -0,0 +1,9 @@
+#T gmake skip
+
+$(shell \
+mkdir foo; \
+touch tfile; \
+)
+
+all: foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../tfile
+	@echo TEST-PASS
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -73,17 +73,17 @@ nsNullPrincipal::AddRef()
 
 NS_IMETHODIMP_(nsrefcnt)
 nsNullPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
   nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsNullPrincipal");
   if (count == 0) {
-    NS_DELETEXPCOM(this);
+    delete this;
   }
 
   return count;
 }
 
 nsNullPrincipal::nsNullPrincipal()
 {
 }
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -161,17 +161,17 @@ nsPrincipal::AddRef()
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
   nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsPrincipal");
   if (count == 0) {
-    NS_DELETEXPCOM(this);
+    delete this;
   }
 
   return count;
 }
 
 nsPrincipal::nsPrincipal()
   : mCapabilities(nsnull),
     mSecurityPolicy(nsnull),
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -70,17 +70,17 @@ nsSystemPrincipal::AddRef()
 
 NS_IMETHODIMP_(nsrefcnt)
 nsSystemPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
   nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
   if (count == 0) {
-    NS_DELETEXPCOM(this);
+    delete this;
   }
 
   return count;
 }
 
 
 ///////////////////////////////////////
 // Methods implementing nsIPrincipal //
--- a/chrome/src/Makefile.in
+++ b/chrome/src/Makefile.in
@@ -41,16 +41,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE          = chrome
 LIBRARY_NAME    = chrome_s
 LIBXUL_LIBRARY  = 1
 FORCE_STATIC_LIB = 1
+FORCE_USE_PIC = 1
 
 EXPORTS_NAMESPACES = mozilla/chrome
 
 EXPORTS_mozilla/chrome = \
 		RegistryMessageUtils.h \
 		$(NULL)
 
 CPPSRCS		= \
--- a/chrome/test/unit/test_resolve_uris.js
+++ b/chrome/test/unit/test_resolve_uris.js
@@ -46,20 +46,22 @@ let manifests = [
 ];
 registerManifests(manifests);
 
 function do_run_test()
 {
   let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
            getService(Ci.nsIChromeRegistry);
 
-  var runtime = Components.classes["@mozilla.org/xre/app-info;1"]
-                .getService(Components.interfaces.nsIXULRuntime);
-  if (runtime.processType ==
-      Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+  // If we don't have libxul or e10s then we don't have process separation, so
+  // we don't need to worry about checking for new chrome.
+  var appInfo = Cc["@mozilla.org/xre/app-info;1"];
+  if (!appInfo ||
+      (appInfo.getService(Ci.nsIXULRuntime).processType ==
+       Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)) {
     cr.checkForNewChrome();
   }
 
   // See if our various things were able to register
   let registrationTypes = [
       "content",
       "locale",
       "skin",
--- a/config/nsStaticComponents.cpp.in
+++ b/config/nsStaticComponents.cpp.in
@@ -1,8 +1,9 @@
+#line 2 "nsStaticComponents.cpp.in"
 /* -*- 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/
@@ -35,42 +36,38 @@
  * 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 ***** */
 
 #define XPCOM_TRANSLATE_NSGM_ENTRY_POINT 1
 
-#include "nsIGenericFactory.h"
+#include "mozilla/ModuleUtils.h"
 #include "nsXPCOM.h"
 #include "nsMemory.h"
 #include "nsStaticComponents.h"
 
 /**
- * Construct a unique NSGetModule entry point for a generic module.
- */
-#define NSGETMODULE(_name) _name##_NSGetModule
-
-/**
  * Declare an NSGetModule() routine for a generic module.
  */
 #define MODULE(_name) \
-NSGETMODULE_ENTRY_POINT(_name) (nsIComponentManager*, nsIFile*, nsIModule**);
+    NSMODULE_DECL(_name);
 
 %MODULE_LIST%
 #line 57 "nsStaticComponents.cpp.in"
 
 #undef MODULE
 
 
-#define MODULE(_name) { #_name, NSGETMODULE(_name) },
+#define MODULE(_name) \
+    NSMODULE_NAME(_name),
 
 /**
  * The nsStaticModuleInfo
  */
-static nsStaticModuleInfo const gStaticModuleInfo[] = {
+static const mozilla::Module *const kStaticModules[] = {
 	%MODULE_LIST%
-#line 69 "nsStaticComponents.cpp.in"
+#line 70 "nsStaticComponents.cpp.in"
+        NULL
 };
 
-nsStaticModuleInfo const *const kPStaticModules = gStaticModuleInfo;
-PRUint32 const kStaticModuleCount = NS_ARRAY_LENGTH(gStaticModuleInfo);
+mozilla::Module const *const *const kPStaticModules = kStaticModules;
--- a/config/outofdate.pl
+++ b/config/outofdate.pl
@@ -42,17 +42,17 @@
 #  otherwise assumes .class files in same directory as .java files)
 #Returns: list of input arguments which are newer than corresponding class
 #files (nonexistent class files are considered to be real old :-)
 #
 
 $found = 1;
 
 # GLOBALS
-$SEP = 0; # the paltform independent path separator
+$SEP = 0; # the platform independent path separator
 $CFG = 0; # the value of the -cfg flag
 
 # determine the path separator
 $_ = $ENV{"PATH"};
 if (m|/|) {
 	$SEP = "/";
 }
 else {
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -163,16 +163,17 @@ testxpcsrcdir = $(topsrcdir)/testing/xpc
 
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
+          $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
           $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE), but don't automatically
 # start the test. Instead, present the xpcshell prompt so the user can
 # attach a debugger and then start the test.
 check-interactive:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
@@ -188,16 +189,18 @@ check-interactive:
 # Execute a single test, specified in $(SOLO_FILE)
 check-one:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           --test-path=$(SOLO_FILE) \
           --profile-name=$(MOZ_APP_NAME) \
+          --verbose \
+          $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
           $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
 
 endif # XPCSHELL_TESTS
 
 ifdef CPP_UNIT_TESTS
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
@@ -2286,8 +2289,11 @@ FREEZE_VARIABLES = \
 
 $(foreach var,$(FREEZE_VARIABLES),$(eval $(var)_FROZEN := '$($(var))'))
 
 CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \
   $(if $(subst $($(var)_FROZEN),,'$($(var))'),$(error Makefile variable '$(var)' changed value after including rules.mk. Was $($(var)_FROZEN), now $($(var)).)))
 
 libs export libs::
 	$(CHECK_FROZEN_VARIABLES)
+
+default::
+	if test -d $(DIST)/bin ; then touch $(DIST)/bin/.purgecaches ; fi
--- a/configure.in
+++ b/configure.in
@@ -1533,17 +1533,17 @@ hppa* | parisc)
 sun4u | sparc*)
     CPU_ARCH=sparc
     ;;
 
 x86_64 | ia64)
     CPU_ARCH="$OS_TEST"
     ;;
 
-arm)
+arm*)
     CPU_ARCH=arm
     ;;
 esac
 
 if test -z "$OS_TARGET"; then
     OS_TARGET=$OS_ARCH
 fi
 OS_CONFIG="${OS_TARGET}${OS_RELEASE}"
@@ -1562,18 +1562,18 @@ if test "$GNU_CC"; then
         DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
     fi
     WARNINGS_AS_ERRORS='-Werror'
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-fPIC'
     ASFLAGS="$ASFLAGS -fPIC"
     _MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
     _MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
-    _MOZ_EXCEPTIONS_FLAGS_ON='-fhandle-exceptions'
-    _MOZ_EXCEPTIONS_FLAGS_OFF='-fno-handle-exceptions'
+    _MOZ_EXCEPTIONS_FLAGS_ON='-fexceptions'
+    _MOZ_EXCEPTIONS_FLAGS_OFF='-fno-exceptions'
 
     # Turn on GNU specific features
     # -Wall - turn on all warnings
     # -pedantic - make compiler warn about non-ANSI stuff, and
     #             be a little bit stricter
     # Warnings slamm took out for now (these were giving more noise than help):
     # -Wbad-function-cast - warns when casting a function to a new return type
     # -Wshadow - removed because it generates more noise than help --pete
@@ -2534,18 +2534,18 @@ ia64*-hpux*)
 	AC_DEFINE(_i386)
 	OS_TARGET=NTO
 	WARNINGS_AS_ERRORS=''
 	MOZ_OPTIMIZE_FLAGS="-O"
 	MOZ_DEBUG_FLAGS="-gstabs"
 	USE_PTHREADS=1
 	_PEDANTIC=
 	LIBS="$LIBS -lsocket -lstdc++"
-	_DEFINES_CFLAGS='-Wp,-include -Wp,$(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506'
-	_DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -Wp,-include -Wp,$(DEPTH)/mozilla-config.h -D_POSIX_C_SOURCE=199506'
+	_DEFINES_CFLAGS='-include $(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506'
+	_DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/mozilla-config.h -D_POSIX_C_SOURCE=199506'
 	if test "$with_x" != "yes"
 	then
 		_PLATFORM_DEFAULT_TOOLKIT="photon"
 	    TK_CFLAGS='-I/usr/include/photon'
 		TK_LIBS='-lph'
 	fi
 	case "${target_cpu}" in
 	ppc*)
@@ -4020,42 +4020,16 @@ EOF
 	    cd ${_curdir}
 	    rm -rf conftest* _conftest
 	    dnl ===================================================================
 	    ;;
 esac
 
 dnl ===================================================================
 dnl ========================================================
-dnl By default, turn rtti and exceptions off on g++/egcs
-dnl ========================================================
-if test "$GNU_CXX"; then
-
-  AC_MSG_CHECKING(for C++ exceptions flag)
-
-  dnl They changed -f[no-]handle-exceptions to -f[no-]exceptions in g++ 2.8
-  AC_CACHE_VAL(ac_cv_cxx_exceptions_flags,
-  [echo "int main() { return 0; }" | cat > conftest.C
-
-  ${CXX-g++} ${CXXFLAGS} -c -fno-handle-exceptions conftest.C > conftest.out 2>&1
-
-  if egrep "warning.*renamed" conftest.out >/dev/null; then
-    ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-exceptions
-  else
-    ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-handle-exceptions
-  fi
-
-  rm -f conftest*])
-
-  AC_MSG_RESULT($ac_cv_cxx_exceptions_flags)
-  _MOZ_EXCEPTIONS_FLAGS_OFF=$ac_cv_cxx_exceptions_flags
-  _MOZ_EXCEPTIONS_FLAGS_ON=`echo $ac_cv_cxx_exceptions_flags | sed 's|no-||'`
-fi
-
-dnl ========================================================
 dnl Put your C++ language/feature checks below
 dnl ========================================================
 AC_LANG_CPLUSPLUS
 
 ARM_ABI_PREFIX=
 HAVE_GCC3_ABI=
 if test "$GNU_CC"; then
   if test "$CPU_ARCH" = "arm" ; then
@@ -6000,17 +5974,17 @@ dnl = Check alsa availability on Linux i
 dnl ========================================================
 
 dnl If using sydneyaudio with Linux, ensure that the alsa library is available
 if test -n "$MOZ_SYDNEYAUDIO"; then
    case "$target_os" in
 linux*)
       PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
          [echo "$MOZ_ALSA_PKG_ERRORS"
-          AC_MSG_ERROR([Need alsa for Ogg or Wave decoding on Linux.  Disable with --disable-ogg --disable-wave.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+          AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
       ;;
    esac
 fi
 
 dnl ========================================================
 dnl Splashscreen
 dnl ========================================================
 AC_ARG_ENABLE(splashscreen,
@@ -7972,17 +7946,17 @@ MOZ_ARG_DISABLE_BOOL(md,
    if test "$SOLARIS_SUNPRO_CC"; then
      _cpp_md_flag=1
    fi])
 if test "$_cpp_md_flag"; then
   COMPILER_DEPEND=1
   if test "$OS_ARCH" = "OpenVMS"; then
     _DEPEND_CFLAGS='$(subst =, ,$(filter-out %/.pp,-MM=-MD=-MF=$(MDDEPDIR)/$(basename $(@F)).pp))'
   else
-    _DEPEND_CFLAGS='$(filter-out %/.pp,-Wp,-MD,$(MDDEPDIR)/$(basename $(@F)).pp)'
+    _DEPEND_CFLAGS='$(filter-out %/.pp,-MD -MF $(MDDEPDIR)/$(basename $(@F)).pp)'
   fi
   dnl Sun Studio on Solaris use -xM instead of -MD, see config/rules.mk
   if test "$SOLARIS_SUNPRO_CC"; then
     _DEPEND_CFLAGS=
   fi
 else
   COMPILER_DEPEND=
   dnl Don't override this for MSVC
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -125,16 +125,17 @@ class nsIXTFService;
 #endif
 #ifdef IBMBIDI
 class nsIBidiKeyboard;
 #endif
 class nsIMIMEHeaderParam;
 class nsIObserver;
 class nsPresContext;
 class nsIChannel;
+struct nsIntMargin;
 
 #ifndef have_PrefChangedFunc_typedef
 typedef int (*PR_CALLBACK PrefChangedFunc)(const char *, void *);
 #define have_PrefChangedFunc_typedef
 #endif
 
 namespace mozilla {
   class IHistory;
--- a/content/base/public/nsCopySupport.h
+++ b/content/base/public/nsCopySupport.h
@@ -34,25 +34,25 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsCopySupport_h__
 #define nsCopySupport_h__
 
 #include "nscore.h"
+#include "nsINode.h"
 
 class nsISelection;
 class nsIDocument;
 class nsIImageLoadingContent;
 class nsIContent;
 class nsITransferable;
 class nsACString;
 class nsAString;
-class nsIDOMNode;
 class nsIPresShell;
 
 class nsCopySupport
 {
   // class of static helper functions for copy support
   public:
     static nsresult HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID);
     static nsresult DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
@@ -64,20 +64,24 @@ class nsCopySupport
     // doc.
     static nsresult GetContents(const nsACString& aMimeType, PRUint32 aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata);
     
     static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
                               PRInt32 aCopyFlags);
 
     // Get the selection as a transferable. Similar to HTMLCopy except does
     // not deal with the clipboard.
-    static nsresult GetTransferableForSelection(nsISelection * aSelection,
-                                                nsIDocument * aDocument,
-                                                nsITransferable ** aTransferable);
+    static nsresult GetTransferableForSelection(nsISelection* aSelection,
+                                                nsIDocument* aDocument,
+                                                nsITransferable** aTransferable);
 
+    // Same as GetTransferableForSelection, but doesn't skip invisible content.
+    static nsresult GetTransferableForNode(nsINode* aNode,
+                                           nsIDocument* aDoc,
+                                           nsITransferable** aTransferable);
     /**
      * Retrieve the selection for the given document. If the current focus
      * within the document has its own selection, aSelection will be set to it
      * and this focused content node returned. Otherwise, aSelection will be
      * set to the document's selection and null will be returned.
      */
     static nsIContent* GetSelectionForCopy(nsIDocument* aDocument,
                                            nsISelection** aSelection);
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -50,25 +50,25 @@
 #include "nsIChannelPolicy.h"
 #include "nsIChannelEventSink.h"
 #include "nsIPropertyBag2.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsNetError.h"
 #include "nsChannelProperties.h"
 
 /* Keeps track of whether or not CSP is enabled */
-static PRBool gCSPEnabled = PR_TRUE;
+PRBool CSPService::sCSPEnabled = PR_TRUE;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 CSPService::CSPService()
 {
-  nsContentUtils::AddBoolPrefVarCache("security.csp.enable", &gCSPEnabled);
+  nsContentUtils::AddBoolPrefVarCache("security.csp.enable", &sCSPEnabled);
 
 #ifdef PR_LOGGING
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
 #endif
 }
 
 CSPService::~CSPService()
@@ -97,17 +97,17 @@ CSPService::ShouldLoad(PRUint32 aContent
         PR_LOG(gCspPRLog, PR_LOG_DEBUG,
             ("CSPService::ShouldLoad called for %s", location.get()));
     }
 #endif
     // default decision, CSP can revise it if there's a policy to enforce
     *aDecision = nsIContentPolicy::ACCEPT;
 
     // No need to continue processing if CSP is disabled
-    if (!gCSPEnabled)
+    if (!sCSPEnabled)
         return NS_OK;
 
     // find the principal of the document that initiated this request and see
     // if it has a CSP policy object
     nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
     nsCOMPtr<nsIPrincipal> principal;
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     if (node) {
@@ -155,17 +155,17 @@ CSPService::ShouldProcess(PRUint32      
 {
     if (!aContentLocation)
         return NS_ERROR_FAILURE;
 
     // default decision is to accept the item
     *aDecision = nsIContentPolicy::ACCEPT;
 
     // No need to continue processing if CSP is disabled
-    if (!gCSPEnabled)
+    if (!sCSPEnabled)
         return NS_OK;
 
     // find the nsDocument that initiated this request and see if it has a
     // CSP policy object
     nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
     nsCOMPtr<nsIPrincipal> principal;
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     if (node) {
--- a/content/base/src/nsCSPService.h
+++ b/content/base/src/nsCSPService.h
@@ -47,12 +47,13 @@
     { 0x97, 0xd9, 0x3f, 0x7d, 0xca, 0x2c, 0xb4, 0x60 } }
 class CSPService : public nsIContentPolicy,
                    public nsIChannelEventSink
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTPOLICY
   NS_DECL_NSICHANNELEVENTSINK
-  
+
   CSPService();
   virtual ~CSPService();
+  static PRBool sCSPEnabled;
 };
--- a/content/base/src/nsChannelPolicy.cpp
+++ b/content/base/src/nsChannelPolicy.cpp
@@ -32,16 +32,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 ***** */
 
 #include "nsChannelPolicy.h"
 
 nsChannelPolicy::nsChannelPolicy()
+  : mLoadType(0)
 {
 }
 
 nsChannelPolicy::~nsChannelPolicy()
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsChannelPolicy, nsIChannelPolicy)
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -74,64 +74,51 @@
 #include "nsIContent.h"
 #include "nsIImageLoadingContent.h"
 #include "nsUnicharUtils.h"
 #include "nsIURL.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsIDocShellTreeItem.h"
-#include "nsRange.h"
 #include "nsIWebBrowserPersist.h"
 #include "nsEscape.h"
 #include "nsContentUtils.h"
 #include "nsIMIMEService.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsDOMDataTransfer.h"
 
 // private clipboard data flavors for html copy, used by editor when pasting
 #define kHTMLContext   "text/_moz_htmlcontext"
 #define kHTMLInfo      "text/_moz_htmlinfo"
 
-nsresult NS_NewDomSelection(nsISelection **aDomSelection);
-
-// if inNode is null, use the selection from the window
+// if aNode is null, use the selection from the window
 static nsresult
 GetTransferableForNodeOrSelection(nsIDOMWindow*     aWindow,
                                   nsIContent*       aNode,
                                   nsITransferable** aTransferable)
 {
   NS_ENSURE_ARG_POINTER(aWindow);
 
   nsCOMPtr<nsIDOMDocument> domDoc;
   aWindow->GetDocument(getter_AddRefs(domDoc));
   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
 
   nsresult rv;
-  nsCOMPtr<nsISelection> selection;
-  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
-  if (node) {
-    // Make a temporary selection with this node in a single range.
-    rv = NS_NewDomSelection(getter_AddRefs(selection));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIDOMRange> range;
-    rv = NS_NewRange(getter_AddRefs(range));
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = range->SelectNode(node);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = selection->AddRange(range);
-    NS_ENSURE_SUCCESS(rv, rv);
+  if (aNode) {
+    rv = nsCopySupport::GetTransferableForNode(aNode, doc, aTransferable);
   } else {
+    nsCOMPtr<nsISelection> selection;
     aWindow->GetSelection(getter_AddRefs(selection));
+    rv = nsCopySupport::GetTransferableForSelection(selection, doc,
+                                                    aTransferable);
   }
 
-  rv = nsCopySupport::GetTransferableForSelection(selection, doc,
-                                                  aTransferable);
   NS_ENSURE_SUCCESS(rv, rv);
   return rv;
 }
 
 class NS_STACK_CLASS DragDataProducer
 {
 public:
   DragDataProducer(nsIDOMWindow* aWindow,
@@ -419,17 +406,16 @@ DragDataProducer::GetNodeString(nsIConte
     docRange->CreateRange(getter_AddRefs(range));
     if (range) {
       range->SelectNode(node);
       range->ToString(outNodeString);
     }
   }
 }
 
-
 nsresult
 DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
                           PRBool* aCanDrag,
                           PRBool* aDragSelection,
                           nsIContent** aDragNode)
 {
   NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode,
                   "null pointer passed to Produce");
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2410,17 +2410,18 @@ nsContentUtils::LoadImage(nsIURI* aURI, 
                           imgIDecoderObserver* aObserver, PRInt32 aLoadFlags,
                           imgIRequest** aRequest)
 {
   NS_PRECONDITION(aURI, "Must have a URI");
   NS_PRECONDITION(aLoadingDocument, "Must have a document");
   NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
   NS_PRECONDITION(aRequest, "Null out param");
 
-  if (!sImgLoader) {
+  imgILoader* imgLoader = GetImgLoader();
+  if (!imgLoader) {
     // nothing we can do here
     return NS_OK;
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
   NS_ASSERTION(loadGroup, "Could not get loadgroup; onload may fire too early");
 
   nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
@@ -2439,27 +2440,27 @@ nsContentUtils::LoadImage(nsIURI* aURI, 
     }
   }
     
   // Make the URI immutable so people won't change it under us
   NS_TryToSetImmutable(aURI);
 
   // XXXbz using "documentURI" for the initialDocumentURI is not quite
   // right, but the best we can do here...
-  return sImgLoader->LoadImage(aURI,                 /* uri to load */
-                               documentURI,          /* initialDocumentURI */
-                               aReferrer,            /* referrer */
-                               loadGroup,            /* loadgroup */
-                               aObserver,            /* imgIDecoderObserver */
-                               aLoadingDocument,     /* uniquification key */
-                               aLoadFlags,           /* load flags */
-                               nsnull,               /* cache key */
-                               nsnull,               /* existing request*/
-                               channelPolicy,        /* CSP info */
-                               aRequest);
+  return imgLoader->LoadImage(aURI,                 /* uri to load */
+                              documentURI,          /* initialDocumentURI */
+                              aReferrer,            /* referrer */
+                              loadGroup,            /* loadgroup */
+                              aObserver,            /* imgIDecoderObserver */
+                              aLoadingDocument,     /* uniquification key */
+                              aLoadFlags,           /* load flags */
+                              nsnull,               /* cache key */
+                              nsnull,               /* existing request*/
+                              channelPolicy,        /* CSP info */
+                              aRequest);
 }
 
 // static
 already_AddRefed<imgIContainer>
 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
                                     imgIRequest **aRequest)
 {
   if (aRequest) {
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -45,16 +45,17 @@
 #include "nsIComponentManager.h" 
 #include "nsIServiceManager.h"
 #include "nsIClipboard.h"
 #include "nsISelection.h"
 #include "nsWidgetsCID.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIDOMRange.h"
+#include "nsRange.h"
 #include "imgIContainer.h"
 #include "nsIPresShell.h"
 #include "nsFocusManager.h"
 #include "nsEventDispatcher.h"
 
 #include "nsIDocShell.h"
 #include "nsIContentViewerEdit.h"
 #include "nsIClipboardDragDropHooks.h"
@@ -73,16 +74,18 @@
 #include "nsIFrame.h"
 
 // image copy stuff
 #include "nsIImageLoadingContent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentCID.h"
 
+nsresult NS_NewDomSelection(nsISelection **aDomSelection);
+
 static NS_DEFINE_CID(kCClipboardCID,           NS_CLIPBOARD_CID);
 static NS_DEFINE_CID(kCTransferableCID,        NS_TRANSFERABLE_CID);
 static NS_DEFINE_CID(kHTMLConverterCID,        NS_HTMLFORMATCONVERTER_CID);
 
 // private clipboard data flavors for html copy, used by editor when pasting
 #define kHTMLContext   "text/_moz_htmlcontext"
 #define kHTMLInfo      "text/_moz_htmlinfo"
 
@@ -95,17 +98,17 @@ static nsresult AppendString(nsITransfer
 static nsresult AppendDOMNode(nsITransferable *aTransferable,
                               nsIDOMNode *aDOMNode);
 
 // Helper used for HTMLCopy and GetTransferableForSelection since both routines
 // share common code.
 static nsresult
 SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc,
                     PRBool doPutOnClipboard, PRInt16 aClipboardID,
-                    nsITransferable ** aTransferable)
+                    PRUint32 aFlags, nsITransferable ** aTransferable)
 {
   // Clear the output parameter for the transferable, if provided.
   if (aTransferable) {
     *aTransferable = nsnull;
   }
 
   nsresult rv = NS_OK;
   
@@ -130,19 +133,18 @@ SelectionCopyHelper(nsISelection *aSel, 
   // is. if it is a selection into input/textarea element or in a html content
   // with pre-wrap style : text/plain. Otherwise text/html.
   // see nsHTMLCopyEncoder::SetSelection
   mimeType.AssignLiteral(kUnicodeMime);
   
   // we want preformatted for the case where the selection is inside input/textarea
   // and we don't want pretty printing for others cases, to not have additionnal
   // line breaks which are then converted into spaces by the htmlConverter (see bug #524975)
-  PRUint32 flags = nsIDocumentEncoder::OutputPreformatted
-                   | nsIDocumentEncoder::OutputRaw
-                   | nsIDocumentEncoder::SkipInvisibleContent;
+  PRUint32 flags = aFlags | nsIDocumentEncoder::OutputPreformatted
+                          | nsIDocumentEncoder::OutputRaw;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   if (NS_FAILED(rv)) 
     return rv;
 
@@ -173,17 +175,17 @@ SelectionCopyHelper(nsISelection *aSel, 
     PRUint32 ConvertedLen;
     rv = htmlConverter->Convert(kHTMLMime, plainHTML, textBuffer.Length() * 2, kUnicodeMime, getter_AddRefs(ConvertedData), &ConvertedLen);
     NS_ENSURE_SUCCESS(rv, rv);
 
     ConvertedData->GetData(plaintextBuffer);
 
     mimeType.AssignLiteral(kHTMLMime);
 
-    flags = nsIDocumentEncoder::SkipInvisibleContent;
+    flags = aFlags;
 
     rv = docEncoder->Init(domDoc, mimeType, flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = docEncoder->SetSelection(aSel);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // encode the selection as html with contextual info
@@ -274,27 +276,57 @@ SelectionCopyHelper(nsISelection *aSel, 
       if (aTransferable != nsnull) {
         trans.swap(*aTransferable);
       }
     }
   }
   return rv;
 }
 
-nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID)
+nsresult
+nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc,
+                        PRInt16 aClipboardID)
 {
-  return SelectionCopyHelper(aSel, aDoc, PR_TRUE, aClipboardID, nsnull);
+  return SelectionCopyHelper(aSel, aDoc, PR_TRUE, aClipboardID,
+                             nsIDocumentEncoder::SkipInvisibleContent,
+                             nsnull);
+}
+
+nsresult
+nsCopySupport::GetTransferableForSelection(nsISelection* aSel,
+                                           nsIDocument* aDoc,
+                                           nsITransferable** aTransferable)
+{
+  return SelectionCopyHelper(aSel, aDoc, PR_FALSE, 0,
+                             nsIDocumentEncoder::SkipInvisibleContent,
+                             aTransferable);
 }
 
 nsresult
-nsCopySupport::GetTransferableForSelection(nsISelection * aSel,
-                                           nsIDocument * aDoc,
-                                           nsITransferable ** aTransferable)
+nsCopySupport::GetTransferableForNode(nsINode* aNode,
+                                      nsIDocument* aDoc,
+                                      nsITransferable** aTransferable)
 {
-  return SelectionCopyHelper(aSel, aDoc, PR_FALSE, 0, aTransferable);
+  nsCOMPtr<nsISelection> selection;
+  // Make a temporary selection with aNode in a single range.
+  nsresult rv = NS_NewDomSelection(getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDOMRange> range;
+  rv = NS_NewRange(getter_AddRefs(range));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
+  rv = range->SelectNode(node);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = selection->AddRange(range);
+  NS_ENSURE_SUCCESS(rv, rv);
+  // It's not the primary selection - so don't skip invisible content.
+  PRUint32 flags = 0;
+  return SelectionCopyHelper(selection, aDoc, PR_FALSE, 0, flags,
+                             aTransferable);
 }
 
 nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
                                 PRBool *aDoPutOnClipboard)
 {
   NS_ENSURE_ARG(aDoc);
 
   *aDoPutOnClipboard = PR_TRUE;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -190,26 +190,24 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #ifdef MOZ_SMIL
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 #endif // MOZ_SMIL
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
+#include "nsCSPService.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 
 #include "mozilla/dom/Link.h"
 using namespace mozilla::dom;
 
 
-/* Keeps track of whether or not CSP is enabled */
-static PRBool gCSPEnabled = PR_TRUE;
-
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 #define NAME_NOT_VALID ((nsBaseContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
@@ -1404,18 +1402,16 @@ nsDocument::nsDocument(const char* aCont
   if (gDocumentLeakPRLog)
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p created", this));
 
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
 #endif
 
-  nsContentUtils::AddBoolPrefVarCache("security.csp.enable", &gCSPEnabled);
-
   // Start out mLastStyleSheetSet as null, per spec
   SetDOMStringToNull(mLastStyleSheetSet);
 }
 
 static PLDHashOperator
 ClearAllBoxObjects(const void* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
 {
   if (aBoxObject) {
@@ -2170,17 +2166,17 @@ nsDocument::StartDocumentLoad(const char
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 nsDocument::InitCSP()
 {
-  if (gCSPEnabled) {
+  if (CSPService::sCSPEnabled) {
     nsAutoString cspHeaderValue;
     nsAutoString cspROHeaderValue;
 
     this->GetHeaderData(nsGkAtoms::headerCSP, cspHeaderValue);
     this->GetHeaderData(nsGkAtoms::headerCSPReportOnly, cspROHeaderValue);
 
     PRBool system = PR_FALSE;
     nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -56,17 +56,17 @@ bool SendSyncMessageToParent(void* aCall
                              nsTArray<nsString>* aJSONRetVal)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIContent> owner = tabChild->mOwner;
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   asyncMessages.SwapElements(tabChild->mASyncMessages);
   PRUint32 len = asyncMessages.Length();
-  for (PRInt32 i = 0; i < len; ++i) {
+  for (PRUint32 i = 0; i < len; ++i) {
     nsCOMPtr<nsIRunnable> async = asyncMessages[i];
     async->Run();
   }
   if (tabChild->mChromeMessageManager) {
     tabChild->mChromeMessageManager->ReceiveMessage(owner, aMessage, PR_TRUE,
                                                     aJSON, nsnull, aJSONRetVal);
   }
   return true;
@@ -281,18 +281,19 @@ nsInProcessTabChildGlobal::InitTabChildG
                          nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
 
   nsISupports* scopeSupports =
     NS_ISUPPORTS_CAST(nsPIDOMEventTarget*, this);
   JS_SetContextPrivate(cx, scopeSupports);
 
   nsresult rv =
     xpc->InitClassesWithNewWrappedGlobal(cx, scopeSupports,
-                                         NS_GET_IID(nsISupports), flags,
-                                         getter_AddRefs(mGlobal));
+                                         NS_GET_IID(nsISupports),
+                                         GetPrincipal(), EmptyCString(),
+                                         flags, getter_AddRefs(mGlobal));
   NS_ENSURE_SUCCESS(rv, false);
 
   JSObject* global = nsnull;
   rv = mGlobal->GetJSObject(&global);
   NS_ENSURE_SUCCESS(rv, false);
 
   JS_SetGlobalObject(cx, global);
 
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1355,18 +1355,17 @@ nsXMLHttpRequest::GetAllResponseHeaders(
 
   if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
 
   if (httpChannel) {
-    nsHeaderVisitor *visitor = nsnull;
-    NS_NEWXPCOM(visitor, nsHeaderVisitor);
+    nsHeaderVisitor *visitor = new nsHeaderVisitor();
     if (!visitor)
       return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(visitor);
 
     nsresult rv = httpChannel->VisitResponseHeaders(visitor);
     if (NS_SUCCEEDED(rv))
       *_retval = ToNewCString(visitor->Headers());
 
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -397,16 +397,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug300992.html \
 		test_websocket_hello.html \
 		file_websocket_hello_wsh.py \
 		test_ws_basic_tests.html \
 		file_ws_basic_tests_wsh.py \
 		test_websocket.html \
 		file_websocket_wsh.py \
 		file_websocket_http_resource.txt \
+		test_bug574596.html \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES2 += 	test_copyimage.html \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug574596.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=574596
+-->
+<head>
+  <title>Test for Bug 574596</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/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=574596">Mozilla Bug 574596</a>
+<style type="text/css">
+#link1 a { -moz-user-select:none; }
+</style>
+<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>
+<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 574596 **/
+
+function ignoreFunc(actualData, expectedData) {
+  return true;
+}
+
+var dragLinkText = [[
+  { type:"text/x-moz-url",          data:"", eqTest:ignoreFunc },
+  { type:"text/x-moz-url-data",     data:"http://www.mozilla.org/" },
+  { type:"text/x-moz-url-desc",     data:"link1" },
+  { type:"text/uri-list",           data:"http://www.mozilla.org/" },
+  { type:"text/_moz_htmlcontext",   data:"", eqTest:ignoreFunc },
+  { type:"text/_moz_htmlinfo",      data:"", eqTest:ignoreFunc },
+  { type:"text/html",               data:'<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>' },
+  { type:"text/plain",              data:"http://www.mozilla.org/" }
+]];
+
+
+function dumpTransfer(dataTransfer,expect) {
+  dtData = dataTransfer.mozItemCount + "items:\n";
+  for (var i = 0; i < dataTransfer.mozItemCount; i++) {
+    var dtTypes = dataTransfer.mozTypesAt(i);
+    for (var j = 0; j < dtTypes.length; j++) {
+      var actualData = dataTransfer.mozGetDataAt(dtTypes[j],i)
+      if (expect && expect[i] && expect[i][j]) {
+        if (expect[i][j].eqTest)
+          dtData += expect[i][j].eqTest(actualData,expect[i][j].data) ? "ok" : "fail";
+        else
+          dtData += (actualData == expect[i][j].data) ? "ok" : "fail";
+      }
+      dtData += "["+i+"]" + "["+j+"]: " + '"' + dtTypes[j] + '"  "' + actualData + '"\n';
+    }
+  }
+  alert(dtData);
+}
+
+function runTest() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  var result = synthesizeDragStart($('link1'), dragLinkText, window);
+  is(result, null, "Drag -moz-user-select:none link (#link1)");
+  // if (result) dumpTransfer(result,dragLinkText);
+
+  dragLinkText[0][2].data = "link2";
+  dragLinkText[0][6].data = '<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>'
+  var result = synthesizeDragStart($('link2'), dragLinkText, window);
+  is(result, null, "Drag link (#link2)");
+  // if (result) dumpTransfer(result,dragLinkText);
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTest);
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/base/test/test_fileapi.html
+++ b/content/base/test/test_fileapi.html
@@ -257,17 +257,17 @@ expectedTestCount++;
 // Test reading from nonexistent files
 r = new FileReader();
 var didThrow = false;
 try {
   r.readAsDataURL(nonExistingFile);
 } catch(ex) {
   didThrow = true;
 }
-// Once this test passes, we shoud test that onerror gets called and
+// Once this test passes, we should test that onerror gets called and
 // that the FileReader object is in the right state during that call.
 todo(!didThrow, "shouldn't throw when opening nonexistent file, should fire error instead");
 
 
 function getLoadHandler(expectedResult, expectedLength, testName) {
   return function (event) {
     is(event.target.readyState, FileReader.DONE,
        "readyState in test " + testName);
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -58,17 +58,31 @@ function shouldNotOpen(e)
 {
   var ws = e.target;
   ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!");
 }
 
 function shouldNotReceiveCloseEvent(e)
 {
   var ws = e.target;
-  ok(false, "onclose shouldn't be called on test " + ws._testNumber + "!");
+  var extendedErrorInfo = "";
+  if (!ws._testNumber) {
+    extendedErrorInfo += "\nws members:\n";
+    for (var i in ws) {
+      extendedErrorInfo += (i + ": " + ws[i] + "\n");
+    }
+
+    extendedErrorInfo += "\ne members:\n";
+    for (var i in e) {
+      extendedErrorInfo += (i + ": " + e[i] + "\n");
+    }
+  }
+  
+  // FIXME: see bug 578276. This should be a test failure, but it's too flaky on the tbox.
+  ok(true, "onclose shouldn't be called on test " + ws._testNumber + "!" + extendedErrorInfo);
 }
 
 function shouldCloseCleanly(e)
 {
   var ws = e.target;
   ok(e.wasClean, "the ws connection in test " + ws._testNumber + " should be closed cleanly");
 }
 
@@ -84,22 +98,25 @@ function CreateTestWS(ws_location, ws_pr
 
   try {
     if (ws_protocol == undefined) {
       ws = new WebSocket(ws_location);
     } else {
       ws = new WebSocket(ws_location, ws_protocol);
     }
 
+
+    ws._testNumber = current_test;
+    ws._receivedCloseEvent = false;
+    ok(true, "added testNumber: " + ws._testNumber +"\n");
+
     ws.onerror = function(e)
     {
       ok(false, "onerror called on test " + e.target._testNumber + "!");
     };
-    ws._testNumber = current_test;
-    ws._receivedCloseEvent = false;
     ws.addEventListener("close", function(e)
     {
       ws._receivedCloseEvent = true;
     }, false);
   }
   catch (e) {
     throw e;
   }
@@ -414,16 +431,17 @@ function test16()
   ws.onclose = shouldCloseCleanly;
 }
 
 var status_test17 = "not started";
 
 window._test17 = function()
 {
   var local_ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 17");
+  local_ws._testNumber = "local17";
   current_test++;
 
   status_test17 = "started";
 
   local_ws.onopen = function(e)
   {
     status_test17 = "opened";
     e.target.send("client data");
@@ -486,16 +504,17 @@ function test19()
     shouldCloseNotCleanly(e);
     doTest(20);
   };
 }
 
 window._test20 = function()
 {
   var local_ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 20");
+  local_ws._testNumber = "local20";
   current_test++;
 
   local_ws.onerror = function()
   {
     ok(false, "onerror called on test " + e.target._testNumber + "!");
   };
 
   local_ws.onclose = shouldNotReceiveCloseEvent;
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -133,18 +133,18 @@ WebGLContext::SetCanvasElement(nsHTMLCan
 }
 
 NS_IMETHODIMP
 WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
 {
     // If incrementing the generation would cause overflow,
     // don't allow it.  Allowing this would allow us to use
     // resource handles created from older context generations.
-    if (mGeneration + 1 == 0)
-        return NS_ERROR_FAILURE;
+    if (!(mGeneration+1).valid())
+        return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
 
     if (mWidth == width && mHeight == height)
         return NS_OK;
 
     if (gl) {
         // hey we already have something
         if (gl->Resize(gfxIntSize(width, height))) {
 
@@ -176,17 +176,17 @@ WebGLContext::SetDimensions(PRInt32 widt
             LogMessage("WebGL: Using software rendering via OSMesa");
         }
     }
 
     mWidth = width;
     mHeight = height;
 
     // increment the generation number
-    mGeneration++;
+    ++mGeneration;
 
     MakeContextCurrent();
 
     // Make sure that we clear this out, otherwise
     // we'll end up displaying random memory
     gl->fViewport(0, 0, mWidth, mHeight);
     gl->fClearColor(0, 0, 0, 0);
     gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -55,16 +55,18 @@
 #include "nsHTMLCanvasElement.h"
 #include "nsWeakReference.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIJSNativeInitializer.h"
 
 #include "GLContext.h"
 #include "Layers.h"
 
+#include "CheckedInt.h"
+
 #define UNPACK_FLIP_Y_WEBGL            0x9240
 #define UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241
 #define CONTEXT_LOST_WEBGL             0x9242
 
 class nsIDocShell;
 
 namespace mozilla {
 
@@ -302,27 +304,27 @@ public:
         return ErrorInvalidEnum("%s: invalid enum value", info);
     }
 
     already_AddRefed<CanvasLayer> GetCanvasLayer(LayerManager *manager);
     void MarkContextClean() { }
 
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
-    PRUint32 Generation() { return mGeneration; }
+    PRUint32 Generation() { return mGeneration.value(); }
 protected:
     nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
     nsHTMLCanvasElement *HTMLCanvasElement() {
         return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get());
     }
 
     nsRefPtr<gl::GLContext> gl;
 
     PRInt32 mWidth, mHeight;
-    PRUint32 mGeneration;
+    CheckedUint32 mGeneration;
 
     PRBool mInvalidated;
 
     WebGLuint mActiveTexture;
     WebGLenum mSynthesizedGLError;
 
     PRBool SafeToCreateCanvas3DContext(nsHTMLCanvasElement *canvasElement);
     PRBool InitAndValidateGL();
@@ -330,16 +332,17 @@ protected:
     PRBool ValidateCapabilityEnum(WebGLenum cap, const char *info);
     PRBool ValidateBlendEquationEnum(WebGLuint cap, const char *info);
     PRBool ValidateBlendFuncDstEnum(WebGLuint mode, const char *info);
     PRBool ValidateBlendFuncSrcEnum(WebGLuint mode, const char *info);
     PRBool ValidateTextureTargetEnum(WebGLenum target, const char *info);
     PRBool ValidateComparisonEnum(WebGLenum target, const char *info);
     PRBool ValidateStencilOpEnum(WebGLenum action, const char *info);
     PRBool ValidateFaceEnum(WebGLenum target, const char *info);
+    PRBool ValidateBufferUsageEnum(WebGLenum target, const char *info);
     PRBool ValidateTexFormatAndType(WebGLenum format, WebGLenum type,
                                       PRUint32 *texelSize, const char *info);
 
     void Invalidate();
 
     void MakeContextCurrent() { gl->MakeCurrent(); }
 
     // helpers
@@ -700,17 +703,17 @@ public:
         ZeroOwners();
         mDeleted = PR_TRUE;
     }
 
     PRBool Deleted() { return mDeleted; }
     WebGLuint GLName() { return mName; }
     const nsTArray<WebGLShader*>& AttachedShaders() const { return mAttachedShaders; }
     PRBool LinkStatus() { return mLinkStatus; }
-    GLuint Generation() const { return mGeneration; }
+    PRUint32 Generation() const { return mGeneration.value(); }
     void SetLinkStatus(PRBool val) { mLinkStatus = val; }
 
     PRBool ContainsShader(WebGLShader *shader) {
         return mAttachedShaders.Contains(shader);
     }
 
     // return true if the shader wasn't already attached
     PRBool AttachShader(WebGLShader *shader) {
@@ -737,20 +740,19 @@ public:
                 return PR_TRUE;
         }
 
         return PR_FALSE;
     }
 
     PRBool NextGeneration()
     {
-        GLuint nextGeneration = mGeneration + 1;
-        if (nextGeneration == 0)
+        if (!(mGeneration+1).valid())
             return PR_FALSE; // must exit without changing mGeneration
-        mGeneration = nextGeneration;
+        ++mGeneration;
         mMapUniformLocations.Clear();
         return PR_TRUE;
     }
 
     already_AddRefed<WebGLUniformLocation> GetUniformLocationObject(GLint glLocation);
 
     /* Called only after LinkProgram */
     PRBool UpdateInfo(gl::GLContext *gl);
@@ -765,17 +767,17 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLPROGRAM
 protected:
     WebGLuint mName;
     PRPackedBool mDeleted;
     PRPackedBool mLinkStatus;
     nsTArray<WebGLShader*> mAttachedShaders;
     nsRefPtrHashtable<nsUint32HashKey, WebGLUniformLocation> mMapUniformLocations;
-    GLuint mGeneration;
+    CheckedUint32 mGeneration;
     GLint mUniformMaxNameLength;
     GLint mAttribMaxNameLength;
     GLint mUniformCount;
     GLint mAttribCount;
     std::vector<bool> mAttribsInUse;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WebGLProgram, WEBGLPROGRAM_PRIVATE_IID)
@@ -859,26 +861,26 @@ public:
     NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLUNIFORMLOCATION_PRIVATE_IID)
 
     WebGLUniformLocation(WebGLContext *context, WebGLProgram *program, GLint location) :
         WebGLContextBoundObject(context), mProgram(program), mProgramGeneration(program->Generation()),
         mLocation(location) { }
 
     WebGLProgram *Program() const { return mProgram; }
     GLint Location() const { return mLocation; }
-    GLuint ProgramGeneration() const { return mProgramGeneration; }
+    PRUint32 ProgramGeneration() const { return mProgramGeneration; }
 
     // needed for our generic helpers to check nsIxxx parameters, see GetConcreteObject.
     PRBool Deleted() { return PR_FALSE; }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLUNIFORMLOCATION
 protected:
     WebGLObjectRefPtr<WebGLProgram> mProgram;
-    GLuint mProgramGeneration;
+    PRUint32 mProgramGeneration;
     GLint mLocation;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WebGLUniformLocation, WEBGLUNIFORMLOCATION_PRIVATE_IID)
 
 /**
  ** Template implementations
  **/
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -120,31 +120,16 @@ already_AddRefed<WebGLUniformLocation> W
 
 /* void present (); */
 NS_IMETHODIMP
 WebGLContext::Present()
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* long sizeInBytes (in GLenum type); */
-NS_IMETHODIMP
-WebGLContext::SizeInBytes(WebGLenum type, PRInt32 *retval)
-{
-    if (type == LOCAL_GL_FLOAT) *retval = sizeof(float);
-    if (type == LOCAL_GL_SHORT) *retval = sizeof(short);
-    if (type == LOCAL_GL_UNSIGNED_SHORT) *retval = sizeof(unsigned short);
-    if (type == LOCAL_GL_BYTE) *retval = 1;
-    if (type == LOCAL_GL_UNSIGNED_BYTE) *retval = 1;
-    if (type == LOCAL_GL_INT) *retval = sizeof(int);
-    if (type == LOCAL_GL_UNSIGNED_INT) *retval = sizeof(unsigned int);
-    if (type == LOCAL_GL_DOUBLE) *retval = sizeof(double);
-    return NS_OK;
-}
-
 /* void GlActiveTexture (in GLenum texture); */
 NS_IMETHODIMP
 WebGLContext::ActiveTexture(WebGLenum texture)
 {
     if (texture < LOCAL_GL_TEXTURE0 || texture >= LOCAL_GL_TEXTURE0+mBound2DTextures.Length())
         return ErrorInvalidEnum("ActiveTexture: texture unit %d out of range (0..%d)",
                                 texture, mBound2DTextures.Length()-1);
 
@@ -287,17 +272,17 @@ WebGLContext::BindTexture(WebGLenum targ
 
     MakeContextCurrent();
 
     gl->fBindTexture(target, texturename);
 
     return NS_OK;
 }
 
-GL_SAME_METHOD_4(BlendColor, BlendColor, float, float, float, float)
+GL_SAME_METHOD_4(BlendColor, BlendColor, WebGLfloat, WebGLfloat, WebGLfloat, WebGLfloat)
 
 NS_IMETHODIMP WebGLContext::BlendEquation(WebGLenum mode)
 {
     if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
         return NS_OK;
 
     MakeContextCurrent();
     gl->fBlendEquation(mode);
@@ -357,16 +342,22 @@ WebGLContext::BufferData_size(WebGLenum 
     if (target == LOCAL_GL_ARRAY_BUFFER) {
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
         boundBuffer = mBoundElementArrayBuffer;
     } else {
         return ErrorInvalidEnum("BufferData: invalid target");
     }
 
+    if (size < 0)
+        return ErrorInvalidValue("bufferData: negative size");
+
+    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
+        return NS_OK;
+
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
     MakeContextCurrent();
 
     boundBuffer->SetByteLength(size);
     boundBuffer->ZeroDataIfElementArray();
 
@@ -383,16 +374,19 @@ WebGLContext::BufferData_buf(WebGLenum t
     if (target == LOCAL_GL_ARRAY_BUFFER) {
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
         boundBuffer = mBoundElementArrayBuffer;
     } else {
         return ErrorInvalidEnum("BufferData: invalid target");
     }
 
+    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
+        return NS_OK;
+
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
     MakeContextCurrent();
 
     boundBuffer->SetByteLength(wb->byteLength);
     boundBuffer->CopyDataIfElementArray(wb->data);
 
@@ -409,16 +403,19 @@ WebGLContext::BufferData_array(WebGLenum
     if (target == LOCAL_GL_ARRAY_BUFFER) {
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
         boundBuffer = mBoundElementArrayBuffer;
     } else {
         return ErrorInvalidEnum("BufferData: invalid target");
     }
 
+    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
+        return NS_OK;
+
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
     MakeContextCurrent();
 
     boundBuffer->SetByteLength(wa->byteLength);
     boundBuffer->CopyDataIfElementArray(wa->data);
 
@@ -444,18 +441,21 @@ WebGLContext::BufferSubData_buf(GLenum t
         boundBuffer = mBoundElementArrayBuffer;
     } else {
         return ErrorInvalidEnum("BufferSubData: invalid target");
     }
 
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
-    // XXX check for overflow
-    if (byteOffset + wb->byteLength > boundBuffer->ByteLength())
+    CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + wb->byteLength;
+    if (!checked_neededByteLength.valid())
+        return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
+
+    if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("BufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
                                      byteOffset, wb->byteLength, boundBuffer->ByteLength());
 
     MakeContextCurrent();
 
     boundBuffer->CopySubDataIfElementArray(byteOffset, wb->byteLength, wb->data);
 
     gl->fBufferSubData(target, byteOffset, wb->byteLength, wb->data);
@@ -474,18 +474,21 @@ WebGLContext::BufferSubData_array(WebGLe
         boundBuffer = mBoundElementArrayBuffer;
     } else {
         return ErrorInvalidEnum("BufferSubData: invalid target");
     }
 
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
-    // XXX check for overflow
-    if (byteOffset + wa->byteLength > boundBuffer->ByteLength())
+    CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + wa->byteLength;
+    if (!checked_neededByteLength.valid())
+        return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
+
+    if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("BufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
                                      byteOffset, wa->byteLength, boundBuffer->ByteLength());
 
     MakeContextCurrent();
 
     boundBuffer->CopySubDataIfElementArray(byteOffset, wa->byteLength, wa->data);
 
     gl->fBufferSubData(target, byteOffset, wa->byteLength, wa->data);
@@ -507,25 +510,25 @@ WebGLContext::Clear(PRUint32 mask)
 {
     MakeContextCurrent();
     gl->fClear(mask);
     Invalidate();
 
     return NS_OK;
 }
 
-GL_SAME_METHOD_4(ClearColor, ClearColor, float, float, float, float)
+GL_SAME_METHOD_4(ClearColor, ClearColor, WebGLfloat, WebGLfloat, WebGLfloat, WebGLfloat)
 
 #ifdef USE_GLES2
-GL_SAME_METHOD_1(ClearDepthf, ClearDepth, float)
+GL_SAME_METHOD_1(ClearDepthf, ClearDepth, WebGLfloat)
 #else
-GL_SAME_METHOD_1(ClearDepth, ClearDepth, float)
+GL_SAME_METHOD_1(ClearDepth, ClearDepth, WebGLfloat)
 #endif
 
-GL_SAME_METHOD_1(ClearStencil, ClearStencil, PRInt32)
+GL_SAME_METHOD_1(ClearStencil, ClearStencil, WebGLint)
 
 GL_SAME_METHOD_4(ColorMask, ColorMask, WebGLboolean, WebGLboolean, WebGLboolean, WebGLboolean)
 
 NS_IMETHODIMP
 WebGLContext::CopyTexImage2D(WebGLenum target,
                              WebGLint level,
                              WebGLenum internalformat,
                              WebGLint x,
@@ -635,17 +638,26 @@ WebGLContext::CreateShader(WebGLenum typ
 
     WebGLShader *shader = new WebGLShader(this, name, type);
     NS_ADDREF(*retval = shader);
     mMapShaders.Put(name, shader);
 
     return NS_OK;
 }
 
-GL_SAME_METHOD_1(CullFace, CullFace, WebGLenum)
+NS_IMETHODIMP
+WebGLContext::CullFace(WebGLenum face)
+{
+    if (!ValidateFaceEnum(face, "cullFace"))
+        return NS_OK;
+
+    MakeContextCurrent();
+    gl->fCullFace(face);
+    return NS_OK;
+}
 
 NS_IMETHODIMP
 WebGLContext::DeleteBuffer(nsIWebGLBuffer *bobj)
 {
     WebGLuint bufname;
     WebGLBuffer *buf;
     PRBool isNull, isDeleted;
     if (!GetConcreteObjectAndGLName(bobj, &buf, &bufname, &isNull, &isDeleted))
@@ -811,19 +823,19 @@ WebGLContext::DepthFunc(WebGLenum func)
     MakeContextCurrent();
     gl->fDepthFunc(func);
     return NS_OK;
 }
 
 GL_SAME_METHOD_1(DepthMask, DepthMask, WebGLboolean)
 
 #ifdef USE_GLES2
-GL_SAME_METHOD_2(DepthRangef, DepthRange, float, float)
+GL_SAME_METHOD_2(DepthRangef, DepthRange, WebGLfloat, WebGLfloat)
 #else
-GL_SAME_METHOD_2(DepthRange, DepthRange, float, float)
+GL_SAME_METHOD_2(DepthRange, DepthRange, WebGLfloat, WebGLfloat)
 #endif
 
 NS_IMETHODIMP
 WebGLContext::DisableVertexAttribArray(WebGLuint index)
 {
     if (index > mAttribBuffers.Length())
         return ErrorInvalidValue("DisableVertexAttribArray: index out of range");
 
@@ -858,20 +870,22 @@ WebGLContext::DrawArrays(GLenum mode, We
     if (count == 0)
         return NS_OK;
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return NS_OK;
 
-    if (first+count < first || first+count < count)
-        return ErrorInvalidOperation("DrawArrays: overflow in first+count");
-
-    if (!ValidateBuffers(first+count))
+    CheckedInt32 checked_firstPlusCount = CheckedInt32(first) + count;
+
+    if (!checked_firstPlusCount.valid())
+        return ErrorInvalidOperation("drawArrays: overflow in first+count");
+
+    if (!ValidateBuffers(checked_firstPlusCount.value()))
         return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient data for given first and count");
 
     MakeContextCurrent();
 
     gl->fDrawArrays(mode, first, count);
 
     Invalidate();
 
@@ -892,57 +906,65 @@ WebGLContext::DrawElements(WebGLenum mod
             break;
         default:
             return ErrorInvalidEnum("DrawElements: invalid mode");
     }
 
     if (count < 0 || byteOffset < 0)
         return ErrorInvalidValue("DrawElements: negative count or offset");
 
-    WebGLuint byteCount;
+    CheckedUint32 checked_byteCount;
+
     if (type == LOCAL_GL_UNSIGNED_SHORT) {
-        byteCount = WebGLuint(count) << 1;
-        if (byteCount >> 1 != WebGLuint(count))
-            return ErrorInvalidValue("DrawElements: overflow in byteCount");
-
+        checked_byteCount = 2 * CheckedUint32(count);
         if (byteOffset % 2 != 0)
             return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
     } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
-        byteCount = count;
+        checked_byteCount = count;
     } else {
         return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
     }
 
+    if (!checked_byteCount.valid())
+        return ErrorInvalidValue("DrawElements: overflow in byteCount");
+
     // If count is 0, there's nothing to do.
     if (count == 0)
         return NS_OK;
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return NS_OK;
 
     if (!mBoundElementArrayBuffer)
         return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
 
-    if (byteOffset+byteCount < WebGLuint(byteOffset) || byteOffset+byteCount < byteCount)
+    CheckedUint32 checked_neededByteCount = checked_byteCount + byteOffset;
+
+    if (!checked_neededByteCount.valid())
         return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
 
-    if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
+    if (checked_neededByteCount.value() > mBoundElementArrayBuffer->ByteLength())
         return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
 
     WebGLuint maxIndex = 0;
     if (type == LOCAL_GL_UNSIGNED_SHORT) {
         maxIndex = mBoundElementArrayBuffer->FindMaximum<GLushort>(count, byteOffset);
     } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
         maxIndex = mBoundElementArrayBuffer->FindMaximum<GLubyte>(count, byteOffset);
     }
 
-    // maxIndex+1 because ValidateBuffers expects the number of elements needed
-    if (!ValidateBuffers(maxIndex+1)) {
+    // maxIndex+1 because ValidateBuffers expects the number of elements needed.
+    // it is very important here to check tha maxIndex+1 doesn't overflow, otherwise the buffer validation is bypassed !!!
+    // maxIndex is a WebGLuint, ValidateBuffers takes a PRUint32, we validate maxIndex+1 as a PRUint32.
+    CheckedUint32 checked_neededCount = CheckedUint32(maxIndex) + 1;
+    if (!checked_neededCount.valid())
+        return ErrorInvalidOperation("drawElements: overflow in maxIndex+1");
+    if (!ValidateBuffers(checked_neededCount.value())) {
         return ErrorInvalidOperation("DrawElements: bound vertex attribute buffers do not have sufficient "
                                      "data for given indices from the bound element array");
     }
 
     MakeContextCurrent();
 
     gl->fDrawElements(mode, count, type, (GLvoid*) (byteOffset));
 
@@ -1301,24 +1323,17 @@ WebGLContext::GetParameter(PRUint32 pnam
         {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             wrval->SetAsInt32(i);
         }
             break;
 
         #define LOCAL_GL_MAX_VARYING_VECTORS 0x8dfc // not present in desktop OpenGL
-        // temporarily add those defs here, as they're missing from
-        //     gfx/thebes/public/GLDefs.h
-        // and from
-        //     gfx/layers/opengl/glDefs.h
-        // and I don't know in which of these 2 files they should go (probably we're going to
-        // kill one of them soon?)
-        #define LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS  0x9125
-        #define LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS   0x9122
+
         case LOCAL_GL_MAX_VARYING_VECTORS:
         {
             #ifdef USE_GLES2
                 GLint i = 0;
                 gl->fGetIntegerv(pname, &i);
                 wrval->SetAsInt32(i);
             #else
                 // since this pname is absent from desktop OpenGL, we have to implement it by hand.
@@ -2112,17 +2127,17 @@ WebGLContext::IsEnabled(WebGLenum cap, W
         return NS_OK;
     }
 
     MakeContextCurrent();
     *retval = gl->fIsEnabled(cap);
     return NS_OK;
 }
 
-GL_SAME_METHOD_1(LineWidth, LineWidth, float)
+GL_SAME_METHOD_1(LineWidth, LineWidth, WebGLfloat)
 
 NS_IMETHODIMP
 WebGLContext::LinkProgram(nsIWebGLProgram *pobj)
 {
     GLuint progname;
     WebGLProgram *program;
     if (!GetConcreteObjectAndGLName(pobj, &program, &progname))
         return ErrorInvalidOperation("LinkProgram: invalid program");
@@ -2174,17 +2189,17 @@ WebGLContext::PixelStorei(WebGLenum pnam
         default:
             return ErrorInvalidEnum("PixelStorei: invalid parameter");
     }
 
     return NS_OK;
 }
 
 
-GL_SAME_METHOD_2(PolygonOffset, PolygonOffset, float, float)
+GL_SAME_METHOD_2(PolygonOffset, PolygonOffset, WebGLfloat, WebGLfloat)
 
 NS_IMETHODIMP
 WebGLContext::ReadPixels(PRInt32 dummy)
 {
     return NS_ERROR_FAILURE;
 }
 
 nsresult
@@ -2227,26 +2242,29 @@ WebGLContext::ReadPixels_base(WebGLint x
         return ErrorInvalidEnum("ReadPixels: unsupported pixel type");
     }
 
     MakeContextCurrent();
 
     PRUint32 packAlignment;
     gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*) &packAlignment);
 
-    PRUint32 plainRowSize = width*size;
-
-    // alignedRowSize = row size rounded up to next multiple of
-    // packAlignment which is a power of 2
-    PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
-        ~PRUint32(packAlignment-1);
-
-    PRUint32 neededByteLength = (height-1)*alignedRowSize + plainRowSize;
-
-    if(neededByteLength > byteLength)
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
+
+    // alignedRowSize = row size rounded up to next multiple of packAlignment
+    CheckedUint32 checked_alignedRowSize
+        = ((checked_plainRowSize + packAlignment-1) / packAlignment) * packAlignment;
+
+    CheckedUint32 checked_neededByteLength
+        = (height-1) * checked_alignedRowSize + checked_plainRowSize;
+
+    if (!checked_neededByteLength.valid())
+        return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
+
+    if (checked_neededByteLength.value() > byteLength)
         return ErrorInvalidOperation("ReadPixels: buffer too small");
 
     if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight)) {
         // the easy case: we're not reading out-of-range pixels
         gl->fReadPixels(x, y, width, height, format, type, data);
     } else {
         // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
         // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
@@ -2272,30 +2290,39 @@ WebGLContext::ReadPixels_base(WebGLint x
         GLint   subrect_x      = PR_MAX(x, 0);
         GLint   subrect_end_x  = PR_MIN(x+width, boundWidth);
         GLsizei subrect_width  = subrect_end_x - subrect_x;
 
         GLint   subrect_y      = PR_MAX(y, 0);
         GLint   subrect_end_y  = PR_MIN(y+height, boundHeight);
         GLsizei subrect_height = subrect_end_y - subrect_y;
 
+        if (subrect_width < 0 || subrect_height < 0 ||
+            subrect_width > width || subrect_height)
+            return ErrorInvalidOperation("ReadPixels: integer overflow computing clipped rect size");
+
+        // now we know that subrect_width is in the [0..width] interval, and same for heights.
+
         // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
+        // no need to check again for integer overflow here, since we already know the sizes aren't greater than before
         PRUint32 subrect_plainRowSize = subrect_width * size;
         PRUint32 subrect_alignedRowSize = (subrect_plainRowSize + packAlignment-1) &
             ~PRUint32(packAlignment-1);
         PRUint32 subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
 
         // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
         GLubyte *subrect_data = new GLubyte[subrect_byteLength];
         gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data);
+
+        // notice that this for loop terminates because we already checked that subrect_height is at most height
         for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
             GLint subrect_x_in_dest_buffer = subrect_x - x;
             GLint subrect_y_in_dest_buffer = subrect_y - y;
             memcpy(static_cast<GLubyte*>(data)
-                     + alignedRowSize * (subrect_y_in_dest_buffer + y_inside_subrect)
+                     + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
                      + size * subrect_x_in_dest_buffer, // destination
                    subrect_data + subrect_alignedRowSize * y_inside_subrect, // source
                    subrect_plainRowSize); // size
         }
         delete [] subrect_data;
     }
     return NS_OK;
 }
@@ -2348,24 +2375,29 @@ WebGLContext::ReadPixels_byteLength_old_
       case LOCAL_GL_UNSIGNED_BYTE:
         break;
       default:
         return ErrorInvalidEnum("ReadPixels: unsupported pixel type");
     }
     PRUint32 packAlignment;
     gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*) &packAlignment);
 
-    PRUint32 plainRowSize = width*size;
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
 
     // alignedRowSize = row size rounded up to next multiple of
     // packAlignment which is a power of 2
-    PRUint32 alignedRowSize = (plainRowSize + packAlignment-1) &
-        ~PRUint32(packAlignment-1);
-
-    *retval = (height-1)*alignedRowSize + plainRowSize;
+    CheckedUint32 checked_alignedRowSize
+        = ((checked_plainRowSize + packAlignment-1) / packAlignment) * packAlignment;
+
+    CheckedUint32 checked_neededByteLength = (height-1)*checked_alignedRowSize + checked_plainRowSize;
+
+    if (!checked_neededByteLength.valid())
+        return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
+
+    *retval = checked_neededByteLength.value();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::RenderbufferStorage(WebGLenum target, WebGLenum internalformat, WebGLsizei width, WebGLsizei height)
 {
     if (target != LOCAL_GL_RENDERBUFFER)
@@ -2390,17 +2422,17 @@ WebGLContext::RenderbufferStorage(WebGLe
         mBoundRenderbuffer->setDimensions(width, height);
 
     MakeContextCurrent();
     gl->fRenderbufferStorage(target, internalformat, width, height);
 
     return NS_OK;
 }
 
-GL_SAME_METHOD_2(SampleCoverage, SampleCoverage, float, WebGLboolean)
+GL_SAME_METHOD_2(SampleCoverage, SampleCoverage, WebGLfloat, WebGLboolean)
 
 NS_IMETHODIMP
 WebGLContext::Scissor(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height)
 {
     if (width < 0 || height < 0)
         return ErrorInvalidValue("Scissor: negative size");
 
     MakeContextCurrent();
@@ -2938,19 +2970,16 @@ WebGLContext::VertexAttribPointer(WebGLu
     }
 
     if (index >= mAttribBuffers.Length())
         return ErrorInvalidValue("VertexAttribPointer: index out of range - %d >= %d", index, mAttribBuffers.Length());
 
     if (size < 1 || size > 4)
         return ErrorInvalidValue("VertexAttribPointer: invalid element size");
 
-    if (stride < 0)
-        return ErrorInvalidValue("VertexAttribPointer: stride cannot be negative");
-
     /* XXX make work with bufferSubData & heterogeneous types 
     if (type != mBoundArrayBuffer->GLType())
         return ErrorInvalidOperation("VertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
     */
 
     // XXX 0 stride?
     //if (stride < (GLuint) size)
     //    return ErrorInvalidOperation("VertexAttribPointer: stride must be >= size!");
@@ -3016,21 +3045,25 @@ WebGLContext::TexImage2D_base(WebGLenum 
 
     if (border != 0)
         return ErrorInvalidValue("TexImage2D: border must be 0");
 
     PRUint32 texelSize = 0;
     if (!ValidateTexFormatAndType(format, type, &texelSize, "texImage2D"))
         return NS_OK;
 
-    // XXX overflow!
-    uint32 bytesNeeded = width * height * texelSize;
-
+    CheckedUint32 checked_bytesNeeded = CheckedUint32(width) * height * texelSize;
+
+    if (!checked_bytesNeeded.valid())
+        return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
+
+    PRUint32 bytesNeeded = checked_bytesNeeded.value();
+    
     if (byteLength && byteLength < bytesNeeded)
-        return ErrorInvalidValue("TexImage2D: not enough data for operation (need %d, have %d)",
+        return ErrorInvalidOperation("TexImage2D: not enough data for operation (need %d, have %d)",
                                  bytesNeeded, byteLength);
 
     MakeContextCurrent();
 
     if (byteLength) {
         gl->fTexImage2D(target, level, internalformat, width, height, border, format, type, data);
     } else {
         // We need some zero pages, because GL doesn't guarantee the
@@ -3149,18 +3182,23 @@ WebGLContext::TexSubImage2D_base(WebGLen
 
     PRUint32 texelSize = 0;
     if (!ValidateTexFormatAndType(format, type, &texelSize, "texSubImage2D"))
         return NS_OK;
 
     if (width == 0 || height == 0)
         return NS_OK; // ES 2.0 says it has no effect, we better return right now
 
-    // XXX overflow!
-    uint32 bytesNeeded = width * height * texelSize;
+    CheckedUint32 checked_bytesNeeded = CheckedUint32(width) * height * texelSize;
+
+    if (!checked_bytesNeeded.valid())
+        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
+
+    PRUint32 bytesNeeded = checked_bytesNeeded.value();
+ 
     if (byteLength < bytesNeeded)
         return ErrorInvalidValue("TexSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
     MakeContextCurrent();
 
     gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
 
     return NS_OK;
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -34,16 +34,18 @@
  * 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 "WebGLContext.h"
 
+#include "CheckedInt.h"
+
 using namespace mozilla;
 
 /*
  * Pull all the data out of the program that will be used by validate later on
  */
 PRBool
 WebGLProgram::UpdateInfo(gl::GLContext *gl)
 {
@@ -107,22 +109,28 @@ WebGLContext::ValidateBuffers(PRUint32 c
         }
 
         // If the attrib is not in use, then we don't have to validate
         // it, just need to make sure that the binding is non-null.
         if (!mCurrentProgram->IsAttribInUse(i))
             continue;
 
         // compute the number of bytes we actually need
-        WebGLuint needed = vd.byteOffset +     // the base offset
-            vd.actualStride() * (count-1) +    // to stride to the start of the last element group
-            vd.componentSize() * vd.size;      // and the number of bytes needed for these components
+        CheckedUint32 checked_needed = CheckedUint32(vd.byteOffset) + // the base offset
+            CheckedUint32(vd.actualStride()) * (count-1) + // to stride to the start of the last element group
+            CheckedUint32(vd.componentSize()) * vd.size;   // and the number of bytes needed for these components
 
-        if (vd.buf->ByteLength() < needed) {
-            LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d", i, needed, vd.buf->ByteLength());
+        if (!checked_needed.valid()) {
+            LogMessage("Integer overflow computing the size of bound vertex attrib buffer at index %d", i);
+            return PR_FALSE;
+        }
+
+        if (vd.buf->ByteLength() < checked_needed.value()) {
+            LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d",
+                       i, checked_needed.value(), vd.buf->ByteLength());
             return PR_FALSE;
         }
     }
 
     return PR_TRUE;
 }
 
 PRBool WebGLContext::ValidateCapabilityEnum(WebGLenum cap, const char *info)
@@ -245,16 +253,29 @@ PRBool WebGLContext::ValidateFaceEnum(We
         case LOCAL_GL_FRONT_AND_BACK:
             return PR_TRUE;
         default:
             ErrorInvalidEnumInfo(info);
             return PR_FALSE;
     }
 }
 
+PRBool WebGLContext::ValidateBufferUsageEnum(WebGLenum target, const char *info)
+{
+    switch (target) {
+        case LOCAL_GL_STREAM_DRAW:
+        case LOCAL_GL_STATIC_DRAW:
+        case LOCAL_GL_DYNAMIC_DRAW:
+            return PR_TRUE;
+        default:
+            ErrorInvalidEnumInfo(info);
+            return PR_FALSE;
+    }
+}
+
 PRBool WebGLContext::ValidateTexFormatAndType(WebGLenum format, WebGLenum type,
                                                 PRUint32 *texelSize, const char *info)
 {
     if (type == LOCAL_GL_UNSIGNED_BYTE)
     {
         switch (format) {
             case LOCAL_GL_RED:
             case LOCAL_GL_GREEN:
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -3106,17 +3106,17 @@ nsCanvasRenderingContext2D::SetLineCap(c
     if (capstyle.EqualsLiteral("butt"))
         cap = gfxContext::LINE_CAP_BUTT;
     else if (capstyle.EqualsLiteral("round"))
         cap = gfxContext::LINE_CAP_ROUND;
     else if (capstyle.EqualsLiteral("square"))
         cap = gfxContext::LINE_CAP_SQUARE;
     else
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-        return NS_ERROR_NOT_IMPLEMENTED;
+        return NS_OK;
 
     mThebes->SetLineCap(cap);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
 {
@@ -3142,17 +3142,17 @@ nsCanvasRenderingContext2D::SetLineJoin(
     if (joinstyle.EqualsLiteral("round"))
         j = gfxContext::LINE_JOIN_ROUND;
     else if (joinstyle.EqualsLiteral("bevel"))
         j = gfxContext::LINE_JOIN_BEVEL;
     else if (joinstyle.EqualsLiteral("miter"))
         j = gfxContext::LINE_JOIN_MITER;
     else
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-        return NS_ERROR_NOT_IMPLEMENTED;
+        return NS_OK;
 
     mThebes->SetLineJoin(j);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle)
 {
@@ -3527,17 +3527,18 @@ nsCanvasRenderingContext2D::SetGlobalCom
     else CANVAS_OP_TO_THEBES_OP("lighter", ADD)
     else CANVAS_OP_TO_THEBES_OP("source-atop", ATOP)
     else CANVAS_OP_TO_THEBES_OP("source-in", IN)
     else CANVAS_OP_TO_THEBES_OP("source-out", OUT)
     else CANVAS_OP_TO_THEBES_OP("source-over", OVER)
     else CANVAS_OP_TO_THEBES_OP("xor", XOR)
     // not part of spec, kept here for compat
     else CANVAS_OP_TO_THEBES_OP("over", OVER)
-    else return NS_ERROR_NOT_IMPLEMENTED;
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    else return NS_OK;
 
 #undef CANVAS_OP_TO_THEBES_OP
 
     mThebes->SetOperator(thebes_op);
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -1458,17 +1458,17 @@ try {
 
 ctx.globalCompositeOperation = 'xor';
 ctx.globalCompositeOperation = 'Source-over';
 ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.composite.operation.clear.html ]]] -->
 
 <p>Canvas test: 2d.composite.operation.clear</p>
@@ -1564,17 +1564,17 @@ try {
 
 ctx.globalCompositeOperation = 'xor';
 ctx.globalCompositeOperation = 'highlight';
 ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.composite.operation.nullsuffix.html ]]] -->
 
 <p>Canvas test: 2d.composite.operation.nullsuffix - bug 401788</p>
@@ -1591,17 +1591,17 @@ try {
 
 ctx.globalCompositeOperation = 'xor';
 ctx.globalCompositeOperation = 'source-over\0';
 ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.composite.operation.over.html ]]] -->
 
 <p>Canvas test: 2d.composite.operation.over</p>
@@ -1637,17 +1637,17 @@ try {
 
 ctx.globalCompositeOperation = 'xor';
 ctx.globalCompositeOperation = 'nonexistent';
 ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.composite.solid.copy.html ]]] -->
 
 <p>Canvas test: 2d.composite.solid.copy</p>
@@ -9126,17 +9126,17 @@ ok(ctx.lineCap === 'butt', "ctx.lineCap 
 
 ctx.lineCap = 'butt';
 ctx.lineCap = 'bevel';
 ok(ctx.lineCap === 'butt', "ctx.lineCap === 'butt'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.line.cap.open.html ]]] -->
 
 <p>Canvas test: 2d.line.cap.open</p>
@@ -9500,17 +9500,17 @@ ok(ctx.lineJoin === 'bevel', "ctx.lineJo
 
 ctx.lineJoin = 'bevel';
 ctx.lineJoin = 'butt';
 ok(ctx.lineJoin === 'bevel', "ctx.lineJoin === 'bevel'");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.line.join.miter.html ]]] -->
 
 <p>Canvas test: 2d.line.join.miter</p>
@@ -19311,17 +19311,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext('2D') === null, "canvas.getContext('2D') === null");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_context.emptystring.html ]]] -->
 
 <p>Canvas test: context.emptystring - bug 401788</p>
@@ -19337,17 +19337,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext("") === null, "canvas.getContext(\"\") === null");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_context.unrecognised.badname.html ]]] -->
 
 <p>Canvas test: context.unrecognised.badname - bug 401788</p>
@@ -19363,17 +19363,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext('This is not an implemented context in any real browser') === null, "canvas.getContext('This is not an implemented context in any real browser') === null");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_context.unrecognised.badsuffix.html ]]] -->
 
 <p>Canvas test: context.unrecognised.badsuffix - bug 401788</p>
@@ -19389,17 +19389,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext("2d#") === null, "canvas.getContext(\"2d#\") === null");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_context.unrecognised.nullsuffix.html ]]] -->
 
 <p>Canvas test: context.unrecognised.nullsuffix - bug 401788</p>
@@ -19415,17 +19415,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext("2d\0") === null, "canvas.getContext(\"2d\\0\") === null");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_context.unrecognised.unicode.html ]]] -->
 
 <p>Canvas test: context.unrecognised.unicode - bug 401788</p>
@@ -19441,17 +19441,17 @@ var ctx = canvas.getContext('2d');
 var _thrown_outer = false;
 try {
 
 ok(canvas.getContext("2\uFF44") === null, "canvas.getContext(\"2\\uFF44\") === null"); // Fullwidth Latin Small Letter D
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_fallback.basic.html ]]] -->
 
 <p>Canvas test: fallback.basic</p>
@@ -20602,17 +20602,17 @@ var _thrown_outer = false;
 try {
 
 var data = canvas.toDataURL('ImAgE/PnG');
 ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_toDataURL.nocontext.html ]]] -->
 
 <p>Canvas test: toDataURL.nocontext</p>
@@ -20710,17 +20710,17 @@ var _thrown_outer = false;
 try {
 
 var data = canvas.toDataURL('image/example');
 ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_toDataURL.zerosize.html ]]] -->
 
 <p>Canvas test: toDataURL.zerosize</p>
--- a/content/canvas/test/webgl/conformance/program-test.html
+++ b/content/canvas/test/webgl/conformance/program-test.html
@@ -135,21 +135,21 @@ function go() {
         if(gl.getError() != gl.NO_ERROR)
             assertMsg(false, "unexpected error in detachShader()");
         assertMsg(areArraysEqual(gl.getAttachedShaders(prog), expected_shaders), errmsg);
     }
     checkGetAttachedShaders([], [], [], "getAttachedShaders should return an empty list by default");
     checkGetAttachedShaders([fs], [], [fs], "attaching a single shader should give the expected list");
     checkGetAttachedShaders([fs, vs, fs2, vs2], [], [fs, vs, fs2, vs2],
         "attaching some shaders should give the expected list");
-    checkGetAttachedShaders([fs], [fs], [], "attaching a shader and detaching it shoud leave an empty list");
+    checkGetAttachedShaders([fs], [fs], [], "attaching a shader and detaching it should leave an empty list");
     checkGetAttachedShaders([fs, vs, fs2, vs2], [fs, vs, fs2, vs2], [],
-        "attaching some shaders and detaching them in same order shoud leave an empty list");
+        "attaching some shaders and detaching them in same order should leave an empty list");
     checkGetAttachedShaders([fs, vs, fs2, vs2], [fs, vs2, vs, fs2], [],
-        "attaching some shaders and detaching them in random order shoud leave an empty list");
+        "attaching some shaders and detaching them in random order should leave an empty list");
     checkGetAttachedShaders([fs, vs, fs2, vs2], [vs], [fs, fs2, vs2],
         "attaching and detaching some shaders should leave the difference list");
     checkGetAttachedShaders([fs, vs, fs2, vs2], [fs, vs2], [vs, fs2],
         "attaching and detaching some shaders should leave the difference list");
     checkGetAttachedShaders([fsBad], [], [fsBad],
         "attaching a shader that failed to compile should still show it in the list");
     checkGetAttachedShaders([fs, vsBad, fs2], [], [fs, vsBad, fs2],
         "attaching shaders, including one that failed to compile, should still show the it in the list");
--- a/content/events/src/nsEventListenerService.cpp
+++ b/content/events/src/nsEventListenerService.cpp
@@ -134,20 +134,23 @@ nsEventListenerInfo::ToSource(nsAString&
 
   if (GetJSVal(&v)) {
     nsCOMPtr<nsIThreadJSContextStack> stack =
       nsContentUtils::ThreadJSContextStack();
     if (stack) {
       JSContext* cx = nsnull;
       stack->GetSafeJSContext(&cx);
       if (cx && NS_SUCCEEDED(stack->Push(cx))) {
-        JSAutoRequest ar(cx);
-        JSString* str = JS_ValueToSource(cx, v);
-        if (str) {
-          aResult.Assign(nsDependentJSString(str));
+        {
+          // Extra block to finish the auto request before calling pop
+          JSAutoRequest ar(cx);
+          JSString* str = JS_ValueToSource(cx, v);
+          if (str) {
+            aResult.Assign(nsDependentJSString(str));
+          }
         }
         stack->Pop(&cx);
       }
     }
   }
 
   return NS_OK;
 }
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -1576,19 +1576,19 @@ nsGenericHTMLElement::ParseTableCellHAli
 
 PRBool
 nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
                                             nsAttrValue& aResult)
 {
   return aResult.ParseEnumValue(aString, kTableVAlignTable, PR_FALSE);
 }
 
-PRBool 
+PRBool
 nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
-                                         nsAttrValue& aResult) const
+                                         nsAttrValue& aResult)
 {
   return aResult.ParseEnumValue(aString, kDivAlignTable, PR_FALSE);
 }
 
 PRBool
 nsGenericHTMLElement::ParseImageAttribute(nsIAtom* aAttribute,
                                           const nsAString& aString,
                                           nsAttrValue& aResult)
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -248,18 +248,18 @@ public:
 
   /**
    * Parse a div align string to value (left/right/center/middle/justify)
    *
    * @param aString the string to parse
    * @param aResult the resulting HTMLValue
    * @return whether the value was parsed
    */
-  PRBool ParseDivAlignValue(const nsAString& aString,
-                            nsAttrValue& aResult) const;
+  static PRBool ParseDivAlignValue(const nsAString& aString,
+                                   nsAttrValue& aResult);
 
   /**
    * Convert a table halign string to value (left/right/center/char/justify)
    *
    * @param aString the string to parse
    * @param aResult the resulting HTMLValue
    * @return whether the value was parsed
    */
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -197,23 +197,17 @@ nsHTMLCanvasElement::ToDataURL(const nsA
 {
   // do a trust check if this is a write-only canvas
   // or if we're trying to use the 2-arg form
   if ((mWriteOnly || optional_argc >= 2) &&
       !nsContentUtils::IsCallerTrustedForRead()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsAutoString type(aType);
-
-  if (type.IsEmpty()) {
-    type.AssignLiteral("image/png");
-  }
-
-  return ToDataURLImpl(type, aParams, aDataURL);
+  return ToDataURLImpl(aType, aParams, aDataURL);
 }
 
 
 // nsHTMLCanvasElement::toDataURLAs
 //
 // Native-callers only
 
 NS_IMETHODIMP
@@ -224,33 +218,40 @@ nsHTMLCanvasElement::ToDataURLAs(const n
   return ToDataURLImpl(aMimeType, aEncoderOptions, aDataURL);
 }
 
 nsresult
 nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
                                    const nsAString& aEncoderOptions,
                                    nsAString& aDataURL)
 {
-  nsresult rv;
+  bool fallbackToPNG = false;
   
   // We get an input stream from the context. If more than one context type
   // is supported in the future, this will have to be changed to do the right
   // thing. For now, just assume that the 2D context has all the goods.
   nsCOMPtr<nsICanvasRenderingContextInternal> context;
-  rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
+  nsresult rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // get image bytes
   nsCOMPtr<nsIInputStream> imgStream;
   NS_ConvertUTF16toUTF8 aMimeType8(aMimeType);
   rv = context->GetInputStream(nsPromiseFlatCString(aMimeType8).get(),
                                nsPromiseFlatString(aEncoderOptions).get(),
                                getter_AddRefs(imgStream));
-  // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    // Use image/png instead.
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    fallbackToPNG = true;
+    rv = context->GetInputStream("image/png",
+                                 nsPromiseFlatString(aEncoderOptions).get(),
+                                 getter_AddRefs(imgStream));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   // Generally, there will be only one chunk of data, and it will be available
   // for us to read right away, so optimize this case.
   PRUint32 bufSize;
   rv = imgStream->Available(&bufSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ...leave a little extra room so we can call read again and make sure we
@@ -278,18 +279,22 @@ nsHTMLCanvasElement::ToDataURLImpl(const
 
   // base 64, result will be NULL terminated
   char* encodedImg = PL_Base64Encode(imgData, imgSize, nsnull);
   PR_Free(imgData);
   if (!encodedImg) // not sure why this would fail
     return NS_ERROR_OUT_OF_MEMORY;
 
   // build data URL string
-  aDataURL = NS_LITERAL_STRING("data:") + aMimeType +
-    NS_LITERAL_STRING(";base64,") + NS_ConvertUTF8toUTF16(encodedImg);
+  if (fallbackToPNG)
+    aDataURL = NS_LITERAL_STRING("data:image/png;base64,") +
+      NS_ConvertUTF8toUTF16(encodedImg);
+  else
+    aDataURL = NS_LITERAL_STRING("data:") + aMimeType +
+      NS_LITERAL_STRING(";base64,") + NS_ConvertUTF8toUTF16(encodedImg);
 
   PR_Free(encodedImg);
 
   return NS_OK;
 }
 
 nsresult
 nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
@@ -304,34 +309,34 @@ nsHTMLCanvasElement::GetContextHelper(co
   for (PRUint32 i = 0; i < ctxId.Length(); i++) {
     if ((ctxId[i] < 'A' || ctxId[i] > 'Z') &&
         (ctxId[i] < 'a' || ctxId[i] > 'z') &&
         (ctxId[i] < '0' || ctxId[i] > '9') &&
         (ctxId[i] != '-') &&
         (ctxId[i] != '_'))
     {
       // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-      return NS_ERROR_INVALID_ARG;
+      return NS_OK;
     }
   }
 
   nsCString ctxString("@mozilla.org/content/canvas-rendering-context;1?id=");
   ctxString.Append(ctxId);
 
   nsresult rv;
   nsCOMPtr<nsICanvasRenderingContextInternal> ctx =
     do_CreateInstance(nsPromiseFlatCString(ctxString).get(), &rv);
   if (rv == NS_ERROR_OUT_OF_MEMORY) {
     *aContext = nsnull;
     return NS_ERROR_OUT_OF_MEMORY;
   }
   if (NS_FAILED(rv)) {
     *aContext = nsnull;
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    return NS_ERROR_INVALID_ARG;
+    return NS_OK;
   }
 
   rv = ctx->SetCanvasElement(this);
   if (NS_FAILED(rv)) {
     *aContext = nsnull;
     return rv;
   }
 
@@ -371,17 +376,17 @@ nsHTMLCanvasElement::GetContext(const ns
     if (NS_FAILED(rv)) {
       mCurrentContext = nsnull;
       return rv;
     }
 
     mCurrentContextId.Assign(aContextId);
   } else if (!mCurrentContextId.Equals(aContextId)) {
     //XXX eventually allow for more than one active context on a given canvas
-    return NS_ERROR_INVALID_ARG;
+    return NS_OK;
   }
 
   NS_ADDREF (*aContext = mCurrentContext);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
--- a/content/html/content/test/test_bug346485.html
+++ b/content/html/content/test/test_bug346485.html
@@ -111,17 +111,17 @@ function checkFormIDLAttribute(element)
 
 function checkHtmlForIDLAttribute(element)
 {
   is(element.htmlFor, 'a b',
     "htmlFor IDL attribute should reflect the for content attribute");
 
   // DOMSettableTokenList is tested in another bug so we just test assignation
   element.htmlFor.value = 'a b c';
-  is(element.htmlFor, 'a b c', "htmlFor shoud have changed");
+  is(element.htmlFor, 'a b c', "htmlFor should have changed");
 }
 
 function submitForm()
 {
   // Setting the values for the submit.
   document.getElementById('o').value = 'foo';
   document.getElementById('a').value = 'afield';
   document.getElementById('b').value = 'bfield';
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -143,21 +143,23 @@ typedef nsGenericHTMLElement* (*contentC
 nsGenericHTMLElement*
 NS_NewHTMLNOTUSEDElement(nsINodeInfo *aNodeInfo, PRUint32 aFromParser)
 {
   NS_NOTREACHED("The element ctor should never be called");
   return nsnull;
 }
 
 #define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
+#define HTML_HTMLELEMENT_TAG(_tag) NS_NewHTMLElement,
 #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
 static const contentCreatorCallback sContentCreatorCallbacks[] = {
   NS_NewHTMLUnknownElement,
 #include "nsHTMLTagList.h"
 #undef HTML_TAG
+#undef HTML_HTMLELEMENT_TAG
 #undef HTML_OTHER
   NS_NewHTMLUnknownElement
 };
 
 class SinkContext;
 class HTMLContentSink;
 
 static void MaybeSetForm(nsGenericHTMLElement*, nsHTMLTag, HTMLContentSink*);
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1249,31 +1249,21 @@ nsHTMLDocument::SetCompatibilityMode(nsC
 //
 // nsIDOMDocument interface implementation
 //
 NS_IMETHODIMP
 nsHTMLDocument::CreateElement(const nsAString& aTagName,
                               nsIDOMElement** aReturn)
 {
   *aReturn = nsnull;
-  nsresult rv;
+  nsresult rv = nsContentUtils::CheckQName(aTagName, PR_FALSE);
+  if (NS_FAILED(rv))
+    return rv;
 
   nsAutoString tagName(aTagName);
-
-  // if we are in quirks, allow surrounding '<' '>' for IE compat
-  if (mCompatMode == eCompatibility_NavQuirks &&
-      tagName.Length() > 2 &&
-      tagName.First() == '<' &&
-      tagName.Last() == '>') {
-    tagName = Substring(tagName, 1, tagName.Length() - 2); 
-  }
-
-  rv = nsContentUtils::CheckQName(tagName, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   if (IsHTML()) {
     ToLowerCase(tagName);
   }
 
   nsCOMPtr<nsIAtom> name = do_GetAtom(tagName);
 
   nsCOMPtr<nsIContent> content;
   rv = CreateElem(name, nsnull, kNameSpaceID_XHTML, PR_TRUE,
--- a/content/html/document/src/nsHTMLFragmentContentSink.cpp
+++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp
@@ -1001,17 +1001,20 @@ nsHTMLParanoidFragmentSink::AddAttribute
     }
 
     // Get value and remove mandatory quotes
     static const char* kWhitespace = "\n\r\t\b";
     const nsAString& v =
       nsContentUtils::TrimCharsInSet(kWhitespace, aNode.GetValueAt(i));
 
     // check the attributes we allow that contain URIs
-    if (IsAttrURI(keyAtom)) {
+    // special case src attributes for img tags, because they can't
+    // run any dangerous code.
+    if (IsAttrURI(keyAtom) &&
+        !(nodeType == eHTMLTag_img && keyAtom == nsGkAtoms::src)) {
       if (!baseURI) {
         baseURI = aContent->GetBaseURI();
       }
       nsCOMPtr<nsIURI> attrURI;
       rv = NS_NewURI(getter_AddRefs(attrURI), v, nsnull, baseURI);
       if (NS_SUCCEEDED(rv)) {
         rv = secMan->
           CheckLoadURIWithPrincipal(mTargetDocument->NodePrincipal(),
--- a/content/html/document/test/Makefile.in
+++ b/content/html/document/test/Makefile.in
@@ -56,16 +56,17 @@ include $(topsrcdir)/config/rules.mk
 		bug199692-scrolled.html \
 		test_bug172261.html \
 		test_bug255820.html \
 		test_bug259332.html \
 		test_bug311681.html \
 		test_bug311681.xhtml \
 		test_bug324378.html \
 		test_bug332848.xhtml \
+		test_bug340017.xhtml \
 		test_bug359657.html \
 		test_bug369370.html \
 		bug369370-popup.png \
 		test_bug380383.html \
 		test_bug391777.html \
 		test_bug402680.html \
 		test_bug403868.html \
 		test_bug403868.xhtml \
@@ -90,20 +91,20 @@ include $(topsrcdir)/config/rules.mk
 		bug448564-iframe-3.html \
 		bug448564-echo.sjs \
 		bug448564-submit.js \
 		test_bug478251.html \
 		test_bug481440.html \
 		test_bug481647.html \
 		test_bug482659.html \
 		test_bug486741.html \
+		test_bug489532.html \
 		test_bug497242.xhtml \
-		test_bug512367.html \
-		test_bug570375.html \
-		test_bug340017.xhtml \
 		test_bug499092.html \
 		bug499092.xml \
 		bug499092.html \
+		test_bug512367.html \
+		test_bug570376.html \
 		test_bug571981.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/document/test/test_bug489532.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=489532
+-->
+<head>
+  <title>Test for Bug 489532</title>
+  <script src="/MochiKit/packed.js"></script>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489532">Mozilla Bug 489532</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 489532 **/
+try {
+  document.createElement("<div>");
+  ok(false, "Should throw.")
+} catch (e) {
+  ok(e instanceof DOMException, "Expected DOMException.");
+  is(e.code, DOMException.INVALID_CHARACTER_ERR,
+     "Expected INVALID_CHARACTER_ERR.");
+}
+</script>
+</pre>
+</body>
+</html>
rename from content/html/document/test/test_bug570375.html
rename to content/html/document/test/test_bug570376.html
--- a/content/html/document/test/test_bug570375.html
+++ b/content/html/document/test/test_bug570376.html
@@ -1,30 +1,30 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=570375
+https://bugzilla.mozilla.org/show_bug.cgi?id=570376
 -->
 <head>
-  <title>Test for Bug 570375</title>
+  <title>Test for Bug 570376</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=570375">Mozilla Bug 570375</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570376">Mozilla Bug 570376</a>
 <p id="display">
   <iframe id="testiframe"></iframe>
 </p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 570375
+/** Test for Bug 570376
     Don't crash loading <form><legend> with the html5 parser preffed off.
  **/
 
 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                       .getService(Components.interfaces.nsIPrefBranch);
 var gOriginalHtml5Pref = prefs.getBoolPref("html5.enable");
 prefs.setBoolPref("html5.enable", false);
--- a/content/media/nsBuiltinDecoderReader.cpp
+++ b/content/media/nsBuiltinDecoderReader.cpp
@@ -121,19 +121,19 @@ VideoData* VideoData::Create(nsVideoInfo
     NS_WARNING("Invalid plane size");
     return nsnull;
   }
   // Ensure the picture size specified in the headers can be extracted out of
   // the frame we've been supplied without indexing out of bounds.
   PRUint32 picXLimit;
   PRUint32 picYLimit;
   if (!AddOverflow32(aInfo.mPicture.x, aInfo.mPicture.width, picXLimit) ||
-      picXLimit > PRUint32(aBuffer.mPlanes[0].mStride) ||
+      picXLimit > aBuffer.mPlanes[0].mStride ||
       !AddOverflow32(aInfo.mPicture.y, aInfo.mPicture.height, picYLimit) ||
-      picYLimit > PRUint32(aBuffer.mPlanes[0].mHeight))
+      picYLimit > aBuffer.mPlanes[0].mHeight)
   {
     // The specified picture dimensions can't be contained inside the video
     // frame, we'll stomp memory if we try to copy it. Fail.
     NS_WARNING("Overflowing picture rect");
     return nsnull;
   }
 
   nsAutoPtr<VideoData> v(new VideoData(aOffset, aTime, aEndTime, aKeyframe, aTimecode));
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -899,20 +899,21 @@ PRInt64 nsOggReader::FindEndTime(PRInt64
 
     nsOggCodecState* codecState = nsnull;
     mCodecStates.Get(serial, &codecState);
 
     if (!codecState) {
       // This page is from a bitstream which we haven't encountered yet.
       // It's probably from a new "link" in a "chained" ogg. Don't
       // bother even trying to find a duration...
+      endTime = -1;
       break;
     }
 
-    PRInt64 t = codecState ? codecState->Time(granulepos) : -1;
+    PRInt64 t = codecState->Time(granulepos);
     if (t != -1) {
       endTime = t;
     }
   }
 
   ogg_sync_reset(&mOggState);
 
   NS_ASSERTION(mDataOffset > 0,
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -320,16 +320,19 @@ nsSMILAnimationController::DoSample()
 
 void
 nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
 {
   // Reset resample flag
   mResampleNeeded = PR_FALSE;
 
   // STEP 1: Bring model up to date
+  // (i)  Rewind elements where necessary
+  // (ii) Run milestone samples
+  RewindElements();
   DoMilestoneSamples();
 
   // STEP 2: Sample the child time containers
   //
   // When we sample the child time containers they will simply record the sample
   // time in document time.
   TimeContainerHashtable activeContainers;
   activeContainers.Init(mChildContainerTable.Count());
@@ -398,16 +401,66 @@ nsSMILAnimationController::DoSample(PRBo
 
   // Update last compositor table
   mLastCompositorTable = currentCompositorTable.forget();
 
   NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
 }
 
 void
+nsSMILAnimationController::RewindElements()
+{
+  PRBool rewindNeeded = PR_FALSE;
+  mChildContainerTable.EnumerateEntries(RewindNeeded, &rewindNeeded);
+  if (!rewindNeeded)
+    return;
+
+  mAnimationElementTable.EnumerateEntries(RewindAnimation, nsnull);
+  mChildContainerTable.EnumerateEntries(ClearRewindNeeded, nsnull);
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::RewindNeeded(TimeContainerPtrKey* aKey,
+                                        void* aData)
+{
+  NS_ABORT_IF_FALSE(aData,
+      "Null data pointer during time container enumeration");
+  PRBool* rewindNeeded = static_cast<PRBool*>(aData);
+
+  nsSMILTimeContainer* container = aKey->GetKey();
+  if (container->NeedsRewind()) {
+    *rewindNeeded = PR_TRUE;
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::RewindAnimation(AnimationElementPtrKey* aKey,
+                                           void* aData)
+{
+  nsISMILAnimationElement* animElem = aKey->GetKey();
+  nsSMILTimeContainer* timeContainer = animElem->GetTimeContainer();
+  if (timeContainer && timeContainer->NeedsRewind()) {
+    animElem->TimedElement().Rewind();
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::ClearRewindNeeded(TimeContainerPtrKey* aKey,
+                                             void* aData)
+{
+  aKey->GetKey()->ClearNeedsRewind();
+  return PL_DHASH_NEXT;
+}
+
+void
 nsSMILAnimationController::DoMilestoneSamples()
 {
   // We need to sample the timing model but because SMIL operates independently
   // of the frame-rate, we can get one sample at t=0s and the next at t=10min.
   //
   // In between those two sample times a whole string of significant events
   // might be expected to take place: events firing, new interdependencies
   // between animations resolved and dissolved, etc.
@@ -537,16 +590,17 @@ nsSMILAnimationController::SampleTimeCon
   SampleTimeContainerParams* params =
     static_cast<SampleTimeContainerParams*>(aData);
 
   nsSMILTimeContainer* container = aKey->GetKey();
   if (!container->IsPausedByType(nsSMILTimeContainer::PAUSE_BEGIN) &&
       (container->NeedsSample() || !params->mSkipUnchangedContainers)) {
     container->ClearMilestones();
     container->Sample();
+    container->MarkSeekFinished();
     params->mActiveContainers->PutEntry(container);
   }
 
   return PL_DHASH_NEXT;
 }
 
 /*static*/ PR_CALLBACK PLDHashOperator
 nsSMILAnimationController::SampleAnimation(AnimationElementPtrKey* aKey,
@@ -582,16 +636,18 @@ nsSMILAnimationController::SampleTimedEl
   // Instead we build up a hashmap of active time containers during the previous
   // step (SampleTimeContainer) and then test here if the container for this
   // timed element is in the list.
   if (!aActiveContainers->GetEntry(timeContainer))
     return;
 
   nsSMILTime containerTime = timeContainer->GetCurrentTime();
 
+  NS_ABORT_IF_FALSE(!timeContainer->IsSeeking(),
+      "Doing a regular sample but the time container is still seeking");
   aElement->TimedElement().SampleAt(containerTime);
 }
 
 /*static*/ void
 nsSMILAnimationController::AddAnimationToCompositorTable(
   nsISMILAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable)
 {
   // Add a compositor to the hash table if there's not already one there
--- a/content/smil/nsSMILAnimationController.h
+++ b/content/smil/nsSMILAnimationController.h
@@ -147,21 +147,31 @@ protected:
 
   // Cycle-collection implementation helpers
   PR_STATIC_CALLBACK(PLDHashOperator) CompositorTableEntryTraverse(
       nsSMILCompositor* aCompositor, void* aArg);
 
   // Sample-related callbacks and implementation helpers
   virtual void DoSample();
   void DoSample(PRBool aSkipUnchangedContainers);
+
+  void RewindElements();
+  PR_STATIC_CALLBACK(PLDHashOperator) RewindNeeded(
+      TimeContainerPtrKey* aKey, void* aData);
+  PR_STATIC_CALLBACK(PLDHashOperator) RewindAnimation(
+      AnimationElementPtrKey* aKey, void* aData);
+  PR_STATIC_CALLBACK(PLDHashOperator) ClearRewindNeeded(
+      TimeContainerPtrKey* aKey, void* aData);
+
   void DoMilestoneSamples();
   PR_STATIC_CALLBACK(PLDHashOperator) GetNextMilestone(
       TimeContainerPtrKey* aKey, void* aData);
   PR_STATIC_CALLBACK(PLDHashOperator) GetMilestoneElements(
       TimeContainerPtrKey* aKey, void* aData);
+
   PR_STATIC_CALLBACK(PLDHashOperator) SampleTimeContainer(
       TimeContainerPtrKey* aKey, void* aData);
   PR_STATIC_CALLBACK(PLDHashOperator) SampleAnimation(
       AnimationElementPtrKey* aKey, void* aData);
   static void SampleTimedElement(nsISMILAnimationElement* aElement,
                                  TimeContainerHashtable* aActiveContainers);
   static void AddAnimationToCompositorTable(
     nsISMILAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable);
--- a/content/smil/nsSMILInstanceTime.cpp
+++ b/content/smil/nsSMILInstanceTime.cpp
@@ -69,48 +69,51 @@ namespace
 // Implementation
 
 nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
                                        nsSMILInstanceTimeSource aSource,
                                        nsSMILTimeValueSpec* aCreator,
                                        nsSMILInterval* aBaseInterval)
   : mTime(aTime),
     mFlags(0),
+    mVisited(PR_FALSE),
+    mFixedEndpointRefCnt(0),
     mSerial(0),
-    mVisited(PR_FALSE),
-    mChainEnd(PR_FALSE),
     mCreator(aCreator),
     mBaseInterval(nsnull) // This will get set to aBaseInterval in a call to
                           // SetBaseInterval() at end of constructor
 {
   switch (aSource) {
     case SOURCE_NONE:
       // No special flags
       break;
 
     case SOURCE_DOM:
-      mFlags = kClearOnReset | kFromDOM;
+      mFlags = kDynamic | kFromDOM;
       break;
 
     case SOURCE_SYNCBASE:
       mFlags = kMayUpdate;
       break;
 
     case SOURCE_EVENT:
-      mFlags = kClearOnReset;
+      mFlags = kDynamic;
       break;
   }
 
   SetBaseInterval(aBaseInterval);
 }
 
 nsSMILInstanceTime::~nsSMILInstanceTime()
 {
   NS_ABORT_IF_FALSE(!mBaseInterval && !mCreator,
       "Destroying instance time without first calling Unlink()");
+  NS_ABORT_IF_FALSE(mFixedEndpointRefCnt == 0,
+      "Destroying instance time that is still used as the fixed endpoint of an "
+      "interval");
 }
 
 void
 nsSMILInstanceTime::Unlink()
 {
   nsRefPtr<nsSMILInstanceTime> deathGrip(this);
   if (mBaseInterval) {
     mBaseInterval->RemoveDependentTime(*this);
@@ -124,22 +127,19 @@ nsSMILInstanceTime::HandleChangedInterva
     const nsSMILTimeContainer* aSrcContainer,
     PRBool aBeginObjectChanged,
     PRBool aEndObjectChanged)
 {
   NS_ABORT_IF_FALSE(mBaseInterval,
       "Got call to HandleChangedInterval on an independent instance time.");
   NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
 
-  if (mVisited || mChainEnd) {
-    // We're breaking the cycle here but we need to ensure that if we later
-    // receive a change notice in a different context (e.g. due to a time
-    // container change) that we don't end up following the chain further and so
-    // we set a flag to that effect.
-    mChainEnd = PR_TRUE;
+  if (mVisited) {
+    // Break the cycle here
+    Unlink();
     return;
   }
 
   PRBool objectChanged = mCreator->DependsOnBegin() ? aBeginObjectChanged :
                                                       aEndObjectChanged;
 
   AutoBoolSetter setVisited(mVisited);
 
@@ -147,42 +147,85 @@ nsSMILInstanceTime::HandleChangedInterva
   mCreator->HandleChangedInstanceTime(*GetBaseTime(), aSrcContainer, *this,
                                       objectChanged);
 }
 
 void
 nsSMILInstanceTime::HandleDeletedInterval()
 {
   NS_ABORT_IF_FALSE(mBaseInterval,
-      "Got call to HandleDeletedInterval on an independent instance time.");
-  NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
+      "Got call to HandleDeletedInterval on an independent instance time");
+  NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not");
 
   mBaseInterval = nsnull;
+  mFlags &= ~kMayUpdate; // Can't update without a base interval
 
   nsRefPtr<nsSMILInstanceTime> deathGrip(this);
   mCreator->HandleDeletedInstanceTime(*this);
   mCreator = nsnull;
 }
 
+void
+nsSMILInstanceTime::HandleFilteredInterval()
+{
+  NS_ABORT_IF_FALSE(mBaseInterval,
+      "Got call to HandleFilteredInterval on an independent instance time");
+
+  mBaseInterval = nsnull;
+  mFlags &= ~kMayUpdate; // Can't update without a base interval
+  mCreator = nsnull;
+}
+
 PRBool
-nsSMILInstanceTime::IsDependent(const nsSMILInstanceTime& aOther) const
+nsSMILInstanceTime::ShouldPreserve() const
+{
+  return mFixedEndpointRefCnt > 0 || (mFlags & kWasDynamicEndpoint);
+}
+
+void
+nsSMILInstanceTime::UnmarkShouldPreserve()
+{
+  mFlags &= ~kWasDynamicEndpoint;
+}
+
+void
+nsSMILInstanceTime::AddRefFixedEndpoint()
 {
-  if (mVisited || mChainEnd)
+  NS_ABORT_IF_FALSE(mFixedEndpointRefCnt < PR_UINT16_MAX,
+      "Fixed endpoint reference count upper limit reached");
+  ++mFixedEndpointRefCnt;
+  mFlags &= ~kMayUpdate; // Once fixed, always fixed
+}
+
+void
+nsSMILInstanceTime::ReleaseFixedEndpoint()
+{
+  NS_ABORT_IF_FALSE(mFixedEndpointRefCnt > 0, "Duplicate release");
+  --mFixedEndpointRefCnt;
+  if (mFixedEndpointRefCnt == 0 && IsDynamic()) {
+    mFlags |= kWasDynamicEndpoint;
+  }
+}
+
+PRBool
+nsSMILInstanceTime::IsDependentOn(const nsSMILInstanceTime& aOther) const
+{
+  if (mVisited)
     return PR_FALSE;
 
   const nsSMILInstanceTime* myBaseTime = GetBaseTime();
   if (!myBaseTime)
     return PR_FALSE;
 
   if (myBaseTime == &aOther)
     return PR_TRUE;
 
   // mVisited is mutable
   AutoBoolSetter setVisited(const_cast<nsSMILInstanceTime*>(this)->mVisited);
-  return myBaseTime->IsDependent(aOther);
+  return myBaseTime->IsDependentOn(aOther);
 }
 
 void
 nsSMILInstanceTime::SetBaseInterval(nsSMILInterval* aBaseInterval)
 {
   NS_ABORT_IF_FALSE(!mBaseInterval,
       "Attempting to reassociate an instance time with a different interval.");
 
--- a/content/smil/nsSMILInstanceTime.h
+++ b/content/smil/nsSMILInstanceTime.h
@@ -87,34 +87,41 @@ public:
                      nsSMILTimeValueSpec* aCreator = nsnull,
                      nsSMILInterval* aBaseInterval = nsnull);
   ~nsSMILInstanceTime();
   void Unlink();
   void HandleChangedInterval(const nsSMILTimeContainer* aSrcContainer,
                              PRBool aBeginObjectChanged,
                              PRBool aEndObjectChanged);
   void HandleDeletedInterval();
+  void HandleFilteredInterval();
 
   const nsSMILTimeValue& Time() const { return mTime; }
   const nsSMILTimeValueSpec* GetCreator() const { return mCreator; }
 
-  PRBool ClearOnReset() const { return !!(mFlags & kClearOnReset); }
-  PRBool MayUpdate() const { return !!(mFlags & kMayUpdate); }
+  PRBool IsDynamic() const { return !!(mFlags & kDynamic); }
+  PRBool IsFixedTime() const { return !(mFlags & kMayUpdate); }
   PRBool FromDOM() const { return !!(mFlags & kFromDOM); }
 
-  void MarkNoLongerUpdating() { mFlags &= ~kMayUpdate; }
+  PRBool ShouldPreserve() const;
+  void   UnmarkShouldPreserve();
+
+  void AddRefFixedEndpoint();
+  void ReleaseFixedEndpoint();
 
   void DependentUpdate(const nsSMILTimeValue& aNewTime)
   {
-    NS_ABORT_IF_FALSE(MayUpdate(),
+    NS_ABORT_IF_FALSE(!IsFixedTime(),
         "Updating an instance time that is not expected to be updated");
     mTime = aNewTime;
   }
 
-  PRBool IsDependent(const nsSMILInstanceTime& aOther) const;
+  PRBool IsDependent() const { return !!mBaseInterval; }
+  PRBool IsDependentOn(const nsSMILInstanceTime& aOther) const;
+  const nsSMILInterval* GetBaseInterval() const { return mBaseInterval; }
 
   PRBool SameTimeAndBase(const nsSMILInstanceTime& aOther) const
   {
     return mTime == aOther.mTime && GetBaseTime() == aOther.GetBaseTime();
   }
 
   // Get and set a serial number which may be used by a containing class to
   // control the sort order of otherwise similar instance times.
@@ -126,43 +133,60 @@ public:
 protected:
   void SetBaseInterval(nsSMILInterval* aBaseInterval);
   const nsSMILInstanceTime* GetBaseTime() const;
 
   nsSMILTimeValue mTime;
 
   // Internal flags used to represent the behaviour of different instance times
   enum {
-    // Indicates if this instance time should be removed when the owning timed
-    // element is reset. True for events and DOM calls.
-    kClearOnReset = 1,
+    // Indicates that this instance time was generated by an event or a DOM
+    // call. Such instance times require special handling when (i) the owning
+    // element is reset, and (ii) when a backwards seek is performed and the
+    // timing model is reconstructed.
+    kDynamic = 1,
 
     // Indicates that this instance time is referred to by an
     // nsSMILTimeValueSpec and as such may be updated. Such instance time should
     // not be filtered out by the nsSMILTimedElement even if they appear to be
-    // in the past as they may be updated to a future time. Initially set for
-    // syncbase-generated times until they are frozen.
+    // in the past as they may be updated to a future time.
     kMayUpdate = 2,
 
     // Indicates that this instance time was generated from the DOM as opposed
     // to an nsSMILTimeValueSpec. When a 'begin' or 'end' attribute is set or
     // reset we should clear all the instance times that have been generated by
     // that attribute (and hence an nsSMILTimeValueSpec), but not those from the
     // DOM.
-    kFromDOM = 4
+    kFromDOM = 4,
+
+    // Indicates that this instance time was used as the endpoint of an interval
+    // that has been filtered or removed. However, since it is a dynamic time it
+    // should be preserved and not filtered.
+    kWasDynamicEndpoint = 8
   };
-  PRUint8       mFlags; // Combination of kClearOnReset, kMayUpdate, etc.
+  PRUint8       mFlags;   // Combination of kDynamic, kMayUpdate, etc.
+  PRPackedBool  mVisited; // (mutable) Cycle tracking
+
+  // Additional reference count to determine if this instance time is currently
+  // used as a fixed endpoint in any intervals. Instance times that are used in
+  // this way should not be removed when the owning nsSMILTimedElement removes
+  // instance times in response to a restart or in an attempt to free up memory
+  // by filtering out old instance times.
+  //
+  // Instance times are only shared in a few cases, namely:
+  // a) early ends,
+  // b) zero-duration intervals, and
+  // c) momentarily whilst establishing new intervals and updating the current
+  //    interval
+  // Hence the limited range of a PRUint16 should be more than adequate.
+  PRUint16      mFixedEndpointRefCnt;
+
   PRUint32      mSerial; // A serial number used by the containing class to
                          // specify the sort order for instance times with the
                          // same mTime.
-  PRPackedBool  mVisited; // (mutable) Cycle tracking
-  PRPackedBool  mChainEnd; // Flag to indicate that this instance time is part
-                           // of some cyclic dependency and that in order to
-                           // avoid infinite recursion the cycle should not be
-                           // followed any further than this point.
 
   nsSMILTimeValueSpec* mCreator; // The nsSMILTimeValueSpec object that created
                                  // us. (currently only needed for syncbase
                                  // instance times.)
   nsSMILInterval* mBaseInterval; // Interval from which this time is derived
                                  // (only used for syncbase instance times)
 };
 
--- a/content/smil/nsSMILInterval.cpp
+++ b/content/smil/nsSMILInterval.cpp
@@ -34,59 +34,82 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSMILInterval.h"
 
 nsSMILInterval::nsSMILInterval()
 :
+  mBeginFixed(PR_FALSE),
+  mEndFixed(PR_FALSE),
   mBeginObjectChanged(PR_FALSE),
   mEndObjectChanged(PR_FALSE)
 {
 }
 
 nsSMILInterval::nsSMILInterval(const nsSMILInterval& aOther)
 :
   mBegin(aOther.mBegin),
   mEnd(aOther.mEnd),
+  mBeginFixed(PR_FALSE),
+  mEndFixed(PR_FALSE),
   mBeginObjectChanged(PR_FALSE),
   mEndObjectChanged(PR_FALSE)
 {
   NS_ABORT_IF_FALSE(aOther.mDependentTimes.IsEmpty(),
       "Attempting to copy-construct an interval with dependent times, "
       "this will lead to instance times being shared between intervals.");
+
+  // For the time being we don't allow intervals with fixed endpoints to be
+  // copied since we only ever copy-construct to establish a new current
+  // interval. If we ever need to copy historical intervals we may need to move
+  // the ReleaseFixedEndpoint calls from Unlink to the dtor.
+  NS_ABORT_IF_FALSE(!aOther.mBeginFixed && !aOther.mEndFixed,
+      "Attempting to copy-construct an interval with fixed endpoints");
 }
 
 nsSMILInterval::~nsSMILInterval()
 {
   NS_ABORT_IF_FALSE(mDependentTimes.IsEmpty(),
       "Destroying interval without disassociating dependent instance times. "
-      "NotifyDeleting was not called.");
+      "Unlink was not called");
 }
 
 void
 nsSMILInterval::NotifyChanged(const nsSMILTimeContainer* aContainer)
 {
   for (PRInt32 i = mDependentTimes.Length() - 1; i >= 0; --i) {
     mDependentTimes[i]->HandleChangedInterval(aContainer,
                                               mBeginObjectChanged,
                                               mEndObjectChanged);
   }
   mBeginObjectChanged = PR_FALSE;
   mEndObjectChanged = PR_FALSE;
 }
 
 void
-nsSMILInterval::NotifyDeleting()
+nsSMILInterval::Unlink(PRBool aFiltered)
 {
   for (PRInt32 i = mDependentTimes.Length() - 1; i >= 0; --i) {
-    mDependentTimes[i]->HandleDeletedInterval();
+    if (aFiltered) {
+      mDependentTimes[i]->HandleFilteredInterval();
+    } else {
+      mDependentTimes[i]->HandleDeletedInterval();
+    }
   }
   mDependentTimes.Clear();
+  if (mBegin && mBeginFixed) {
+    mBegin->ReleaseFixedEndpoint();
+  }
+  mBegin = nsnull;
+  if (mEnd && mEndFixed) {
+    mEnd->ReleaseFixedEndpoint();
+  }
+  mEnd = nsnull;
 }
 
 nsSMILInstanceTime*
 nsSMILInterval::Begin()
 {
   NS_ABORT_IF_FALSE(mBegin && mEnd,
       "Requesting Begin() on un-initialized interval.");
   return mBegin;
@@ -99,36 +122,63 @@ nsSMILInterval::End()
       "Requesting End() on un-initialized interval.");
   return mEnd;
 }
 
 void
 nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
 {
   NS_ABORT_IF_FALSE(aBegin.Time().IsResolved(),
-      "Attempting to set unresolved begin time on interval.");
+      "Attempting to set unresolved begin time on interval");
+  NS_ABORT_IF_FALSE(!mBeginFixed,
+      "Attempting to set begin time but the begin point is fixed");
 
   if (mBegin == &aBegin)
     return;
 
   mBegin = &aBegin;
   mBeginObjectChanged = PR_TRUE;
 }
 
 void
 nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
 {
+  NS_ABORT_IF_FALSE(!mEndFixed,
+      "Attempting to set end time but the end point is fixed");
+
   if (mEnd == &aEnd)
     return;
 
   mEnd = &aEnd;
   mEndObjectChanged = PR_TRUE;
 }
 
 void
+nsSMILInterval::FixBegin()
+{
+  NS_ABORT_IF_FALSE(mBegin && mEnd,
+      "Fixing begin point on un-initialized interval");
+  NS_ABORT_IF_FALSE(!mBeginFixed, "Duplicate calls to FixBegin()");
+  mBeginFixed = PR_TRUE;
+  mBegin->AddRefFixedEndpoint();
+}
+
+void
+nsSMILInterval::FixEnd()
+{
+  NS_ABORT_IF_FALSE(mBegin && mEnd,
+      "Fixing end point on un-initialized interval");
+  NS_ABORT_IF_FALSE(mBeginFixed,
+      "Fixing the end of an interval without a fixed begin");
+  NS_ABORT_IF_FALSE(!mEndFixed, "Duplicate calls to FixEnd()");
+  mEndFixed = PR_TRUE;
+  mEnd->AddRefFixedEndpoint();
+}
+
+void
 nsSMILInterval::AddDependentTime(nsSMILInstanceTime& aTime)
 {
   nsRefPtr<nsSMILInstanceTime>* inserted =
     mDependentTimes.InsertElementSorted(&aTime);
   if (!inserted) {
     NS_WARNING("Insufficient memory to insert instance time.");
   }
 }
@@ -137,8 +187,24 @@ void
 nsSMILInterval::RemoveDependentTime(const nsSMILInstanceTime& aTime)
 {
 #ifdef DEBUG
   PRBool found =
 #endif
     mDependentTimes.RemoveElementSorted(&aTime);
   NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete.");
 }
+
+PRBool
+nsSMILInterval::IsDependencyChainLink() const
+{
+  if (!mBegin || !mEnd)
+    return PR_FALSE; // Not yet initialised so it can't be part of a chain
+
+  if (mDependentTimes.IsEmpty())
+    return PR_FALSE; // No dependents, chain end
+
+  // So we have dependents, but we're still only a link in the chain (as opposed
+  // to the end of the chain) if one of our endpoints is dependent on an
+  // interval other than ourselves.
+  return (mBegin->IsDependent() && mBegin->GetBaseInterval() != this) ||
+         (mEnd->IsDependent() && mEnd->GetBaseInterval() != this);
+}
--- a/content/smil/nsSMILInterval.h
+++ b/content/smil/nsSMILInterval.h
@@ -52,17 +52,17 @@
 
 class nsSMILInterval
 {
 public:
   nsSMILInterval();
   nsSMILInterval(const nsSMILInterval& aOther);
   ~nsSMILInterval();
   void NotifyChanged(const nsSMILTimeContainer* aContainer);
-  void NotifyDeleting();
+  void Unlink(PRBool aFiltered = PR_FALSE);
 
   const nsSMILInstanceTime* Begin() const
   {
     NS_ABORT_IF_FALSE(mBegin && mEnd,
         "Requesting Begin() on un-initialized instance time");
     return mBegin;
   }
   nsSMILInstanceTime* Begin();
@@ -78,56 +78,46 @@ public:
   void SetBegin(nsSMILInstanceTime& aBegin);
   void SetEnd(nsSMILInstanceTime& aEnd);
   void Set(nsSMILInstanceTime& aBegin, nsSMILInstanceTime& aEnd)
   {
     SetBegin(aBegin);
     SetEnd(aEnd);
   }
 
-  void FreezeBegin()
-  {
-    NS_ABORT_IF_FALSE(mBegin && mEnd,
-        "Freezing Begin() on un-initialized instance time");
-    mBegin->MarkNoLongerUpdating();
-  }
-
-  void FreezeEnd()
-  {
-    NS_ABORT_IF_FALSE(mBegin && mEnd,
-        "Freezing End() on un-initialized instance time");
-    NS_ABORT_IF_FALSE(!mBegin->MayUpdate(),
-        "Freezing the end of an interval without a fixed begin");
-    mEnd->MarkNoLongerUpdating();
-  }
-
-  // XXX Backwards seeking support (bug 492458)
-  void Unfreeze()
-  {
-    // XXX
-    UnfreezeEnd();
-  }
-
-  void UnfreezeEnd()
-  {
-    // XXX
-  }
+  void FixBegin();
+  void FixEnd();
 
   void AddDependentTime(nsSMILInstanceTime& aTime);
   void RemoveDependentTime(const nsSMILInstanceTime& aTime);
 
+  // Cue for assessing if this interval can be filtered
+  PRBool IsDependencyChainLink() const;
+
 private:
   nsRefPtr<nsSMILInstanceTime> mBegin;
   nsRefPtr<nsSMILInstanceTime> mEnd;
 
   typedef nsTArray<nsRefPtr<nsSMILInstanceTime> > InstanceTimeList;
 
   // nsSMILInstanceTimes to notify when this interval is changed or deleted.
   InstanceTimeList mDependentTimes;
 
+  // Indicates if the end points of the interval are fixed or not.
+  //
+  // Note that this is not the same as having an end point whose TIME is fixed
+  // (i.e. nsSMILInstanceTime::IsFixed() returns PR_TRUE). This is because it is
+  // possible to have an end point with a fixed TIME and yet still update the
+  // end point to refer to a different nsSMILInstanceTime object.
+  //
+  // However, if mBegin/EndFixed is PR_TRUE, then BOTH the nsSMILInstanceTime
+  // OBJECT returned for that end point and its TIME value will not change.
+  PRPackedBool mBeginFixed;
+  PRPackedBool mEndFixed;
+
   // When change notifications are passed around the timing model we try to
   // filter out all changes where there is no observable difference to an
   // instance time. Changes that may produce an observable difference are:
   //
   // * Changes to the time of an interval endpoint
   // * Changes in the relative times of different time containers
   // * Changes to the dependency chain (which may affect the animation sandwich)
   //
--- a/content/smil/nsSMILTimeContainer.cpp
+++ b/content/smil/nsSMILTimeContainer.cpp
@@ -41,16 +41,18 @@
 
 nsSMILTimeContainer::nsSMILTimeContainer()
 :
   mParent(nsnull),
   mCurrentTime(0L),
   mParentOffset(0L),
   mPauseStart(0L),
   mNeedsPauseSample(PR_FALSE),
+  mNeedsRewind(PR_FALSE),
+  mIsSeeking(PR_FALSE),
   mPauseState(PAUSE_BEGIN)
 {
 }
 
 nsSMILTimeContainer::~nsSMILTimeContainer()
 {
   if (mParent) {
     mParent->RemoveChild(*this);
@@ -140,29 +142,40 @@ nsSMILTimeContainer::GetCurrentTime() co
     return 0L;
 
   return mCurrentTime;
 }
 
 void
 nsSMILTimeContainer::SetCurrentTime(nsSMILTime aSeekTo)
 {
+  // SVG 1.1 doesn't specify what to do for negative times so we adopt SVGT1.2's
+  // behaviour of clamping negative times to 0.
+  aSeekTo = PR_MAX(0, aSeekTo);
+
   // The following behaviour is consistent with:
   // http://www.w3.org/2003/01/REC-SVG11-20030114-errata
   //  #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
   // which says that if SetCurrentTime is called before the document timeline
   // has begun we should still adjust the offset.
   nsSMILTime parentTime = GetParentTime();
   mParentOffset = parentTime - aSeekTo;
+  mIsSeeking = PR_TRUE;
 
   if (IsPaused()) {
     mNeedsPauseSample = PR_TRUE;
     mPauseStart = parentTime;
   }
 
+  if (aSeekTo < mCurrentTime) {
+    // Backwards seek
+    mNeedsRewind = PR_TRUE;
+    ClearMilestones();
+  }
+
   // Force an update to the current time in case we get a call to GetCurrentTime
   // before another call to Sample().
   UpdateCurrentTime();
 
   NotifyTimeChange();
 }
 
 nsSMILTime
--- a/content/smil/nsSMILTimeContainer.h
+++ b/content/smil/nsSMILTimeContainer.h
@@ -166,16 +166,31 @@ public:
    * Return if this time container should be sampled or can be skipped.
    *
    * This is most useful as an optimisation for skipping time containers that
    * don't require a sample.
    */
   PRBool NeedsSample() const { return !mPauseState || mNeedsPauseSample; }
 
   /*
+   * Indicates if the elements of this time container need to be rewound.
+   * This occurs during a backwards seek.
+   */
+  PRBool NeedsRewind() const { return mNeedsRewind; }
+  void ClearNeedsRewind() { mNeedsRewind = PR_FALSE; }
+
+  /*
+   * Indicates the time container is currently processing a SetCurrentTime
+   * request and appropriate seek behaviour should be applied by child elements
+   * (e.g. not firing time events).
+   */
+  PRBool IsSeeking() const { return mIsSeeking; }
+  void MarkSeekFinished() { mIsSeeking = PR_FALSE; }
+
+  /*
    * Sets the parent time container.
    *
    * The callee still retains ownership of the time container.
    */
   nsresult SetParent(nsSMILTimeContainer* aParent);
 
   /*
    * Registers an element for a sample at the given time.
@@ -274,16 +289,19 @@ protected:
   nsSMILTime mParentOffset;
 
   // The timestamp in parent time when the container was paused
   nsSMILTime mPauseStart;
 
   // Whether or not a pause sample is required
   PRPackedBool mNeedsPauseSample;
 
+  PRPackedBool mNeedsRewind; // Backwards seek performed
+  PRPackedBool mIsSeeking; // Currently in the middle of a seek operation
+
   // A bitfield of the pause state for all pause requests
   PRUint32 mPauseState;
 
   struct MilestoneEntry
   {
     MilestoneEntry(nsSMILMilestone aMilestone,
                    nsISMILAnimationElement& aElement)
       : mMilestone(aMilestone), mTimebase(&aElement)
--- a/content/smil/nsSMILTimeValueSpec.cpp
+++ b/content/smil/nsSMILTimeValueSpec.cpp
@@ -155,18 +155,18 @@ nsSMILTimeValueSpec::HandleNewInterval(n
 void
 nsSMILTimeValueSpec::HandleChangedInstanceTime(
     const nsSMILInstanceTime& aBaseTime,
     const nsSMILTimeContainer* aSrcContainer,
     nsSMILInstanceTime& aInstanceTimeToUpdate,
     PRBool aObjectChanged)
 {
   // If the instance time is fixed (e.g. because it's being used as the begin
-  // time of an active interval) we just ignore the change.
-  if (!aInstanceTimeToUpdate.MayUpdate())
+  // time of an active or postactive interval) we just ignore the change.
+  if (aInstanceTimeToUpdate.IsFixedTime())
     return;
 
   nsSMILTimeValue updatedTime =
     ConvertBetweenTimeContainers(aBaseTime.Time(), aSrcContainer);
 
   // Apply offset
   if (updatedTime.IsResolved()) {
     updatedTime.SetMillis(updatedTime.GetMillis() +
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -86,16 +86,39 @@ nsSMILTimedElement::InstanceTimeComparat
   NS_ABORT_IF_FALSE(aElem1->Serial() && aElem2->Serial(),
       "Instance times have not been assigned serial numbers");
 
   PRInt8 cmp = aElem1->Time().CompareTo(aElem2->Time());
   return cmp == 0 ? aElem1->Serial() < aElem2->Serial() : cmp < 0;
 }
 
 //----------------------------------------------------------------------
+// Templated helper functions
+
+// Selectively remove elements from an array of type
+// nsTArray<nsRefPtr<nsSMILInstanceTime> > with O(n) performance.
+template <class TestFunctor>
+void
+nsSMILTimedElement::RemoveInstanceTimes(InstanceTimeList& aArray,
+                                        TestFunctor& aTest)
+{
+  InstanceTimeList newArray;
+  for (PRUint32 i = 0; i < aArray.Length(); ++i) {
+    nsSMILInstanceTime* item = aArray[i].get();
+    if (aTest(item, i)) {
+      item->Unlink();
+    } else {
+      newArray.AppendElement(item);
+    }
+  }
+  aArray.Clear();
+  aArray.SwapElements(newArray);
+}
+
+//----------------------------------------------------------------------
 // Static members
 
 nsAttrValue::EnumTable nsSMILTimedElement::sFillModeTable[] = {
       {"remove", FILL_REMOVE},
       {"freeze", FILL_FREEZE},
       {nsnull, 0}
 };
 
@@ -103,31 +126,38 @@ nsAttrValue::EnumTable nsSMILTimedElemen
       {"always", RESTART_ALWAYS},
       {"whenNotActive", RESTART_WHENNOTACTIVE},
       {"never", RESTART_NEVER},
       {nsnull, 0}
 };
 
 const nsSMILMilestone nsSMILTimedElement::sMaxMilestone(LL_MAXINT, PR_FALSE);
 
+// The thresholds at which point we start filtering intervals and instance times
+// indiscriminately.
+// See FilterIntervals and FilterInstanceTimes.
+const PRUint8 nsSMILTimedElement::sMaxNumIntervals = 20;
+const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100;
+
 //----------------------------------------------------------------------
 // Ctor, dtor
 
 nsSMILTimedElement::nsSMILTimedElement()
 :
   mAnimationElement(nsnull),
   mFillMode(FILL_REMOVE),
   mRestartMode(RESTART_ALWAYS),
   mBeginSpecSet(PR_FALSE),
   mEndHasEventConditions(PR_FALSE),
   mInstanceSerialIndex(0),
   mClient(nsnull),
   mCurrentInterval(nsnull),
   mPrevRegisteredMilestone(sMaxMilestone),
-  mElementState(STATE_STARTUP)
+  mElementState(STATE_STARTUP),
+  mSeekState(SEEK_NOT_SEEKING)
 {
   mSimpleDur.SetIndefinite();
   mMin.SetMillis(0L);
   mMax.SetIndefinite();
   mTimeDependents.Init();
 }
 
 nsSMILTimedElement::~nsSMILTimedElement()
@@ -144,22 +174,22 @@ nsSMILTimedElement::~nsSMILTimedElement(
     mEndInstances[i]->Unlink();
   }
   mEndInstances.Clear();
 
   // Notify anyone listening to our intervals that they're gone
   // (We shouldn't get any callbacks from this because all our instance times
   // are now disassociated with any intervals)
   if (mCurrentInterval) {
-    mCurrentInterval->NotifyDeleting();
+    mCurrentInterval->Unlink();
     mCurrentInterval = nsnull;
   }
 
   for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
-    mOldIntervals[i]->NotifyDeleting();
+    mOldIntervals[i]->Unlink();
   }
   mOldIntervals.Clear();
 }
 
 void
 nsSMILTimedElement::SetAnimationElement(nsISMILAnimationElement* aElement)
 {
   NS_ABORT_IF_FALSE(aElement, "NULL owner element");
@@ -327,32 +357,43 @@ nsSMILTimedElement::RemoveInstanceTime(n
   PRBool found =
 #endif
     instanceList.RemoveElementSorted(aInstanceTime, InstanceTimeComparator());
   NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete");
 
   UpdateCurrentInterval();
 }
 
+namespace
+{
+  class RemoveByCreator
+  {
+  public:
+    RemoveByCreator(const nsSMILTimeValueSpec* aCreator) : mCreator(aCreator)
+    { }
+
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
+    {
+      return aInstanceTime->GetCreator() == mCreator;
+    }
+
+  private:
+    const nsSMILTimeValueSpec* mCreator;
+  };
+}
+
 void
 nsSMILTimedElement::RemoveInstanceTimesForCreator(
     const nsSMILTimeValueSpec* aCreator, PRBool aIsBegin)
 {
   NS_ABORT_IF_FALSE(aCreator, "Creator not set");
+
   InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
-
-  PRInt32 count = instances.Length();
-  for (PRInt32 i = count - 1; i >= 0; --i) {
-    nsSMILInstanceTime* instance = instances[i].get();
-    NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
-    if (instance->GetCreator() == aCreator) {
-      instance->Unlink();
-      instances.RemoveElementAt(i);
-    }
-  }
+  RemoveByCreator removeByCreator(aCreator);
+  RemoveInstanceTimes(instances, removeByCreator);
 
   UpdateCurrentInterval();
 }
 
 void
 nsSMILTimedElement::SetTimeClient(nsSMILAnimationFunction* aClient)
 {
   //
@@ -411,16 +452,26 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
   // the already active container.
   if (GetTimeContainer()->IsPausedByType(nsSMILTimeContainer::PAUSE_BEGIN))
     return;
 
   NS_ABORT_IF_FALSE(mElementState != STATE_STARTUP || aEndOnly,
       "Got a regular sample during startup state, expected an end sample"
       " instead");
 
+  PRBool finishedSeek = PR_FALSE;
+  if (GetTimeContainer()->IsSeeking() && mSeekState == SEEK_NOT_SEEKING) {
+    mSeekState = mElementState == STATE_ACTIVE ?
+                 SEEK_FORWARD_FROM_ACTIVE :
+                 SEEK_FORWARD_FROM_INACTIVE;
+  } else if (mSeekState != SEEK_NOT_SEEKING &&
+             !GetTimeContainer()->IsSeeking()) {
+    finishedSeek = PR_TRUE;
+  }
+
   PRBool          stateChanged;
   nsSMILTimeValue sampleTime(aContainerTime);
 
   do {
 #ifdef DEBUG
     // Check invariant
     if (mElementState == STATE_STARTUP || mElementState == STATE_POSTACTIVE) {
       NS_ABORT_IF_FALSE(!mCurrentInterval,
@@ -440,31 +491,26 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
         nsSMILInterval firstInterval;
         mElementState =
          NS_SUCCEEDED(GetNextInterval(nsnull, nsnull, firstInterval))
          ? STATE_WAITING
          : STATE_POSTACTIVE;
         stateChanged = PR_TRUE;
         if (mElementState == STATE_WAITING) {
           mCurrentInterval = new nsSMILInterval(firstInterval);
-          if (!mCurrentInterval) {
-            NS_WARNING("Failed to allocate memory for new interval");
-            mElementState = STATE_POSTACTIVE;
-          } else {
-            NotifyNewInterval();
-          }
+          NotifyNewInterval();
         }
       }
       break;
 
     case STATE_WAITING:
       {
         if (mCurrentInterval->Begin()->Time() <= sampleTime) {
           mElementState = STATE_ACTIVE;
-          mCurrentInterval->FreezeBegin();
+          mCurrentInterval->FixBegin();
           if (HasPlayed()) {
             Reset(); // Apply restart behaviour
           }
           if (mClient) {
             mClient->Activate(mCurrentInterval->Begin()->Time().GetMillis());
           }
           if (HasPlayed()) {
             // The call to Reset() may mean that the end point of our current
@@ -478,50 +524,41 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
           }
           stateChanged = PR_TRUE;
         }
       }
       break;
 
     case STATE_ACTIVE:
       {
-        // Only apply an early end if we're not already ending.
-        if (mCurrentInterval->End()->Time() > sampleTime) {
-          nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(sampleTime);
-          if (earlyEnd) {
-            mCurrentInterval->SetEnd(*earlyEnd);
-            NotifyChangedInterval();
-          }
-        }
+        ApplyEarlyEnd(sampleTime);
 
         if (mCurrentInterval->End()->Time() <= sampleTime) {
           nsSMILInterval newInterval;
           mElementState =
             NS_SUCCEEDED(GetNextInterval(mCurrentInterval, nsnull, newInterval))
             ? STATE_WAITING
             : STATE_POSTACTIVE;
           if (mClient) {
             mClient->Inactivate(mFillMode == FILL_FREEZE);
           }
-          mCurrentInterval->FreezeEnd();
+          mCurrentInterval->FixEnd();
           mOldIntervals.AppendElement(mCurrentInterval.forget());
           // We must update mOldIntervals before calling SampleFillValue
           SampleFillValue();
           if (mElementState == STATE_WAITING) {
             mCurrentInterval = new nsSMILInterval(newInterval);
-            if (!mCurrentInterval) {
-              NS_WARNING("Failed to allocate memory for new interval");
-              mElementState = STATE_POSTACTIVE;
-            } else {
-              NotifyNewInterval();
-            }
+            NotifyNewInterval();
           }
+          FilterHistory();
           stateChanged = PR_TRUE;
         } else {
           nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis();
+          NS_ASSERTION(aContainerTime >= beginTime,
+                       "Sample time should not precede current interval");
           nsSMILTime activeTime = aContainerTime - beginTime;
           SampleSimpleTime(activeTime);
         }
       }
       break;
 
     case STATE_POSTACTIVE:
       break;
@@ -531,16 +568,19 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim
   // state. However, for end samples we only drive the state machine as far as
   // the waiting or postactive state because we don't want to commit to any new
   // interval (by transitioning to the active state) until all the end samples
   // have finished and we then have complete information about the available
   // instance times upon which to base our next interval.
   } while (stateChanged && (!aEndOnly || (mElementState != STATE_WAITING &&
                                           mElementState != STATE_POSTACTIVE)));
 
+  if (finishedSeek) {
+    DoPostSeek();
+  }
   RegisterMilestone();
 }
 
 void
 nsSMILTimedElement::HandleContainerTimeChange()
 {
   // In future we could possibly introduce a separate change notice for time
   // container changes and only notify those dependents who live in other time
@@ -548,44 +588,56 @@ nsSMILTimedElement::HandleContainerTimeC
   // the nsSMILTimeValueSpec we'll check if anything has changed and if not, we
   // won't go any further.
   if (mElementState == STATE_WAITING || mElementState == STATE_ACTIVE) {
     NotifyChangedInterval();
   }
 }
 
 void
-nsSMILTimedElement::Reset()
+nsSMILTimedElement::Rewind()
 {
-  // SMIL 3.0 section 5.4.3, 'Resetting element state':
-  //   Any instance times associated with past Event-values, Repeat-values,
-  //   Accesskey-values or added via DOM method calls are removed from the
-  //   dependent begin and end instance times lists. In effect, all events and
-  //   DOM methods calls in the past are cleared. This does not apply to an
-  //   instance time that defines the begin of the current interval.
-  PRInt32 count = mBeginInstances.Length();
-  for (PRInt32 i = count - 1; i >= 0; --i) {
-    nsSMILInstanceTime* instance = mBeginInstances[i].get();
-    NS_ABORT_IF_FALSE(instance, "NULL instance in begin instances array");
-    if (instance->ClearOnReset() &&
-       (!mCurrentInterval || instance != mCurrentInterval->Begin())) {
-      instance->Unlink();
-      mBeginInstances.RemoveElementAt(i);
-    }
+  NS_ABORT_IF_FALSE(mAnimationElement,
+      "Got rewind request before being attached to an animation element");
+  NS_ABORT_IF_FALSE(mSeekState == SEEK_NOT_SEEKING,
+      "Got rewind request whilst already seeking");
+
+  mSeekState = mElementState == STATE_ACTIVE ?
+               SEEK_BACKWARD_FROM_ACTIVE :
+               SEEK_BACKWARD_FROM_INACTIVE;
+
+  // Set the STARTUP state first so that if we get any callbacks we won't waste
+  // time recalculating the current interval
+  mElementState = STATE_STARTUP;
+
+  // Clear the intervals and instance times except those instance times we can't
+  // regenerate (DOM calls etc.)
+  RewindTiming();
+
+  UnsetBeginSpec();
+  UnsetEndSpec();
+
+  if (mClient) {
+    mClient->Inactivate(PR_FALSE);
   }
 
-  count = mEndInstances.Length();
-  for (PRInt32 j = count - 1; j >= 0; --j) {
-    nsSMILInstanceTime* instance = mEndInstances[j].get();
-    NS_ABORT_IF_FALSE(instance, "NULL instance in end instances array");
-    if (instance->ClearOnReset()) {
-      instance->Unlink();
-      mEndInstances.RemoveElementAt(j);
-    }
+  if (mAnimationElement->HasAnimAttr(nsGkAtoms::begin)) {
+    nsAutoString attValue;
+    mAnimationElement->GetAnimAttr(nsGkAtoms::begin, attValue);
+    SetBeginSpec(attValue, &mAnimationElement->Content());
   }
+
+  if (mAnimationElement->HasAnimAttr(nsGkAtoms::end)) {
+    nsAutoString attValue;
+    mAnimationElement->GetAnimAttr(nsGkAtoms::end, attValue);
+    SetEndSpec(attValue, &mAnimationElement->Content());
+  }
+
+  mPrevRegisteredMilestone = sMaxMilestone;
+  RegisterMilestone();
 }
 
 PRBool
 nsSMILTimedElement::SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
                             nsAttrValue& aResult, nsIContent* aContextNode,
                             nsresult* aParseResult)
 {
   PRBool foundMatch = PR_TRUE;
@@ -917,26 +969,25 @@ void
 nsSMILTimedElement::AddDependent(nsSMILTimeValueSpec& aDependent)
 {
   // There's probably no harm in attempting to register a dependent
   // nsSMILTimeValueSpec twice, but we're not expecting it to happen.
   NS_ABORT_IF_FALSE(!mTimeDependents.GetEntry(&aDependent),
       "nsSMILTimeValueSpec is already registered as a dependency");
   mTimeDependents.PutEntry(&aDependent);
 
-  // Add old and current intervals
+  // Add current interval. We could add historical intervals too but that would
+  // cause unpredictable results since some intervals may have been filtered.
+  // SMIL doesn't say what to do here so for simplicity and consistency we
+  // simply add the current interval if there is one.
   //
   // It's not necessary to call SyncPauseTime since we're dealing with
   // historical instance times not newly added ones.
-  nsSMILTimeContainer* container = GetTimeContainer();
-  for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i) {
-    aDependent.HandleNewInterval(*mOldIntervals[i], container);
-  }
   if (mCurrentInterval) {
-    aDependent.HandleNewInterval(*mCurrentInterval, container);
+    aDependent.HandleNewInterval(*mCurrentInterval, GetTimeContainer());
   }
 }
 
 void
 nsSMILTimedElement::RemoveDependent(nsSMILTimeValueSpec& aDependent)
 {
   mTimeDependents.RemoveEntry(&aDependent);
 }
@@ -945,17 +996,17 @@ PRBool
 nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
 {
   const nsSMILInstanceTime* thisBegin = GetEffectiveBeginInstance();
   const nsSMILInstanceTime* otherBegin = aOther.GetEffectiveBeginInstance();
 
   if (!thisBegin || !otherBegin)
     return PR_FALSE;
 
-  return thisBegin->IsDependent(*otherBegin);
+  return thisBegin->IsDependentOn(*otherBegin);
 }
 
 void
 nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
 {
   // Resolve references to other parts of the tree
   PRUint32 count = mBeginSpecs.Length();
   for (PRUint32 i = 0; i < count; ++i) {
@@ -1049,33 +1100,307 @@ nsSMILTimedElement::SetBeginOrEndSpec(co
     ClearBeginOrEndSpecs(aIsBegin);
   }
 
   UpdateCurrentInterval();
 
   return rv;
 }
 
+namespace
+{
+  class RemoveNonDOM
+  {
+  public:
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
+    {
+      return !aInstanceTime->FromDOM();
+    }
+  };
+}
+
 void
 nsSMILTimedElement::ClearBeginOrEndSpecs(PRBool aIsBegin)
 {
   TimeValueSpecList& specs = aIsBegin ? mBeginSpecs : mEndSpecs;
   specs.Clear();
 
   // Remove only those instance times generated by the attribute, not those from
   // DOM calls.
   InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
-  PRInt32 count = instances.Length();
-  for (PRInt32 i = count - 1; i >= 0; --i) {
-    nsSMILInstanceTime* instance = instances[i].get();
-    NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
-    if (!instance->FromDOM()) {
-      instance->Unlink();
-      instances.RemoveElementAt(i);
+  RemoveNonDOM removeNonDOM;
+  RemoveInstanceTimes(instances, removeNonDOM);
+}
+
+void
+nsSMILTimedElement::RewindTiming()
+{
+  RewindInstanceTimes(mBeginInstances);
+  RewindInstanceTimes(mEndInstances);
+
+  if (mCurrentInterval) {
+    mCurrentInterval->Unlink();
+    mCurrentInterval = nsnull;
+  }
+
+  for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
+    mOldIntervals[i]->Unlink();
+  }
+  mOldIntervals.Clear();
+}
+
+namespace
+{
+  class RemoveNonDynamic
+  {
+  public:
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
+    {
+      // Generally dynamically-generated instance times (DOM calls, event-based
+      // times) are not associated with their creator nsSMILTimeValueSpec.
+      // If that ever changes though we'll need to make sure to disassociate
+      // them here otherwise they'll get removed when we clear the set of
+      // nsSMILTimeValueSpecs later on.
+      NS_ABORT_IF_FALSE(!aInstanceTime->IsDynamic() ||
+           !aInstanceTime->GetCreator(),
+          "Instance time retained during rewind needs to be unlinked");
+      return !aInstanceTime->IsDynamic();
+    }
+  };
+}
+
+void
+nsSMILTimedElement::RewindInstanceTimes(InstanceTimeList& aList)
+{
+  RemoveNonDynamic removeNonDynamic;
+  RemoveInstanceTimes(aList, removeNonDynamic);
+}
+
+void
+nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime)
+{
+  // This should only be called within DoSampleAt as a helper function
+  NS_ABORT_IF_FALSE(mElementState == STATE_ACTIVE,
+      "Unexpected state to try to apply an early end");
+
+  // Only apply an early end if we're not already ending.
+  if (mCurrentInterval->End()->Time() > aSampleTime) {
+    nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(aSampleTime);
+    if (earlyEnd) {
+      if (earlyEnd->IsDependent()) {
+        // Generate a new instance time for the early end since the
+        // existing instance time is part of some dependency chain that we
+        // don't want to participate in.
+        nsRefPtr<nsSMILInstanceTime> newEarlyEnd =
+          new nsSMILInstanceTime(earlyEnd->Time());
+        mCurrentInterval->SetEnd(*newEarlyEnd);
+      } else {
+        mCurrentInterval->SetEnd(*earlyEnd);
+      }
+      NotifyChangedInterval();
+    }
+  }
+}
+
+namespace
+{
+  class RemoveReset
+  {
+  public:
+    RemoveReset(const nsSMILInstanceTime* aCurrentIntervalBegin)
+      : mCurrentIntervalBegin(aCurrentIntervalBegin) { }
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
+    {
+      // SMIL 3.0 section 5.4.3, 'Resetting element state':
+      //   Any instance times associated with past Event-values, Repeat-values,
+      //   Accesskey-values or added via DOM method calls are removed from the
+      //   dependent begin and end instance times lists. In effect, all events
+      //   and DOM methods calls in the past are cleared. This does not apply to
+      //   an instance time that defines the begin of the current interval.
+      return aInstanceTime->IsDynamic() &&
+             !aInstanceTime->ShouldPreserve() &&
+             (!mCurrentIntervalBegin || aInstanceTime != mCurrentIntervalBegin);
     }
+
+  private:
+    const nsSMILInstanceTime* mCurrentIntervalBegin;
+  };
+}
+
+void
+nsSMILTimedElement::Reset()
+{
+  RemoveReset resetBegin(mCurrentInterval ? mCurrentInterval->Begin() : nsnull);
+  RemoveInstanceTimes(mBeginInstances, resetBegin);
+
+  RemoveReset resetEnd(nsnull);
+  RemoveInstanceTimes(mEndInstances, resetEnd);
+}
+
+void
+nsSMILTimedElement::DoPostSeek()
+{
+  // XXX When implementing TimeEvents we'll need to compare mElementState with
+  // mSeekState and dispatch events as follows:
+  //     ACTIVE->INACTIVE: End event
+  //     INACTIVE->ACTIVE: Begin event
+  //     ACTIVE->ACTIVE: Nothing (even if they're different intervals)
+  //     INACTIVE->INACTIVE: Nothing (even if we've skipped intervals)
+
+  // Finish backwards seek
+  if (mSeekState == SEEK_BACKWARD_FROM_INACTIVE ||
+      mSeekState == SEEK_BACKWARD_FROM_ACTIVE) {
+    // Previously some dynamic instance times may have been marked to be
+    // preserved because they were endpoints of an historic interval (which may
+    // or may not have been filtered). Now that we've finished a seek we should
+    // clear that flag for those instance times whose intervals are no longer
+    // historic.
+    UnpreserveInstanceTimes(mBeginInstances);
+    UnpreserveInstanceTimes(mEndInstances);
+
+    // Now that the times have been unmarked perform a reset. This might seem
+    // counter-intuitive when we're only doing a seek within an interval but
+    // SMIL seems to require this. SMIL 3.0, 'Hyperlinks and timing':
+    //   Resolved end times associated with events, Repeat-values,
+    //   Accesskey-values or added via DOM method calls are cleared when seeking
+    //   to time earlier than the resolved end time.
+    Reset();
+    UpdateCurrentInterval();
+  }
+
+  mSeekState = SEEK_NOT_SEEKING;
+}
+
+void
+nsSMILTimedElement::UnpreserveInstanceTimes(InstanceTimeList& aList)
+{
+  const nsSMILInterval* prevInterval = GetPreviousInterval();
+  const nsSMILInstanceTime* cutoff = mCurrentInterval ?
+      mCurrentInterval->Begin() :
+      prevInterval ? prevInterval->Begin() : nsnull;
+  InstanceTimeComparator cmp;
+  PRUint32 count = aList.Length();
+  for (PRUint32 i = 0; i < count; ++i) {
+    nsSMILInstanceTime* instance = aList[i].get();
+    if (!cutoff || cmp.LessThan(cutoff, instance)) {
+      instance->UnmarkShouldPreserve();
+    }
+  }
+}
+
+void
+nsSMILTimedElement::FilterHistory()
+{
+  // We should filter the intervals first, since instance times still used in an
+  // interval won't be filtered.
+  FilterIntervals();
+  FilterInstanceTimes(mBeginInstances);
+  FilterInstanceTimes(mEndInstances);
+}
+
+void
+nsSMILTimedElement::FilterIntervals()
+{
+  // We can filter old intervals that:
+  //
+  // a) are not the previous interval; AND
+  // b) are not in the middle of a dependency chain
+  //
+  // Condition (a) is necessary since the previous interval is used for applying
+  // fill effects and updating the current interval.
+  //
+  // Condition (b) is necessary since even if this interval itself is not
+  // active, it may be part of a dependency chain that includes active
+  // intervals. Such chains are used to establish priorities within the
+  // animation sandwich.
+  //
+  // Although the above conditions allow us to safely filter intervals for most
+  // scenarios they do not cover all cases and there will still be scenarios
+  // that generate intervals indefinitely. In such a case we simply set
+  // a maximum number of intervals and drop any intervals beyond that threshold.
+
+  PRUint32 threshold = mOldIntervals.Length() > sMaxNumIntervals ?
+                       mOldIntervals.Length() - sMaxNumIntervals :
+                       0;
+  IntervalList filteredList;
+  for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i)
+  {
+    nsSMILInterval* interval = mOldIntervals[i].get();
+    if (i + 1 < mOldIntervals.Length() /*skip previous interval*/ &&
+        (i < threshold || !interval->IsDependencyChainLink())) {
+      interval->Unlink(PR_TRUE /*filtered, not deleted*/);
+    } else {
+      filteredList.AppendElement(mOldIntervals[i].forget());
+    }
+  }
+  mOldIntervals.Clear();
+  mOldIntervals.SwapElements(filteredList);
+}
+
+namespace
+{
+  class RemoveFiltered
+  {
+  public:
+    RemoveFiltered(nsSMILTimeValue aCutoff) : mCutoff(aCutoff) { }
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
+    {
+      // We can filter instance times that:
+      // a) Precede the end point of the previous interval; AND
+      // b) Are NOT syncbase times that might be updated to a time after the end
+      //    point of the previous interval; AND
+      // c) Are NOT fixed end points in any remaining interval.
+      return aInstanceTime->Time() < mCutoff &&
+             aInstanceTime->IsFixedTime() &&
+             !aInstanceTime->ShouldPreserve();
+    }
+
+  private:
+    nsSMILTimeValue mCutoff;
+  };
+
+  class RemoveBelowThreshold
+  {
+  public:
+    RemoveBelowThreshold(PRUint32 aThreshold,
+                         const nsSMILInstanceTime* aCurrentIntervalBegin)
+      : mThreshold(aThreshold),
+        mCurrentIntervalBegin(aCurrentIntervalBegin) { }
+    PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 aIndex)
+    {
+      return aInstanceTime != mCurrentIntervalBegin && aIndex < mThreshold;
+    }
+
+  private:
+    PRUint32 mThreshold;
+    const nsSMILInstanceTime* mCurrentIntervalBegin;
+  };
+}
+
+void
+nsSMILTimedElement::FilterInstanceTimes(InstanceTimeList& aList)
+{
+  if (GetPreviousInterval()) {
+    RemoveFiltered removeFiltered(GetPreviousInterval()->End()->Time());
+    RemoveInstanceTimes(aList, removeFiltered);
+  }
+
+  // As with intervals it is possible to create a document that, even despite
+  // our most aggressive filtering, will generate instance times indefinitely
+  // (e.g. cyclic dependencies with TimeEvents---we can't filter such times as
+  // they're unpredictable due to the possibility of seeking the document which
+  // may prevent some events from being generated). Therefore we introduce
+  // a hard cutoff at which point we just drop the oldest instance times.
+  if (aList.Length() > sMaxNumInstanceTimes) {
+    PRUint32 threshold = aList.Length() - sMaxNumInstanceTimes;
+    // We should still preserve the current interval begin time however
+    const nsSMILInstanceTime* currentIntervalBegin = mCurrentInterval ?
+      mCurrentInterval->Begin() : nsnull;
+    RemoveBelowThreshold removeBelowThreshold(threshold, currentIntervalBegin);
+    RemoveInstanceTimes(aList, removeBelowThreshold);
   }
 }
 
 //
 // This method is based on the pseudocode given in the SMILANIM spec.
 //
 // See:
 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LC-Start
@@ -1114,18 +1439,16 @@ nsSMILTimedElement::GetNextInterval(cons
     // Calculate begin time
     if (aFixedBeginTime) {
       if (aFixedBeginTime->Time() < beginAfter)
         return NS_ERROR_FAILURE;
       // our ref-counting is not const-correct
       tempBegin = const_cast<nsSMILInstanceTime*>(aFixedBeginTime);
     } else if (!mBeginSpecSet && beginAfter <= zeroTime) {
       tempBegin = new nsSMILInstanceTime(nsSMILTimeValue(0));
-      if (!tempBegin)
-        return NS_ERROR_OUT_OF_MEMORY;
     } else {
       PRInt32 beginPos = 0;
       tempBegin = GetNextGreaterOrEqual(mBeginInstances, beginAfter, beginPos);
       if (!tempBegin || !tempBegin->Time().IsResolved())
         return NS_ERROR_FAILURE;
     }
     NS_ABORT_IF_FALSE(tempBegin && tempBegin->Time().IsResolved() && 
         tempBegin->Time() >= beginAfter,
@@ -1161,18 +1484,16 @@ nsSMILTimedElement::GetNextInterval(cons
 
       nsSMILTimeValue intervalEnd = tempEnd
                                   ? tempEnd->Time() : nsSMILTimeValue();
       nsSMILTimeValue activeEnd = CalcActiveEnd(tempBegin->Time(), intervalEnd);
 
       if (!tempEnd || intervalEnd != activeEnd) {
         tempEnd = new nsSMILInstanceTime(activeEnd);
       }
-      if (!tempEnd)
-        return NS_ERROR_OUT_OF_MEMORY;
     }
     NS_ABORT_IF_FALSE(tempEnd, "Failed to get end point for next interval");
 
     // If we get two zero-length intervals in a row we will potentially have an
     // infinite loop so we break it here by searching for the next begin time
     // greater than tempEnd on the next time around.
     if (tempEnd->Time().IsResolved() && tempBegin->Time() == tempEnd->Time()) {
       if (prevIntervalWasZeroDur) {
@@ -1328,16 +1649,19 @@ nsSMILTimedElement::ApplyMinAndMax(const
 nsSMILTime
 nsSMILTimedElement::ActiveTimeToSimpleTime(nsSMILTime aActiveTime,
                                            PRUint32& aRepeatIteration)
 {
   nsSMILTime result;
 
   NS_ASSERTION(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(),
       "Unresolved simple duration in ActiveTimeToSimpleTime");
+  NS_ASSERTION(aActiveTime >= 0, "Expecting non-negative active time");
+  // Note that a negative aActiveTime will give us a negative value for
+  // aRepeatIteration, which is bad because aRepeatIteration is unsigned
 
   if (mSimpleDur.IsIndefinite() || mSimpleDur.GetMillis() == 0L) {
     aRepeatIteration = 0;
     result = aActiveTime;
   } else {
     result = aActiveTime % mSimpleDur.GetMillis();
     aRepeatIteration = (PRUint32)(aActiveTime / mSimpleDur.GetMillis());
   }
@@ -1404,20 +1728,16 @@ nsSMILTimedElement::UpdateCurrentInterva
 
   if (NS_SUCCEEDED(rv)) {
 
     if (mElementState == STATE_POSTACTIVE) {
 
       NS_ABORT_IF_FALSE(!mCurrentInterval,
           "In postactive state but the interval has been set");
       mCurrentInterval = new nsSMILInterval(updatedInterval);
-      if (!mCurrentInterval) {
-        NS_WARNING("Failed to allocate memory for new interval.");
-        return;
-      }
       mElementState = STATE_WAITING;
       NotifyNewInterval();
 
     } else {
 
       PRBool changed = PR_FALSE;
 
       if (mElementState != STATE_ACTIVE &&
@@ -1445,17 +1765,17 @@ nsSMILTimedElement::UpdateCurrentInterva
       // Only apply a fill if it was already being applied before the (now
       // deleted) interval was created
       PRBool applyFill = HasPlayed() && mFillMode == FILL_FREEZE;
       mClient->Inactivate(applyFill);
     }
 
     if (mElementState == STATE_ACTIVE || mElementState == STATE_WAITING) {
       mElementState = STATE_POSTACTIVE;
-      mCurrentInterval->NotifyDeleting();
+      mCurrentInterval->Unlink();
       mCurrentInterval = nsnull;
     }
   }
 }
 
 void
 nsSMILTimedElement::SampleSimpleTime(nsSMILTime aActiveTime)
 {
@@ -1472,19 +1792,19 @@ nsSMILTimedElement::SampleFillValue()
 {
   if (mFillMode != FILL_FREEZE || !mClient)
     return;
 
   const nsSMILInterval* prevInterval = GetPreviousInterval();
   NS_ABORT_IF_FALSE(prevInterval,
       "Attempting to sample fill value but there is no previous interval");
   NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsResolved() &&
-      !prevInterval->End()->MayUpdate(),
+      prevInterval->End()->IsFixedTime(),
       "Attempting to sample fill value but the endpoint of the previous "
-      "interval is not resolved and frozen");
+      "interval is not resolved and fixed");
 
   nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() -
                           prevInterval->Begin()->Time().GetMillis();
 
   PRUint32 repeatIteration;
   nsSMILTime simpleTime =
     ActiveTimeToSimpleTime(activeTime, repeatIteration);
 
@@ -1503,20 +1823,16 @@ nsSMILTimedElement::AddInstanceTimeFromC
   nsSMILTime timeWithOffset = aCurrentTime + PRInt64(NS_round(offset));
 
   nsSMILTimeValue timeVal(timeWithOffset);
 
   // XXX If we re-use this method for event-based timing we'll need to change it
   // so we don't end up setting SOURCE_DOM for event-based times.
   nsRefPtr<nsSMILInstanceTime> instanceTime =
     new nsSMILInstanceTime(timeVal, nsSMILInstanceTime::SOURCE_DOM);
-  if (!instanceTime) {
-    NS_WARNING("Insufficient memory to create instance time");
-    return;
-  }
 
   AddInstanceTime(instanceTime, aIsBegin);
 }
 
 void
 nsSMILTimedElement::RegisterMilestone()
 {
   nsSMILTimeContainer* container = GetTimeContainer();
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -224,20 +224,24 @@ public:
    * Informs the timed element that its time container has changed time
    * relative to document time. The timed element therefore needs to update its
    * dependent elements (which may belong to a different time container) so they
    * can re-resolve their times.
    */
   void HandleContainerTimeChange();
 
   /**
-   * Reset the element's internal state. As described in SMILANIM 3.3.7, all
-   * instance times associated with DOM calls, events, etc. are cleared.
+   * Resets this timed element's accumulated times and intervals back to start
+   * up state.
+   *
+   * This is used for backwards seeking where rather than accumulating
+   * historical timing state and winding it back, we reset the element and seek
+   * forwards.
    */
-  void Reset();
+  void Rewind();
 
   /**
    * Attempts to set an attribute on this timed element.
    *
    * @param aAttribute  The name of the attribute to set. The namespace of this
    *                    attribute is not specified as it is checked by the host
    *                    element. Only attributes in the namespace defined for
    *                    SMIL attributes in the host language are passed to the
@@ -340,16 +344,20 @@ protected:
                       const nsSMILInstanceTime* aElem2) const;
   };
 
   struct NotifyTimeDependentsParams {
     nsSMILInterval*      mCurrentInterval;
     nsSMILTimeContainer* mTimeContainer;
   };
 
+  // Templated helper functions
+  template <class TestFunctor>
+  void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest);
+
   //
   // Implementation helpers
   //
 
   nsresult          SetBeginSpec(const nsAString& aBeginSpec,
                                  nsIContent* aContextNode);
   nsresult          SetEndSpec(const nsAString& aEndSpec,
                                nsIContent* aContextNode);
@@ -370,19 +378,69 @@ protected:
   void              UnsetRepeatCount();
   void              UnsetRepeatDur();
   void              UnsetFillMode();
 
   nsresult          SetBeginOrEndSpec(const nsAString& aSpec,
                                       nsIContent* aContextNode,
                                       PRBool aIsBegin);
   void              ClearBeginOrEndSpecs(PRBool aIsBegin);
+  void              RewindTiming();
+  void              RewindInstanceTimes(InstanceTimeList& aList);
   void              DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly);
 
   /**
+   * Helper function to check for an early end and, if necessary, update the
+   * current interval accordingly.
+   *
+   * See SMIL 3.0, section 5.4.5, Element life cycle, "Active Time - Playing an
+   * interval" for a description of ending early.
+   *
+   * @param aSampleTime The current sample time. Early ends should only be
+   *                    applied at the last possible moment (i.e. if they are at
+   *                    or before the current sample time) and only if the
+   *                    current interval is not already ending.
+   */
+  void ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime);
+
+  /**
+   * Clears certain state in response to the element restarting.
+   *
+   * This state is described in SMIL 3.0, section 5.4.3, Resetting element state
+   */
+  void Reset();
+
+  /**
+   * Completes a seek operation by sending appropriate events and, in the case
+   * of a backwards seek, updating the state of timing information that was
+   * previously considered historical.
+   */
+  void DoPostSeek();
+
+  /**
+   * Unmarks instance times that were previously preserved because they were
+   * considered important historical milestones but are no longer such because
+   * a backwards seek has been performed.
+   */
+  void UnpreserveInstanceTimes(InstanceTimeList& aList);
+
+  /**
+   * Helper function to iterate through this element's accumulated timing
+   * information (specifically old nsSMILIntervals and nsSMILTimeInstanceTimes)
+   * and discard items that are no longer needed or exceed some threshold of
+   * accumulated state.
+   */
+  void FilterHistory();
+
+  // Helper functions for FilterHistory to clear old nsSMILIntervals and
+  // nsSMILInstanceTimes respectively.
+  void FilterIntervals();
+  void FilterInstanceTimes(InstanceTimeList& aList);
+
+  /**
    * Calculates the next acceptable interval for this element after the
    * specified interval, or, if no previous interval is specified, it will be
    * the first interval with an end time after t=0.
    *
    * @see SMILANIM 3.6.8
    *
    * @param aPrevInterval   The previous interval used. If supplied, the first
    *                        interval that begins after aPrevInterval will be
@@ -478,16 +536,18 @@ protected:
   InstanceTimeList                mEndInstances;
   PRUint32                        mInstanceSerialIndex;
 
   nsSMILAnimationFunction*        mClient;
   nsAutoPtr<nsSMILInterval>       mCurrentInterval;
   IntervalList                    mOldIntervals;
   nsSMILMilestone                 mPrevRegisteredMilestone;
   static const nsSMILMilestone    sMaxMilestone;
+  static const PRUint8            sMaxNumIntervals;
+  static const PRUint8            sMaxNumInstanceTimes;
 
   // Set of dependent time value specs to be notified when establishing a new
   // current interval. Change notifications and delete notifications are handled
   // by the interval.
   //
   // [weak] The nsSMILTimeValueSpec objects register themselves and unregister
   // on destruction. Likewise, we notify them when we are destroyed.
   TimeValueSpecHashSet mTimeDependents;
@@ -499,11 +559,21 @@ protected:
   enum nsSMILElementState
   {
     STATE_STARTUP,
     STATE_WAITING,
     STATE_ACTIVE,
     STATE_POSTACTIVE
   };
   nsSMILElementState              mElementState;
+
+  enum nsSMILSeekState
+  {
+    SEEK_NOT_SEEKING,
+    SEEK_FORWARD_FROM_ACTIVE,
+    SEEK_FORWARD_FROM_INACTIVE,
+    SEEK_BACKWARD_FROM_ACTIVE,
+    SEEK_BACKWARD_FROM_INACTIVE
+  };
+  nsSMILSeekState                 mSeekState;
 };
 
 #endif // NS_SMILTIMEDELEMENT_H_
--- a/content/smil/test/Makefile.in
+++ b/content/smil/test/Makefile.in
@@ -52,16 +52,17 @@ include $(topsrcdir)/config/rules.mk
 	  db_smilCSSPropertyList.js \
 	  db_smilMappedAttrList.js \
 	  smilAnimateMotionValueLists.js \
 	  smilTestUtils.js \
 	  smilXHR_helper.svg \
 	  test_smilAnimateMotion.xhtml \
 	  test_smilAnimateMotionInvalidValues.xhtml \
 	  test_smilAnimateMotionOverrideRules.xhtml \
+	  test_smilBackwardsSeeking.xhtml \
 	  test_smilChangeAfterFrozen.xhtml \
 	  test_smilContainerBinding.xhtml \
 	  test_smilCrossContainer.xhtml \
 	  test_smilCSSFontStretchRelative.xhtml \
 	  test_smilCSSFromBy.xhtml \
 	  test_smilCSSFromTo.xhtml \
 	  test_smilCSSInherit.xhtml \
 	  test_smilCSSInvalidValues.xhtml \
new file mode 100644
--- /dev/null
+++ b/content/smil/test/test_smilBackwardsSeeking.xhtml
@@ -0,0 +1,191 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for backwards seeking behavior </title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" />
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test for backwards seeking behavior  **/
+
+var gSvg = document.getElementById("svg");
+
+SimpleTest.waitForExplicitFinish();
+
+function main()
+{
+  // Pause our document, so that the setCurrentTime calls are the only
+  // thing affecting document time
+  gSvg.pauseAnimations();
+
+  // We define a series of scenarios, sample times, and expected return values
+  // from getStartTime.
+  //
+  // Each scenario is basically a variation on the following arrangement:
+  //
+  // <svg>
+  //   <set ... dur="1s" begin="<A-BEGIN>"/>
+  //   <set ... dur="1s" begin="<B-BEGIN>"/>
+  // </svg>
+  //
+  // Each test then consists of the following:
+  //    animA: attributes to be applied to a
+  //    animB: attributes to be applied to b
+  //    times: a series of triples which consist of:
+  //             <sample time, a's expected start time, b's expected start time>
+  //           * The sample time is the time passed to setCurrentTime and so is
+  //             in seconds.
+  //           * The expected start times are compared with the return value of
+  //             getStartTime. To check for an unresolved start time where
+  //             getStartTime would normally throw an exception use
+  //             'unresolved'.
+  //           * We also allow the special notation to indicate a call to
+  //             beginElement
+  //             <'beginElementAt', id of animation element, offset>
+  //
+  // In the diagrams below '^' means the time before the seek and '*' is the
+  // seek time.
+  var testCases = Array();
+
+  // 0: Simple case
+  //
+  //   A:     +-------
+  //   B:     +-------       begin: a.begin
+  //        *            ^
+  testCases[0] = {
+    'animA': {'begin':'1s', 'id':'a'},
+    'animB': {'begin':'a.begin'},
+    'times': [ [0, 1, 1],
+               [1, 1, 1],
+               [2, 'unresolved', 'unresolved'],
+               [0, 1, 1],
+               [1.5, 1, 1],
+               [1, 1, 1],
+               [2, 'unresolved', 'unresolved'] ]
+  };
+
+  // 1: Restored times should be live
+  //
+  // When we restore times they should be live. So we have the following
+  // scenario.
+  //
+  //   A:     +-------
+  //   B:     +-------       begin: a.begin
+  //       *            ^
+  //
+  // Then we call beginElement at an earlier time which should give us the
+  // following.
+  //
+  //   A:   +-------
+  //   B:   +-------
+  //       *            ^
+  //
+  //  If the times are not live however we'll end up with this
+  //
+  //   A:   +-------
+  //   B:   +-+-------
+  //       *            ^
+  testCases[1] = {
+    'animA': {'begin':'1s', 'id':'a', 'restart':'whenNotActive'},
+    'animB': {'begin':'a.begin', 'restart':'always'},
+    'times': [ [0, 1, 1],
+               [2, 'unresolved', 'unresolved'],
+               [0.25, 1, 1],
+               ['beginElementAt', 'a', 0.25], // = start time of 0.5
+               [0.25, 0.5, 0.5],
+               [1, 0.5, 0.5],
+               [1.5, 'unresolved', 'unresolved'] ]
+  };
+
+  // 2: Multiple intervals A
+  //
+  //   A:  +-  +-
+  //   B:          +-  +-   begin: a.begin+4s
+  //             *    ^
+  testCases[2] = {
+    'animA': {'begin':'1s; 3s', 'id':'a'},
+    'animB': {'begin':'a.begin+4s'},
+    'times': [ [0, 1, 5],
+               [3, 3, 5],
+               [6.5, 'unresolved', 7],
+               [4, 'unresolved', 5],
+               [6, 'unresolved', 7],
+               [2, 3, 5],
+               ['beginElementAt', 'a', 0],
+               [2, 2, 5],
+               [5, 'unresolved', 5],
+               [6, 'unresolved', 6],
+               [7, 'unresolved', 7],
+               [8, 'unresolved', 'unresolved'] ]
+  };
+
+  for (var i = 0; i < testCases.length; i++) {
+    gSvg.setCurrentTime(0);
+    var test = testCases[i];
+
+    // Create animation elements
+    var animA = createAnim(test.animA);
+    var animB = createAnim(test.animB);
+
+    // Run samples
+    for (var j = 0; j < test.times.length; j++) {
+      var times = test.times[j];
+      if (times[0] == 'beginElementAt') {
+        var anim = getElement(times[1]);
+        anim.beginElementAt(times[2]);
+      } else {
+        gSvg.setCurrentTime(times[0]);
+        checkStartTime(animA, times[1], times[0], i, 'a');
+        checkStartTime(animB, times[2], times[0], i, 'b');
+      }
+    }
+
+    // Tidy up
+    removeElement(animA);
+    removeElement(animB);
+  }
+
+  SimpleTest.finish();
+}
+
+function createAnim(attr)
+{
+  const svgns = "http://www.w3.org/2000/svg";
+  var anim = document.createElementNS(svgns, 'set');
+  anim.setAttribute('attributeName','x');
+  anim.setAttribute('to','10');
+  anim.setAttribute('dur','1s');
+  for (name in attr) {
+    anim.setAttribute(name, attr[name]);
+  }
+  return gSvg.appendChild(anim);
+}
+
+function checkStartTime(anim, expectedStartTime, sampleTime, caseNum, id)
+{
+  var startTime = 'unresolved';
+  try {
+    startTime = anim.getStartTime();
+  } catch (e) {
+    if (e.code != DOMException.INVALID_STATE_ERR)
+      throw e;
+  }
+
+  var msg = "Test case " + caseNum + ", t=" + sampleTime + " animation '" +
+    id + "': Unexpected getStartTime:";
+  is(startTime, expectedStartTime, msg);
+}
+
+window.addEventListener("load", main, false);
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/smil/test/test_smilContainerBinding.xhtml
+++ b/content/smil/test/test_smilContainerBinding.xhtml
@@ -55,21 +55,21 @@ function main() {
   svg.setCurrentTime(1);
   is(anim.getStartTime(), 2);
 
   // Unbind
   removeElement(anim);
   is(anim.getStartTime(), 6);
 
   // Rebind
-  // At this point all the old intervals should be re-added to anim. If they're
-  // not and only the current interval is added to anim we'll get a start time
-  // of 4s instead of 2s.
+  // At this point only the current interval will be re-added to anim (this is
+  // for consistency since old intervals may or may not have been filtered).
+  // Therefore the start time should be 4s instead of 2s.
   circle.appendChild(anim);
-  is(anim.getStartTime(), 2);
+  is(anim.getStartTime(), 4);
 
   SimpleTest.finish();
 }
 
 function createAnim() {
   const svgns="http://www.w3.org/2000/svg";
   var anim = document.createElementNS(svgns,'set');
   anim.setAttribute('attributeName','cx');
--- a/content/smil/test/test_smilSetCurrentTime.xhtml
+++ b/content/smil/test/test_smilSetCurrentTime.xhtml
@@ -26,17 +26,18 @@ SimpleTest.waitForExplicitFinish();
 
 function main() {
   ok(gSvg.animationsPaused(), "should be paused by <svg> load handler");
   is(gSvg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
 
   // Test that seeking takes effect immediately
   for (var i = 0; i < gTimes.length; i++) {
     gSvg.setCurrentTime(gTimes[i]);
-    assertFloatsEqual(gSvg.getCurrentTime(), gTimes[i]);
+    // We adopt the SVGT1.2 behavior of clamping negative times to 0
+    assertFloatsEqual(gSvg.getCurrentTime(), Math.max(gTimes[i], 0.0));
   }
 
   // Test that seeking isn't messed up by timeouts
   // (using tail recursion to set up the chain of timeout function calls)
   var func = function() {
     checkTimesAfterIndex(0);
   }
   setTimeout(func, gWaitTime);
@@ -51,17 +52,17 @@ function checkTimesAfterIndex(index) {
   if (index == gTimes.length) {
     // base case -- we're done!
     SimpleTest.finish();
     return;
   }
 
   gSvg.setCurrentTime(gTimes[index]);
   var func = function() {
-    assertFloatsEqual(gSvg.getCurrentTime(), gTimes[index]);
+    assertFloatsEqual(gSvg.getCurrentTime(), Math.max(gTimes[index], 0.0));
     checkTimesAfterIndex(index + 1);
   }
   setTimeout(func, gWaitTime);
 }
 
 function assertFloatsEqual(aVal, aExpected) {
   ok(Math.abs(aVal - aExpected) <= PRECISION_LEVEL,
      "getCurrentTime returned " + aVal + " after seeking to " + aExpected)
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1948,23 +1948,27 @@ nsSVGElement::GetAnimatedAttr(nsIAtom* a
     if (!transformable)
       return nsnull;
     nsresult rv = transformable->GetTransform(getter_AddRefs(transformList));
     NS_ENSURE_SUCCESS(rv, nsnull);
   }
   if (aName == nsGkAtoms::gradientTransform) {
     nsCOMPtr<nsIDOMSVGGradientElement> gradientElement(
             do_QueryInterface(static_cast<nsIContent*>(this)));
+    if (!gradientElement)
+      return nsnull;
 
     nsresult rv = gradientElement->GetGradientTransform(getter_AddRefs(transformList));
     NS_ENSURE_SUCCESS(rv, nsnull);
   }
   if (aName == nsGkAtoms::patternTransform) {
     nsCOMPtr<nsIDOMSVGPatternElement> patternElement(
             do_QueryInterface(static_cast<nsIContent*>(this)));
+    if (!patternElement)
+      return nsnull;
 
     nsresult rv = patternElement->GetPatternTransform(getter_AddRefs(transformList));
     NS_ENSURE_SUCCESS(rv, nsnull);
   }
   if (transformList) {
     nsSVGAnimatedTransformList* list
       = static_cast<nsSVGAnimatedTransformList*>(transformList.get());
     NS_ENSURE_TRUE(list, nsnull);
--- a/content/svg/content/src/nsSVGPointList.cpp
+++ b/content/svg/content/src/nsSVGPointList.cpp
@@ -164,49 +164,59 @@ NS_INTERFACE_MAP_END
 NS_IMETHODIMP
 nsSVGPointList::SetValueString(const nsAString& aValue)
 {
   nsCharSeparatedTokenizer
     tokenizer(aValue, ',',
               nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
   nsCOMArray<nsIDOMSVGPoint> points;
 
+  PRBool parseError = PR_FALSE;
+
   while (tokenizer.hasMoreTokens()) {
     // Parse 2 tokens
     NS_ConvertUTF16toUTF8 utf8String1(tokenizer.nextToken());
     const char *token1 = utf8String1.get();
     if (!tokenizer.hasMoreTokens() ||  // No 2nd token.
         *token1 == '\0') {             // 1st token is empty string.
-      return NS_ERROR_DOM_SYNTAX_ERR;
+      parseError = PR_TRUE;
+      break;
     }
     NS_ConvertUTF16toUTF8 utf8String2(tokenizer.nextToken());
     const char *token2 = utf8String2.get();
     if (*token2 == '\0') {             // 2nd token is empty string.
-      return NS_ERROR_DOM_SYNTAX_ERR;
+      parseError = PR_TRUE;
+      break;
     }
 
     // Convert parsed tokens to float values.
     char *end;
     float x = float(PR_strtod(token1, &end));
     if (*end != '\0' || !NS_FloatIsFinite(x)) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
+      parseError = PR_TRUE;
+      break;
     }
     float y = float(PR_strtod(token2, &end));
     if (*end != '\0' || !NS_FloatIsFinite(y)) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
+      parseError = PR_TRUE;
+      break;
     }
 
     // Build a point from our parsed float values.
     nsCOMPtr<nsIDOMSVGPoint> point;
     NS_NewSVGPoint(getter_AddRefs(point), x, y); // uses infallible 'new'.
     points.AppendObject(point);
   }
 
   if (tokenizer.lastTokenEndedWithSeparator()) { // Reject trailing comma
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    parseError = PR_TRUE;
+  }
+
+  if (parseError) {
+    // XXX nsSVGUtils::ReportToConsole()
   }
 
   WillModify();
   ReleasePoints();
   PRInt32 count = points.Count();
   for (PRInt32 i = 0; i < count; ++i) {
     AppendElement(points.ObjectAt(i));
   }
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=79: */
 /* ***** 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/
  *
@@ -622,16 +623,19 @@ nsBindingManager::SetWrappedJS(nsIConten
 }
 
 void
 nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
                                               nsIDocument* aOldDocument)
 {
   NS_PRECONDITION(aOldDocument != nsnull, "no old document");
 
+  if (mDestroyed)
+    return;
+
   // Hold a ref to the binding so it won't die when we remove it from our
   // table.
   nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
   if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
     nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContent->GetBindingParent());
     if (parentBinding) {
       parentBinding->RemoveInsertionParent(aContent);
       // Clear insertion parent only if we don't have a binding which
@@ -1645,16 +1649,18 @@ nsBindingManager::ContentRemoved(nsIDocu
       }
     }
   }
 }
 
 void
 nsBindingManager::DropDocumentReference()
 {
+  mDestroyed = PR_TRUE;
+
   // Make sure to not run any more XBL constructors
   mProcessingAttachedStack = PR_TRUE;
   if (mProcessAttachedQueueEvent) {
     mProcessAttachedQueueEvent->Revoke();
   }
 
   if (mContentListTable.ops)
     PL_DHashTableFinish(&(mContentListTable));
@@ -1663,16 +1669,19 @@ nsBindingManager::DropDocumentReference(
   if (mAnonymousNodesTable.ops)
     PL_DHashTableFinish(&(mAnonymousNodesTable));
   mAnonymousNodesTable.ops = nsnull;
 
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&(mInsertionParentTable));
   mInsertionParentTable.ops = nsnull;
 
+  if (mBindingTable.IsInitialized())
+    mBindingTable.Clear();
+
   mDocument = nsnull;
 }
 
 void
 nsBindingManager::Traverse(nsIContent *aContent,
                            nsCycleCollectionTraversalCallback &cb)
 {
   if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
--- a/content/xbl/src/nsXBLContentSink.cpp
+++ b/content/xbl/src/nsXBLContentSink.cpp
@@ -66,18 +66,17 @@ using namespace mozilla::dom;
 nsresult
 NS_NewXBLContentSink(nsIXMLContentSink** aResult,
                      nsIDocument* aDoc,
                      nsIURI* aURI,
                      nsISupports* aContainer)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
-  nsXBLContentSink* it;
-  NS_NEWXPCOM(it, nsXBLContentSink);
+  nsXBLContentSink* it = new nsXBLContentSink();
   NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
 
   nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
   nsresult rv = it->Init(aDoc, aURI, aContainer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return CallQueryInterface(it, aResult);
 }
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -258,17 +258,17 @@ nsXBLDocGlobalObject::SetContext(nsIScri
   NS_ASSERTION(aScriptContext->GetScriptTypeID() ==
                                         nsIProgrammingLanguage::JAVASCRIPT,
                "xbl is not multi-language");
   aScriptContext->WillInitializeContext();
   // NOTE: We init this context with a NULL global, so we automatically
   // hook up to the existing nsIScriptGlobalObject global setup by
   // nsGlobalWindow.
   nsresult rv;
-  rv = aScriptContext->InitContext(nsnull);
+  rv = aScriptContext->InitContext();
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Script Language's InitContext failed");
   aScriptContext->SetGCOnDestruction(PR_FALSE);
   aScriptContext->DidInitializeContext();
   // and we set up our global manually
   mScriptContext = aScriptContext;
 }
 
 nsresult
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -119,18 +119,17 @@ NS_NewXMLContentSink(nsIXMLContentSink**
                      nsIURI* aURI,
                      nsISupports* aContainer,
                      nsIChannel* aChannel)
 {
   NS_PRECONDITION(nsnull != aResult, "null ptr");
   if (nsnull == aResult) {
     return NS_ERROR_NULL_POINTER;
   }
-  nsXMLContentSink* it;
-  NS_NEWXPCOM(it, nsXMLContentSink);
+  nsXMLContentSink* it = new nsXMLContentSink();
   if (nsnull == it) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   
   nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
   nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
   
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -679,17 +679,17 @@ nsXULPDGlobalObject::SetScriptContext(PR
 
   if (!aScriptContext)
     NS_WARNING("Possibly early removal of script object, see bug #41608");
   else {
     // should probably assert the context is clean???
     aScriptContext->WillInitializeContext();
     // NOTE: We init this context with a NULL global - this is subtly
     // different than nsGlobalWindow which passes 'this'
-    rv = aScriptContext->InitContext(nsnull);
+    rv = aScriptContext->InitContext();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ASSERTION(!aScriptContext || !mScriptContexts[lang_ndx],
                "Bad call to SetContext()!");
 
   void *script_glob = nsnull;
 
--- a/db/mork/build/nsMorkFactory.cpp
+++ b/db/mork/build/nsMorkFactory.cpp
@@ -31,20 +31,18 @@
  * 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 "nsIServiceManager.h"
+#include "mozilla/ModuleUtils.h"
 #include "nsCOMPtr.h"
-#include "nsIModule.h"
-#include "nsIGenericFactory.h"
 #include "nsMorkCID.h"
 #include "nsIMdbFactoryFactory.h"
 #include "mdb.h"
 
 class nsMorkFactoryService : public nsIMdbFactoryService
 {
 public:
   nsMorkFactoryService() {};
@@ -54,26 +52,35 @@ public:
   NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory);
 
 protected:
   nsCOMPtr<nsIMdbFactory> mMdbFactory;
 };
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkFactoryService)
 
-static const nsModuleComponentInfo components[] =
-{
-    { "Mork Factory Service", 
-      NS_MORK_CID, 
-      NS_MORK_CONTRACTID,
-      nsMorkFactoryServiceConstructor 
-    }
+NS_DEFINE_NAMED_CID(NS_MORK_CID);
+
+const mozilla::Module::CIDEntry kMorkCIDs[] = {
+  { &kNS_MORK_CID, false, NULL, nsMorkFactoryServiceConstructor },
+  { NULL }
 };
 
-NS_IMPL_NSGETMODULE(nsMorkModule, components)
+const mozilla::Module::ContractIDEntry kMorkContracts[] = {
+  { NS_MORK_CONTRACTID, &kNS_MORK_CID },
+  { NULL }
+};
+
+static const mozilla::Module kMorkModule = {
+  mozilla::Module::kVersion,
+  kMorkCIDs,
+  kMorkContracts
+};
+
+NSMODULE_DEFN(nsMorkModule) = &kMorkModule;
 
 NS_IMPL_ISUPPORTS1(nsMorkFactoryService, nsIMdbFactoryService)
 
 NS_IMETHODIMP nsMorkFactoryService::GetMdbFactory(nsIMdbFactory **aFactory)
 {
   if (!mMdbFactory)
     mMdbFactory = MakeMdbFactory();
   NS_IF_ADDREF(*aFactory = mMdbFactory);
--- a/docshell/base/IHistory.h
+++ b/docshell/base/IHistory.h
@@ -38,26 +38,25 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_IHistory_h_
 #define mozilla_IHistory_h_
 
 #include "nsISupports.h"
 
 class nsIURI;
-class nsString;
 
 namespace mozilla {
 
     namespace dom {
         class Link;
     }
 
 #define IHISTORY_IID \
-  {0x6f736049, 0x6370, 0x4376, {0xb7, 0x17, 0xfa, 0xfc, 0x0b, 0x4f, 0xd0, 0xf1}}
+  {0xaf27265d, 0x5672, 0x4d23, {0xa0, 0x75, 0x34, 0x8e, 0xb9, 0x73, 0x5a, 0x9a}}
 
 class IHistory : public nsISupports
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(IHISTORY_IID)
 
     /**
      * Registers the Link for notifications about the visited-ness of aURI.
@@ -92,69 +91,21 @@ public:
      *
      * @param aURI
      *        The URI that aLink was registered for.
      * @param aLink
      *        The link object to unregister for aURI.
      */
     NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, dom::Link *aLink) = 0;
 
-    enum VisitFlags {
-        /**
-         * Indicates whether the URI was loaded in a top-level window.
-         */
-        TOP_LEVEL = 1 << 0,
-        /**
-         * Indicates whether the URI was loaded as part of a permanent redirect.
-         */
-        REDIRECT_PERMANENT = 1 << 1,
-        /**
-         * Indicates whether the URI was loaded as part of a temporary redirect.
-         */
-        REDIRECT_TEMPORARY = 1 << 2
-    };
-
-    /**
-     * Adds a history visit for the URI.
-     *
-     * @pre aURI must not be null.
-     *
-     * @param aURI
-     *        The URI of the page being visited.
-     * @param aLastVisitedURI
-     *        The URI of the last visit in the chain.
-     * @param aFlags
-     *        The VisitFlags describing this visit.
-     */
-    NS_IMETHOD VisitURI(
-        nsIURI *aURI,
-        nsIURI *aLastVisitedURI,
-        PRUint32 aFlags
-    ) = 0;
-
-    /**
-     * Set the title of the URI.
-     *
-     * @pre aURI must not be null.
-     *
-     * @param aURI
-     *        The URI to set the title for.
-     * @param aTitle
-     *        The title string.
-     */
-    NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(IHistory, IHISTORY_IID)
 
 #define NS_DECL_IHISTORY \
     NS_IMETHOD RegisterVisitedCallback(nsIURI *aURI, \
                                        mozilla::dom::Link *aContent); \
     NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, \
-                                         mozilla::dom::Link *aContent); \
-    NS_IMETHOD VisitURI(nsIURI *aURI, \
-                        nsIURI *aLastVisitedURI, \
-                        PRUint32 aFlags); \
-    NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle);
+                                         mozilla::dom::Link *aContent);
 
 } // namespace mozilla
 
 #endif // mozilla_IHistory_h_
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -107,18 +107,16 @@
 #include "nsDOMJSUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScriptChannel.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsCPrefetchService.h"
 #include "nsJSON.h"
-#include "IHistory.h"
-#include "mozilla/Services.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"  
 
 
 // Local Includes
@@ -4699,25 +4697,20 @@ nsDocShell::SetTitle(const PRUnichar * a
     // tree owner.
     if (!parent) {
         nsCOMPtr<nsIBaseWindow>
             treeOwnerAsWin(do_QueryInterface(mTreeOwner));
         if (treeOwnerAsWin)
             treeOwnerAsWin->SetTitle(aTitle);
     }
 
-    if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
-        nsCOMPtr<IHistory> history = services::GetHistoryService();
-        if (history) {
-            history->SetURITitle(mCurrentURI, mTitle);
-        }
-        else if (mGlobalHistory) {
-            mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
-        }
-    }
+    if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
+        mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
+    }
+
 
     // Update SessionHistory with the document's title.
     if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
         mLoadType != LOAD_ERROR_PAGE) {
 
         mOSHE->SetTitle(mTitle);    
     }
 
@@ -5674,63 +5667,42 @@ nsDocShell::OnRedirectStateChange(nsICha
                                   PRUint32 aRedirectFlags,
                                   PRUint32 aStateFlags)
 {
     NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
                  "Calling OnRedirectStateChange when there is no redirect");
     if (!(aStateFlags & STATE_IS_DOCUMENT))
         return; // not a toplevel document
 
-    nsCOMPtr<nsIURI> oldURI, newURI;
-    aOldChannel->GetURI(getter_AddRefs(oldURI));
-    aNewChannel->GetURI(getter_AddRefs(newURI));
-    if (!oldURI || !newURI) {
-        return;
-    }
-
-    // Below a URI visit is saved (see AddURIVisit method doc).
-    // The visit chain looks something like:
-    //   ...
-    //   Site N - 1
-    //                =>  Site N
-    //   (redirect to =>) Site N + 1 (we are here!)
-
-    // Get N - 1 and transition type
-    nsCOMPtr<nsIURI> previousURI;
-    PRUint32 previousFlags = 0;
-    ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
-
-    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
-        ChannelIsPost(aOldChannel)) {
-        // 1. Internal redirects are ignored because they are specific to the
-        //    channel implementation.
-        // 2. POSTs are not saved by global history.
-        //
-        // Regardless, we need to propagate the previous visit to the new
-        // channel.
-        SaveLastVisit(aNewChannel, previousURI, previousFlags);
-    }
-    else {
-        nsCOMPtr<nsIURI> referrer;
-        // Treat referrer as null if there is an error getting it.
-        (void)NS_GetReferrerFromChannel(aOldChannel,
-                                        getter_AddRefs(referrer));
-
-        // Add visit N -1 => N
-        AddURIVisit(oldURI, referrer, previousURI, previousFlags);
-
-        // Since N + 1 could be the final destination, we will not save N => N + 1
-        // here.  OnNewURI will do that, so we will cache it.
-        SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
+    nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
+    nsresult result = NS_ERROR_NOT_IMPLEMENTED;
+    if (history3) {
+        // notify global history of this redirect
+        result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
+                                               aRedirectFlags, !IsFrame());
+    }
+
+    if (result == NS_ERROR_NOT_IMPLEMENTED) {
+        // when there is no GlobalHistory3, or it doesn't implement
+        // AddToplevelRedirect, we fall back to GlobalHistory2.  Just notify
+        // that the redirecting page was a rePdirect so it will be link colored
+        // but not visible.
+        nsCOMPtr<nsIURI> oldURI;
+        aOldChannel->GetURI(getter_AddRefs(oldURI));
+        if (! oldURI)
+            return; // nothing to tell anybody about
+        AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel);
     }
 
     // check if the new load should go through the application cache.
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(aNewChannel);
     if (appCacheChannel) {
+        nsCOMPtr<nsIURI> newURI;
+        aNewChannel->GetURI(getter_AddRefs(newURI));
         appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
     }
 
     if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) && 
         mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
         mLoadType = LOAD_NORMAL_REPLACE;
         SetHistoryEntry(&mLSHE, nsnull);
     }
@@ -8960,17 +8932,17 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIC
     PRBool updateHistory = PR_TRUE;
     PRBool equalUri = PR_FALSE;
     PRBool shAvailable = PR_TRUE;  
 
     // Get the post data from the channel
     nsCOMPtr<nsIInputStream> inputStream;
     if (aChannel) {
         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
-
+        
         // Check if the HTTPChannel is hiding under a multiPartChannel
         if (!httpChannel)  {
             GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
         }
 
         if (httpChannel) {
             nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
             if (uploadChannel) {
@@ -9050,18 +9022,18 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIC
         (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
          aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
          aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
         NS_ASSERTION(!updateHistory,
                      "We shouldn't be updating history for forced reloads!");
         
         nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
         nsCOMPtr<nsISupports>  cacheKey;
-        // Get the Cache Key and store it in SH.
-        if (cacheChannel)
+        // Get the Cache Key  and store it in SH.         
+        if (cacheChannel) 
             cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
         // If we already have a loading history entry, store the new cache key
         // in it.  Otherwise, since we're doing a reload and won't be updating
         // our history entry, store the cache key in our current history entry.
         if (mLSHE)
             mLSHE->SetCacheKey(cacheKey);
         else if (mOSHE)
             mOSHE->SetCacheKey(cacheKey);
@@ -9073,32 +9045,20 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIC
             /* This is  a fresh page getting loaded for the first time
              *.Create a Entry for it and add it to SH, if this is the
              * rootDocShell
              */
             (void) AddToSessionHistory(aURI, aChannel, aOwner,
                                        getter_AddRefs(mLSHE));
         }
 
+        // Update Global history
         if (aAddToGlobalHistory) {
-            // If this is a POST request, we do not want to include this in global
-            // history.
-            if (!ChannelIsPost(aChannel)) {
-                nsCOMPtr<nsIURI> previousURI;
-                PRUint32 previousFlags = 0;
-                ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
-                                 &previousFlags);
-
-                nsCOMPtr<nsIURI> referrer;
-                // Treat referrer as null if there is an error getting it.
-                (void)NS_GetReferrerFromChannel(aChannel,
-                                                getter_AddRefs(referrer));
-
-                AddURIVisit(aURI, referrer, previousURI, previousFlags);
-            }
+            // Get the referrer uri from the channel
+            AddToGlobalHistory(aURI, PR_FALSE, aChannel);
         }
     }
 
     // If this was a history load, update the index in 
     // SH. 
     if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
         nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
         if (shInternal) {
@@ -9407,17 +9367,17 @@ nsDocShell::AddState(nsIVariant *aData, 
     // We need to call FireOnLocationChange so that the browser's address bar
     // gets updated and the back button is enabled, but we only need to
     // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
     // since SetCurrentURI will call FireOnLocationChange for us.
     if (!equalURIs) {
         SetCurrentURI(newURI, nsnull, PR_TRUE);
         document->SetDocumentURI(newURI);
 
-        AddURIVisit(newURI, oldURI, oldURI, 0);
+        AddToGlobalHistory(newURI, PR_FALSE, oldURI);
     }
     else {
         FireOnLocationChange(this, nsnull, mCurrentURI);
     }
 
     // Try to set the title of the current history element
     if (mOSHE)
         mOSHE->SetTitle(aTitle);
@@ -10103,119 +10063,63 @@ NS_IMETHODIMP nsDocShell::GetHasEditingS
 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
 {
   nsresult rv = EnsureEditorData();
   if (NS_FAILED(rv)) return rv;
 
   return mEditorData->MakeEditable(inWaitForUriLoad);
 }
 
-bool
-nsDocShell::ChannelIsPost(nsIChannel* aChannel)
-{
-    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
-    if (!httpChannel) {
-        return false;
-    }
-
-    nsCAutoString method;
-    httpChannel->GetRequestMethod(method);
-    return method.Equals("POST");
-}
-
-void
-nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
-                             nsIURI** aURI,
-                             PRUint32* aChannelRedirectFlags)
-{
-    nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
-    if (!props) {
-        return;
-    }
-
-    nsresult rv = props->GetPropertyAsInterface(
-        NS_LITERAL_STRING("docshell.previousURI"),
-        NS_GET_IID(nsIURI),
-        reinterpret_cast<void**>(aURI)
-    );
-
-    if (NS_FAILED(rv)) {
-        // There is no last visit for this channel, so this must be the first
-        // link.  Link the visit to the referrer of this request, if any.
-        // Treat referrer as null if there is an error getting it.
-        (void)NS_GetReferrerFromChannel(aChannel, aURI);
-    }
-    else {
-      rv = props->GetPropertyAsUint32(
-          NS_LITERAL_STRING("docshell.previousFlags"),
-          aChannelRedirectFlags
-      );
-
-      NS_WARN_IF_FALSE(
-          NS_FAILED(rv),
-          "Could not fetch previous flags, URI will be treated like referrer"
-      );
-    }
-}
-
-void
-nsDocShell::SaveLastVisit(nsIChannel* aChannel,
-                          nsIURI* aURI,
-                          PRUint32 aChannelRedirectFlags)
-{
-    nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
-    if (!props || !aURI) {
-        return;
-    }
-
-    props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
-                                  aURI);
-    props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
-                               aChannelRedirectFlags);
-}
-
-void
-nsDocShell::AddURIVisit(nsIURI* aURI,
-                        nsIURI* aReferrerURI,
-                        nsIURI* aPreviousURI,
-                        PRUint32 aChannelRedirectFlags)
-{
-    NS_ASSERTION(aURI, "Visited URI is null!");
-
-    // Only content-type docshells save URI visits.
-    if (mItemType != typeContent) {
-        return;
-    }
-
-    nsCOMPtr<IHistory> history = services::GetHistoryService();
-
-    if (history) {
-        PRUint32 visitURIFlags = 0;
-
-        if (!IsFrame()) {
-            visitURIFlags |= IHistory::TOP_LEVEL;
-        }
-
-        if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
-            visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
-        }
-        else if (aChannelRedirectFlags &
-                 nsIChannelEventSink::REDIRECT_PERMANENT) {
-            visitURIFlags |= IHistory::REDIRECT_PERMANENT;
-        }
-
-        (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
-    }
-    else if (mGlobalHistory) {
-        // Falls back to sync global history interface.
-        (void)mGlobalHistory->AddURI(aURI,
-                                     !!aChannelRedirectFlags,
-                                     !IsFrame(),
-                                     aReferrerURI);
-    }
+nsresult
+nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
+                               nsIChannel * aChannel)
+{
+    // If this is a POST request, we do not want to include this in global
+    // history, so return early.
+    nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
+    if (hchan) {
+        nsCAutoString type;
+        nsresult rv = hchan->GetRequestMethod(type);
+        if (NS_SUCCEEDED(rv) && type.EqualsLiteral("POST"))
+            return NS_OK;
+    }
+
+    nsCOMPtr<nsIURI> referrer;
+    if (aChannel)
+        NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
+
+    return AddToGlobalHistory(aURI, aRedirect, referrer);
+}
+
+nsresult
+nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
+                               nsIURI * aReferrer)
+{
+    if (mItemType != typeContent || !mGlobalHistory)
+        return NS_OK;
+
+    PRBool visited;
+    nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
+    if (NS_FAILED(rv))