[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
[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)