Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 26 Jun 2012 15:37:34 -0700
changeset 112762 6f258bdf1b8222fd5477e91b0654320774bb7c58
parent 112761 bd0eba3ea3976721e44e7a41ebdf0cf3e1f23926 (current diff)
parent 102487 be373f4b243feda9d4b2942c690b055611e635f0 (diff)
child 112763 02c16738f77844ddfd925fac6f1d6abbbc5c0840
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge from mozilla-central.
accessible/public/nsIAccessibilityService.h
accessible/src/base/nsAccessibilityService.h
accessible/src/generic/ARIAGridAccessible.cpp
accessible/src/generic/ARIAGridAccessible.h
accessible/src/generic/Accessible.cpp
accessible/src/html/HTMLTableAccessible.cpp
accessible/src/html/HTMLTableAccessible.h
accessible/src/msaa/AccessibleWrap.cpp
accessible/src/msaa/CAccessibleHyperlink.cpp
accessible/src/msaa/CAccessibleHyperlink.h
accessible/src/xul/XULTreeGridAccessible.cpp
accessible/src/xul/XULTreeGridAccessible.h
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/locales/en-US/chrome/browser/browser.dtd
browser/themes/winstripe/browser.css
config/autoconf.mk.in
configure.in
content/base/public/nsContentUtils.h
content/base/public/nsINode.h
content/base/src/Makefile.in
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericElement.cpp
content/base/src/nsINode.cpp
content/events/src/nsEventStateManager.cpp
content/html/document/src/nsHTMLContentSink.cpp
content/html/document/src/nsHTMLDocument.cpp
content/svg/content/src/nsSVGUseElement.cpp
content/svg/content/src/nsSVGUseElement.h
content/xml/document/src/nsXMLContentSink.cpp
content/xul/document/src/nsXULCommandDispatcher.cpp
docshell/base/nsDocShell.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_distinguishability.py
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/plugins/base/nsNPAPIPluginStreamListener.cpp
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginNativeWindowGtk2.cpp
dom/plugins/base/nsPluginsDirUnix.cpp
dom/src/events/nsJSEventListener.cpp
editor/composer/src/nsComposerCommands.cpp
editor/composer/src/nsComposerDocumentCommands.cpp
editor/libeditor/base/CreateElementTxn.cpp
editor/libeditor/base/DeleteElementTxn.cpp
editor/libeditor/base/DeleteRangeTxn.cpp
editor/libeditor/base/DeleteTextTxn.cpp
editor/libeditor/base/InsertElementTxn.cpp
editor/libeditor/base/InsertTextTxn.cpp
editor/libeditor/base/JoinElementTxn.cpp
editor/libeditor/base/SplitElementTxn.cpp
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/base/nsEditorCommands.cpp
editor/libeditor/html/nsHTMLAnonymousUtils.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
editor/libeditor/html/nsHTMLEditor.cpp
editor/libeditor/html/nsHTMLEditor.h
editor/libeditor/html/nsWSRunObject.cpp
editor/libeditor/html/nsWSRunObject.h
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/d3d10/ImageLayerD3D10.cpp
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/d3d10/LayerManagerD3D10.h
gfx/layers/d3d9/ImageLayerD3D9.cpp
gfx/layers/d3d9/LayerManagerD3D9.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPlatformMac.cpp
gfx/thebes/gfxPlatformMac.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
image/src/imgRequest.cpp
intl/uconv/tests/TestUConv.cpp
intl/uconv/util/nsUCSupport.h
js/src/MemoryMetrics.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/gc/Root.h
js/src/jsclass.h
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobjinlines.h
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/jsxml.cpp
js/src/shell/js.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsFrameManager.cpp
layout/base/nsFrameManager.h
layout/base/nsIPresShell.h
layout/base/nsLayoutDebugger.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/forms/nsFileControlFrame.cpp
layout/forms/nsFileControlFrame.h
layout/forms/nsProgressFrame.h
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
layout/generic/crashtests/crashtests.list
layout/generic/nsAbsoluteContainingBlock.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowContext.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsCanvasFrame.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsFirstLetterFrame.cpp
layout/generic/nsFirstLetterFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsFrameUtil.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsHTMLReflowState.cpp
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsLineBox.h
layout/generic/nsPageContentFrame.h
layout/generic/nsPageFrame.h
layout/generic/nsSimplePageSequence.h
layout/generic/nsTextFrameThebes.cpp
layout/generic/nsViewportFrame.cpp
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLContainerFrame.cpp
layout/mathml/nsMathMLFrame.cpp
layout/mathml/nsMathMLFrame.h
layout/mathml/nsMathMLOperators.cpp
layout/mathml/nsMathMLmactionFrame.cpp
layout/mathml/nsMathMLmoFrame.cpp
layout/mathml/nsMathMLmpaddedFrame.cpp
layout/mathml/nsMathMLmtableFrame.cpp
layout/printing/nsPrintEngine.cpp
layout/reftests/svg/reftest.list
layout/svg/base/src/nsISVGChildFrame.h
layout/svg/base/src/nsSVGClipPathFrame.cpp
layout/svg/base/src/nsSVGContainerFrame.cpp
layout/svg/base/src/nsSVGFilterFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.h
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGImageFrame.cpp
layout/svg/base/src/nsSVGInnerSVGFrame.cpp
layout/svg/base/src/nsSVGIntegrationUtils.cpp
layout/svg/base/src/nsSVGMarkerFrame.cpp
layout/svg/base/src/nsSVGMaskFrame.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.h
layout/svg/base/src/nsSVGPathGeometryFrame.cpp
layout/svg/base/src/nsSVGPathGeometryFrame.h
layout/svg/base/src/nsSVGPatternFrame.cpp
layout/svg/base/src/nsSVGSwitchFrame.cpp
layout/svg/base/src/nsSVGTSpanFrame.cpp
layout/svg/base/src/nsSVGTextFrame.cpp
layout/svg/base/src/nsSVGUseFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
layout/tables/nsCellMap.cpp
layout/tables/nsCellMap.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
layout/xul/base/src/grid/nsGridRowLeafFrame.h
layout/xul/base/src/nsProgressMeterFrame.cpp
layout/xul/base/src/nsProgressMeterFrame.h
layout/xul/base/src/nsXULLabelFrame.cpp
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/Makefile.in
mobile/android/base/resources/layout/sync_custom_popup.xml
mobile/android/chrome/content/browser.js
modules/libpref/src/init/all.js
netwerk/protocol/http/SpdySession2.cpp
netwerk/protocol/http/SpdySession3.cpp
netwerk/protocol/http/nsAHttpTransaction.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpPipeline.cpp
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
parser/htmlparser/src/nsDTDUtils.cpp
parser/htmlparser/src/nsDTDUtils.h
parser/htmlparser/src/nsElementTable.cpp
parser/htmlparser/src/nsElementTable.h
parser/htmlparser/src/nsHTMLEntities.cpp
parser/htmlparser/src/nsParser.cpp
parser/htmlparser/src/nsParserModule.cpp
rdf/base/src/nsCompositeDataSource.cpp
rdf/tests/dsds/nsRDFDataSourceDS.cpp
services/makefiles.sh
testing/xpcshell/xpcshell.ini
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/viewsource/content/viewPartialSource.js
toolkit/content/widgets/popup.xml
toolkit/mozapps/extensions/content/selectAddons.xml
toolkit/mozapps/update/nsUpdateService.js
view/src/nsView.cpp
widget/nsGUIEvent.h
widget/windows/nsClipboard.cpp
widget/windows/nsDragService.cpp
widget/windows/nsWindow.cpp
widget/xpwidgets/nsPrintOptionsImpl.cpp
xpcom/base/nsExceptionService.cpp
xpcom/base/nsMemoryReporterManager.cpp
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsAtomTable.h
xpcom/glue/nsBaseHashtable.h
xpcom/glue/nsCOMPtr.h
xpcom/glue/nsHashKeys.h
xpcom/glue/nsISupportsImpl.h
xpcom/string/src/nsStringObsolete.cpp
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -62,17 +62,17 @@
 #include "nsIMutableArray.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsAttrName.h"
 #include "nsNetUtil.h"
 #include "nsEventStates.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 #include "nsIDOMCharacterData.h"
 #endif
 
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
@@ -794,62 +794,59 @@ Accessible::ChildAtPoint(PRInt32 aX, PRI
   NS_ASSERTION(contentDocAcc, "could not get the document accessible");
   if (!contentDocAcc)
     return fallbackAnswer;
 
   Accessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
   if (!accessible)
     return fallbackAnswer;
 
-  if (accessible == this) {
-    // Manually walk through accessible children and see if the are within this
-    // point. Skip offscreen or invisible accessibles. This takes care of cases
-    // where layout won't walk into things for us, such as image map areas and
-    // sub documents (XXX: subdocuments should be handled by methods of
-    // OuterDocAccessibles).
-    PRUint32 childCount = ChildCount();
-    for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
-      Accessible* child = GetChildAt(childIdx);
-
-      PRInt32 childX, childY, childWidth, childHeight;
-      child->GetBounds(&childX, &childY, &childWidth, &childHeight);
-      if (aX >= childX && aX < childX + childWidth &&
-          aY >= childY && aY < childY + childHeight &&
-          (child->State() & states::INVISIBLE) == 0) {
-
-        if (aWhichChild == eDeepestChild)
-          return child->ChildAtPoint(aX, aY, eDeepestChild);
-
-        return child;
-      }
-    }
-
-    // The point is in this accessible but not in a child. We are allowed to
-    // return |this| as the answer.
-    return accessible;
-  }
-
+  // Hurray! We have an accessible for the frame that layout gave us.
   // Since DOM node of obtained accessible may be out of flow then we should
   // ensure obtained accessible is a child of this accessible.
   Accessible* child = accessible;
-  while (true) {
+  while (child != this) {
     Accessible* parent = child->Parent();
     if (!parent) {
       // Reached the top of the hierarchy. These bounds were inside an
       // accessible that is not a descendant of this one.
       return fallbackAnswer;
     }
 
-    if (parent == this)
-      return aWhichChild == eDeepestChild ? accessible : child;
+    // If we landed on a legitimate child of |this|, and we want the direct
+    // child, return it here.
+    if (parent == this && aWhichChild == eDirectChild)
+        return child;
 
     child = parent;
   }
 
-  return nsnull;
+  // Manually walk through accessible children and see if the are within this
+  // point. Skip offscreen or invisible accessibles. This takes care of cases
+  // where layout won't walk into things for us, such as image map areas and
+  // sub documents (XXX: subdocuments should be handled by methods of
+  // OuterDocAccessibles).
+  PRUint32 childCount = accessible->ChildCount();
+  for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
+    Accessible* child = accessible->GetChildAt(childIdx);
+
+    PRInt32 childX, childY, childWidth, childHeight;
+    child->GetBounds(&childX, &childY, &childWidth, &childHeight);
+    if (aX >= childX && aX < childX + childWidth &&
+        aY >= childY && aY < childY + childHeight &&
+        (child->State() & states::INVISIBLE) == 0) {
+
+      if (aWhichChild == eDeepestChild)
+        return child->ChildAtPoint(aX, aY, eDeepestChild);
+
+      return child;
+    }
+  }
+
+  return accessible;
 }
 
 // nsIAccessible getChildAtPoint(in long x, in long y)
 NS_IMETHODIMP
 Accessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
                             nsIAccessible** aAccessible)
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -227,18 +227,19 @@ var AccessFu = {
             QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
           let event = aEvent.
             QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
           let position = pivot.position;
           let doc = aEvent.DOMNode;
 
           let presenterContext =
             new PresenterContext(position, event.oldAccessible);
+          let reason = event.reason;
           this.presenters.forEach(
-            function(p) { p.pivotChanged(presenterContext); });
+            function(p) { p.pivotChanged(presenterContext, reason); });
 
           break;
         }
       case Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE:
         {
           let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
           if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
               !(event.isExtraState())) {
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -34,18 +34,20 @@ Presenter.prototype = {
    * Detach function.
    */
   detach: function detach() {},
 
   /**
    * The virtual cursor's position changed.
    * @param {PresenterContext} aContext the context object for the new pivot
    *   position.
+   * @param {int} aReason the reason for the pivot change.
+   *   See nsIAccessiblePivot.
    */
-  pivotChanged: function pivotChanged(aContext) {},
+  pivotChanged: function pivotChanged(aContext, aReason) {},
 
   /**
    * An object's action has been invoked.
    * @param {nsIAccessible} aObject the object that has been invoked.
    * @param {string} aActionName the name of the action.
    */
   actionInvoked: function actionInvoked(aObject, aActionName) {},
 
@@ -135,17 +137,17 @@ VisualPresenter.prototype = {
     this.highlightBox = this.stylesheet = null;
   },
 
   viewportChanged: function VisualPresenter_viewportChanged() {
     if (this._currentObject)
       this._highlight(this._currentObject);
   },
 
-  pivotChanged: function VisualPresenter_pivotChanged(aContext) {
+  pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
     this._currentObject = aContext.accessible;
 
     if (!aContext.accessible) {
       this._hide();
       return;
     }
 
     try {
@@ -154,17 +156,17 @@ VisualPresenter.prototype = {
       this._highlight(aContext.accessible);
     } catch (e) {
       Logger.error('Failed to get bounds: ' + e);
       return;
     }
   },
 
   tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
-    this.pivotChanged(aVCContext);
+    this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
   },
 
   tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
                                                             aPageState) {
     if (aPageState == 'newdoc')
       this._hide();
   },
 
@@ -224,41 +226,79 @@ AndroidPresenter.prototype = {
 
   // Android AccessibilityEvent type constants.
   ANDROID_VIEW_CLICKED: 0x01,
   ANDROID_VIEW_LONG_CLICKED: 0x02,
   ANDROID_VIEW_SELECTED: 0x04,
   ANDROID_VIEW_FOCUSED: 0x08,
   ANDROID_VIEW_TEXT_CHANGED: 0x10,
   ANDROID_WINDOW_STATE_CHANGED: 0x20,
+  ANDROID_VIEW_HOVER_ENTER: 0x80,
+  ANDROID_VIEW_HOVER_EXIT: 0x100,
+  ANDROID_VIEW_SCROLLED: 0x1000,
 
-  pivotChanged: function AndroidPresenter_pivotChanged(aContext) {
+  attach: function AndroidPresenter_attach(aWindow) {
+    this.chromeWin = aWindow;
+  },
+
+  pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
     if (!aContext.accessible)
       return;
 
+    let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
+                            Utils.AndroidSdkVersion >= 14);
+
+    if (isExploreByTouch) {
+      // This isn't really used by TalkBack so this is a half-hearted attempt
+      // for now.
+      this.sendMessageToJava({
+         gecko: {
+           type: 'Accessibility:Event',
+           eventType: this.ANDROID_VIEW_HOVER_EXIT,
+           text: []
+         }
+      });
+    }
+
     let output = [];
-    aContext.newAncestry.forEach(
-      function(acc) {
-        output.push.apply(output, UtteranceGenerator.genForObject(acc));
+
+    if (isExploreByTouch) {
+      // Just provide the parent for some context, no need to utter the entire
+      // ancestry change since it doesn't make sense in spatial navigation.
+      for (var i = aContext.newAncestry.length - 1; i >= 0; i--) {
+        let utter = UtteranceGenerator.genForObject(aContext.newAncestry[i]);
+        if (utter.length) {
+          output.push.apply(output, utter);
+          break;
+        }
       }
-    );
+    } else {
+      // Utter the entire context change in linear navigation.
+      aContext.newAncestry.forEach(
+        function(acc) {
+          output.push.apply(output, UtteranceGenerator.genForObject(acc));
+        }
+      );
+    }
 
     output.push.apply(output,
                       UtteranceGenerator.genForObject(aContext.accessible));
 
     aContext.subtreePreorder.forEach(
       function(acc) {
         output.push.apply(output, UtteranceGenerator.genForObject(acc));
       }
     );
 
     this.sendMessageToJava({
       gecko: {
         type: 'Accessibility:Event',
-        eventType: this.ANDROID_VIEW_FOCUSED,
+        eventType: isExploreByTouch ?
+          this.ANDROID_VIEW_HOVER_ENTER :
+          this.ANDROID_VIEW_FOCUSED,
         text: output
       }
     });
   },
 
   actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
     this.sendMessageToJava({
       gecko: {
@@ -266,17 +306,17 @@ AndroidPresenter.prototype = {
         eventType: this.ANDROID_VIEW_CLICKED,
         text: UtteranceGenerator.genForAction(aObject, aActionName)
       }
     });
   },
 
   tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
     // Send a pivot change message with the full context utterance for this doc.
-    this.pivotChanged(aVCContext);
+    this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
   },
 
   tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
                                                              aPageState) {
     let stateUtterance = UtteranceGenerator.
       genForTabStateChange(aDocObj, aPageState);
 
     if (!stateUtterance.length)
@@ -314,16 +354,34 @@ AndroidPresenter.prototype = {
       androidEvent.removedCount = aLength;
       androidEvent.beforeText =
         aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
     }
 
     this.sendMessageToJava({gecko: androidEvent});
   },
 
+  viewportChanged: function AndroidPresenter_viewportChanged() {
+    if (Utils.AndroidSdkVersion < 14)
+      return;
+
+    let win = Utils.getBrowserApp(this.chromeWin).selectedBrowser.contentWindow;
+    this.sendMessageToJava({
+      gecko: {
+        type: 'Accessibility:Event',
+        eventType: this.ANDROID_VIEW_SCROLLED,
+        text: [],
+        scrollX: win.scrollX,
+        scrollY: win.scrollY,
+        maxScrollX: win.scrollMaxX,
+        maxScrollY: win.scrollMaxY
+      }
+    });
+  },
+
   sendMessageToJava: function AndroidPresenter_sendMessageTojava(aMessage) {
     return Cc['@mozilla.org/android/bridge;1'].
       getService(Ci.nsIAndroidBridge).
       handleGeckoMessage(JSON.stringify(aMessage));
   }
 };
 
 /**
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -43,16 +43,20 @@ var Utils = {
     switch (this.OS) {
       case 'Android':
         return aWindow.BrowserApp;
       default:
         return aWindow.gBrowser;
     }
   },
 
+  getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
+    return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
+  },
+
   getViewport: function getViewport(aWindow) {
     switch (this.OS) {
       case 'Android':
         return aWindow.BrowserApp.selectedTab.getViewport();
       default:
         return null;
     }
   }
--- a/accessible/src/jsat/VirtualCursorController.jsm
+++ b/accessible/src/jsat/VirtualCursorController.jsm
@@ -403,30 +403,64 @@ var TraversalRules = {
   }
 };
 
 var VirtualCursorController = {
   NOT_EDITABLE: 0,
   SINGLE_LINE_EDITABLE: 1,
   MULTI_LINE_EDITABLE: 2,
 
-  explorebytouch: false,
+  exploreByTouch: false,
 
   attach: function attach(aWindow) {
     this.chromeWin = aWindow;
     this.chromeWin.document.addEventListener('keypress', this, true);
+    this.chromeWin.document.addEventListener('mousemove', this, true);
   },
 
   detach: function detach() {
     this.chromeWin.document.removeEventListener('keypress', this, true);
+    this.chromeWin.document.removeEventListener('mousemove', this, true);
+  },
+
+  handleEvent: function VirtualCursorController_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case 'keypress':
+        this._handleKeypress(aEvent);
+        break;
+      case 'mousemove':
+        this._handleMousemove(aEvent);
+        break;
+    }
   },
 
-  handleEvent: function handleEvent(aEvent) {
-    let document = Utils.getBrowserApp(this.chromeWin).
-      selectedBrowser.contentDocument;
+  _handleMousemove: function _handleMousemove(aEvent) {
+    // Explore by touch is disabled.
+    if (!this.exploreByTouch)
+      return;
+
+    // On non-Android we use the shift key to simulate touch.
+    if (Utils.OS != 'Android' && !aEvent.shiftKey)
+      return;
+
+    // We should not be calling moveToPoint more than 10 times a second.
+    // It is granular enough to feel natural, and it does not hammer the CPU.
+    if (!this._handleMousemove._lastEventTime ||
+        aEvent.timeStamp - this._handleMousemove._lastEventTime >= 100) {
+      this.moveToPoint(Utils.getCurrentContentDoc(this.chromeWin),
+                       aEvent.screenX, aEvent.screenY);
+      this._handleMousemove._lastEventTime = aEvent.timeStamp;
+    }
+
+    aEvent.preventDefault();
+    aEvent.stopImmediatePropagation();
+  },
+
+  _handleKeypress: function _handleKeypress(aEvent) {
+    let document = Utils.getCurrentContentDoc(this.chromeWin);
     let target = aEvent.target;
 
     switch (aEvent.keyCode) {
       case 0:
         // an alphanumeric key was pressed, handle it separately.
         // If it was pressed with either alt or ctrl, just pass through.
         // If it was pressed with meta, pass the key on without the meta.
         if (this._isEditableText(target) ||
@@ -494,16 +528,21 @@ var VirtualCursorController = {
       default:
         return;
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
+  moveToPoint: function moveToPoint(aDocument, aX, aY) {
+    this.getVirtualCursor(aDocument).moveToPoint(TraversalRules.Simple,
+                                                 aX, aY, true);
+  },
+
   _isEditableText: function _isEditableText(aElement) {
     // XXX: Support contentEditable and design mode
     if (aElement instanceof Ci.nsIDOMHTMLInputElement &&
         aElement.mozIsTextField(false))
       return this.SINGLE_LINE_EDITABLE;
 
     if (aElement instanceof Ci.nsIDOMHTMLTextAreaElement)
       return this.MULTI_LINE_EDITABLE;
@@ -577,18 +616,17 @@ var VirtualCursorController = {
     while (doc) {
       let vc = null;
       try {
         vc = doc.QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
       } catch (x) {
         doc = doc.parentDocument;
         continue;
       }
-      if (vc)
-        vc.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
+      vc.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
       break;
     }
   },
 
   keyMap: {
     a: ['moveForward', TraversalRules.Anchor],
     A: ['moveBackward', TraversalRules.Anchor],
     b: ['moveForward', TraversalRules.Button],
--- a/accessible/src/msaa/AccessibleWrap.cpp
+++ b/accessible/src/msaa/AccessibleWrap.cpp
@@ -98,17 +98,17 @@ AccessibleWrap::QueryInterface(REFIID ii
 
   if (NULL == *ppv) {
     HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
   if (NULL == *ppv) {
-    HRESULT hr = CAccessibleHyperlink::QueryInterface(iid, ppv);
+    HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
   if (NULL == *ppv) {
     HRESULT hr = CAccessibleValue::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
--- a/accessible/src/msaa/AccessibleWrap.h
+++ b/accessible/src/msaa/AccessibleWrap.h
@@ -9,17 +9,17 @@
 
 #ifndef _AccessibleWrap_H_
 #define _AccessibleWrap_H_
 
 #include "nsCOMPtr.h"
 #include "Accessible.h"
 #include "Accessible2.h"
 #include "ia2AccessibleComponent.h"
-#include "CAccessibleHyperlink.h"
+#include "ia2AccessibleHyperlink.h"
 #include "CAccessibleValue.h"
 
 #define DECL_IUNKNOWN_INHERITED                                               \
 public:                                                                       \
 STDMETHODIMP QueryInterface(REFIID, void**);                                  \
 
 #define IMPL_IUNKNOWN_QUERY_HEAD(Class)                                       \
 STDMETHODIMP                                                                  \
@@ -60,17 +60,17 @@ Class::QueryInterface(REFIID iid, void**
   IMPL_IUNKNOWN_QUERY_ENTRY(I1);                                              \
   IMPL_IUNKNOWN_QUERY_ENTRY(I2);                                              \
   IMPL_IUNKNOWN_QUERY_ENTRY(Super)                                            \
   IMPL_IUNKNOWN_QUERY_TAIL                                                    \
 
 
 class AccessibleWrap : public Accessible,
                        public ia2AccessibleComponent,
-                       public CAccessibleHyperlink,
+                       public ia2AccessibleHyperlink,
                        public CAccessibleValue,
                        public IAccessible2
 {
 public: // construction, destruction
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
     Accessible(aContent, aDoc) { }
   virtual ~AccessibleWrap() { }
 
--- a/accessible/src/msaa/Makefile.in
+++ b/accessible/src/msaa/Makefile.in
@@ -21,26 +21,26 @@ CPPSRCS = \
   ARIAGridAccessibleWrap.cpp \
   DocAccessibleWrap.cpp \
   HTMLTableAccessibleWrap.cpp \
   HyperTextAccessibleWrap.cpp \
   ImageAccessibleWrap.cpp \
   nsAccessNodeWrap.cpp \
   nsHTMLWin32ObjectAccessible.cpp \
   nsWinUtils.cpp \
-  CAccessibleHyperlink.cpp \
   CAccessibleTable.cpp \
   CAccessibleTableCell.cpp \
   CAccessibleValue.cpp \
   Compatibility.cpp \
   EnumVariant.cpp \
   ia2AccessibleAction.cpp \
   ia2AccessibleComponent.cpp \
   ia2AccessibleEditableText.cpp \
   ia2AccessibleImage.cpp \
+  ia2AccessibleHyperlink.cpp \
   ia2AccessibleHypertext.cpp \
   ia2AccessibleRelation.cpp \
   ia2AccessibleText.cpp \
   RootAccessibleWrap.cpp \
   TextLeafAccessibleWrap.cpp \
   $(NULL)
 
 ifdef MOZ_XUL
rename from accessible/src/msaa/CAccessibleHyperlink.cpp
rename to accessible/src/msaa/ia2AccessibleHyperlink.cpp
--- a/accessible/src/msaa/CAccessibleHyperlink.cpp
+++ b/accessible/src/msaa/ia2AccessibleHyperlink.cpp
@@ -1,53 +1,50 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=2:tabstop=2:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "CAccessibleHyperlink.h"
-
 #include "Accessible2.h"
 #include "AccessibleHyperlink.h"
 #include "AccessibleHyperlink_i.c"
 
 #include "AccessibleWrap.h"
 #include "nsIWinAccessNode.h"
 
 // IUnknown
 
 STDMETHODIMP
-CAccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
+ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
 {
   *ppv = NULL;
 
   if (IID_IAccessibleHyperlink == iid) {
-    nsRefPtr<Accessible> thisObj = do_QueryObject(this);
-    if (!thisObj->IsLink())
+    if (!static_cast<AccessibleWrap*>(this)->IsLink())
       return E_NOINTERFACE;
 
     *ppv = static_cast<IAccessibleHyperlink*>(this);
     (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
     return S_OK;
   }
 
   return ia2AccessibleAction::QueryInterface(iid, ppv);
 }
 
 // IAccessibleHyperlink
 
 STDMETHODIMP
-CAccessibleHyperlink::get_anchor(long aIndex, VARIANT *aAnchor)
+ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
 {
 __try {
   VariantInit(aAnchor);
 
-  nsRefPtr<Accessible> thisObj = do_QueryObject(this);
+  Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
     return E_INVALIDARG;
 
   if (!thisObj->IsLink())
     return S_FALSE;
@@ -67,22 +64,22 @@ CAccessibleHyperlink::get_anchor(long aI
   aAnchor->vt = VT_UNKNOWN;
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
-CAccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT *aAnchorTarget)
+ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
 {
 __try {
   VariantInit(aAnchorTarget);
 
-  nsRefPtr<Accessible> thisObj = do_QueryObject(this);
+  Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
     return E_INVALIDARG;
 
   if (!thisObj->IsLink())
     return S_FALSE;
@@ -110,62 +107,62 @@ CAccessibleHyperlink::get_anchorTarget(l
                                                stringURI.Length());
   return aAnchorTarget->bstrVal ? S_OK : E_OUTOFMEMORY;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
-CAccessibleHyperlink::get_startIndex(long *aIndex)
+ia2AccessibleHyperlink::get_startIndex(long* aIndex)
 {
 __try {
   *aIndex = 0;
 
-  nsRefPtr<Accessible> thisObj = do_QueryObject(this);
+  Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
   *aIndex = thisObj->StartOffset();
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
-CAccessibleHyperlink::get_endIndex(long *aIndex)
+ia2AccessibleHyperlink::get_endIndex(long* aIndex)
 {
 __try {
   *aIndex = 0;
 
-  nsRefPtr<Accessible> thisObj = do_QueryObject(this);
+  Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
   *aIndex = thisObj->EndOffset();
   return S_OK;
 
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return E_FAIL;
 }
 
 STDMETHODIMP
-CAccessibleHyperlink::get_valid(boolean *aValid)
+ia2AccessibleHyperlink::get_valid(boolean* aValid)
 {
 __try {
   *aValid = false;
 
-  nsRefPtr<Accessible> thisObj = do_QueryObject(this);
+  Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
   *aValid = thisObj->IsLinkValid();
   return S_OK;
rename from accessible/src/msaa/CAccessibleHyperlink.h
rename to accessible/src/msaa/ia2AccessibleHyperlink.h
--- a/accessible/src/msaa/CAccessibleHyperlink.h
+++ b/accessible/src/msaa/ia2AccessibleHyperlink.h
@@ -8,18 +8,18 @@
 #ifndef _ACCESSIBLE_HYPERLINK_H
 #define _ACCESSIBLE_HYPERLINK_H
 
 #include "nsISupports.h"
 
 #include "ia2AccessibleAction.h"
 #include "AccessibleHyperlink.h"
 
-class CAccessibleHyperlink: public ia2AccessibleAction,
-                            public IAccessibleHyperlink
+class ia2AccessibleHyperlink : public ia2AccessibleAction,
+                               public IAccessibleHyperlink
 {
 public:
 
   // IUnknown
   STDMETHODIMP QueryInterface(REFIID, void**);
 
   // IAccessibleAction
   FORWARD_IACCESSIBLEACTION(ia2AccessibleAction)
--- a/accessible/tests/mochitest/hittest/test_general.html
+++ b/accessible/tests/mochitest/hittest/test_general.html
@@ -1,23 +1,33 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>nsIAccessible::childAtPoint() tests</title>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
 
   <script type="application/javascript">
+    function doPreTest()
+    {
+      var imgMap = document.getElementById("imgmap");
+      waitForImageMap(imgMap, doTest);
+    }
+
     function doTest()
     {
       // Not specific case, child and deepchild testing.
       var list = getAccessible("list");
       var listitem = getAccessible("listitem");
       var image = getAccessible("image");
 if (!MAC) {
       testChildAtPoint(list, 1, 1, listitem, image.firstChild);
@@ -49,21 +59,26 @@ if (!MAC) {
       // because it's not a child of the accessible even visually it is.
       var rectArea = getNode("area").getBoundingClientRect();
       var outOfFlow = getNode("outofflow");
       outOfFlow.style.left = rectArea.left + "px";
       outOfFlow.style.top = rectArea.top + "px";
 
       testChildAtPoint("area", 1, 1, "area", "area");
 
+      // Test image maps. Their children are not in the layout tree.
+      var theLetterA = getAccessible("imgmap").firstChild;
+      hitTest("imgmap", theLetterA, theLetterA);
+      hitTest("container", "imgmap", theLetterA);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    addA11yLoadEvent(doPreTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=491657"
      title="nsIAccessible::childAtPoint() tests">Mozilla Bug 491657</a>
   <p id="display"></p>
@@ -77,10 +92,20 @@ if (!MAC) {
 
   <span role="button">button1</span><span role="button" id="btn">button2</span>
 
   <span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span>
 
   <div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;">
   </div>
   <div id="area" style="width: 100px; height: 100px; background-color: blue;"></div>
+
+  <map name="atoz_map">
+    <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+          coords="0,0,15,15" alt="thelettera" shape="rect"/>
+  </map>
+
+  <div id="container">
+    <img id="imgmap" width="447" height="15" usemap="#atoz_map" src="../letters.gif"/>
+  </div>
+
 </body>
 </html>
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -66,19 +66,30 @@ let FormAssistant = {
 
         Services.fm.focusedElement.scrollIntoView(false);
         break;
 
       case "mousedown":
         if (evt.target != target || this.isKeyboardOpened)
           return;
 
-        if (!(evt.target instanceof HTMLInputElement  ||
-              evt.target instanceof HTMLTextAreaElement))
+        let ignore = {
+          button: true,
+          checkbox: true,
+          file: true,
+          radio: true,
+          reset: true,
+          submit: true
+        };
+    
+        if ((target instanceof HTMLInputElement && ignore[target.type]) ||
+            !(target instanceof HTMLInputElement ||
+              target instanceof HTMLTextAreaElement)) {
           return;
+        }
 
         this.isKeyboardOpened = this.tryShowIme(evt.target);
         break;
 
       case "keypress":
         if (evt.keyCode != evt.DOM_VK_ESCAPE || !this.isKeyboardOpened)
           return;
 
--- a/b2g/chrome/content/screen.js
+++ b/b2g/chrome/content/screen.js
@@ -13,16 +13,18 @@ window.addEventListener('ContentStart', 
   // The <browser> element inside it
   let browser = document.getElementById('homescreen');
 
   // Figure out the native resolution of the screen
   let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Components.interfaces.nsIDOMWindowUtils);
   let hostDPI = windowUtils.displayDPI;
 
+  let DEFAULT_SCREEN = "320x480";
+
   // This is a somewhat random selection of named screens.
   // Add more to this list when we support more hardware.
   // Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
   let screens = {
     iphone: {
       name: 'Apple iPhone', width:320, height:480,  dpi:163
     },
     ipad: {
@@ -57,19 +59,19 @@ window.addEventListener('ContentStart', 
   // Get the command line arguments that were passed to the b2g client
   let args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
   let screenarg;
 
   // Get the --screen argument from the command line
   try {
     screenarg = args.handleFlagWithParam('screen', false);
 
-    // If there isn't one, we don't need to do anything
+    // If there isn't one, use the default screen
     if (screenarg === null)
-      return;
+      screenarg = DEFAULT_SCREEN;
 
     // With no value, tell the user how to use it
     if (screenarg == '')
       usage();
   }
   catch(e) {
     // If getting the argument value fails, its an error
     usage();
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -135,17 +135,16 @@ var shell = {
     let domains = "";
     try {
       domains = Services.prefs.getCharPref('b2g.privileged.domains');
     } catch(e) {}
 
     addPermissions(domains.split(","));
 
     CustomEventManager.init();
-
     WebappsHelper.init();
 
     // XXX could factor out into a settings->pref map.  Not worth it yet.
     SettingsListener.observe("debug.fps.enabled", false, function(value) {
       Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
     });
     SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
       Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
@@ -211,17 +210,17 @@ var shell = {
       case 'keypress':
         // For debug purposes and because some of the APIs are not yet exposed
         // to the content, let's react on some of the keyup events.
         if (evt.type == 'keyup' && evt.eventPhase == evt.BUBBLING_PHASE) {
           switch (evt.keyCode) {
             case evt.DOM_VK_PAGE_DOWN:
               this.changeVolume(-1);
               break;
-  
+
             case evt.DOM_VK_PAGE_UP:
               this.changeVolume(1);
               break;
           }
         }
 
         // Redirect the HOME key to System app and stop the applications from
         // handling it.
@@ -244,16 +243,19 @@ var shell = {
       case 'sizemodechange':
         if (window.windowState == window.STATE_MINIMIZED) {
           this.contentBrowser.setVisible(false);
         } else {
           this.contentBrowser.setVisible(true);
         }
         break;
       case 'mozbrowserloadstart':
+        if (content.document.location == 'about:blank')
+          return;
+
         this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
 
         let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
         chromeWindow.browserDOMWindow = new nsBrowserAccess();
 
         this.sendEvent(window, 'ContentStart');
         break;
       case 'MozApplicationManifest':
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -182,16 +182,17 @@
                     command="Tools:ChromeDebugger"/>
           <menuitem id="appmenu_scratchpad"
                     hidden="true"
                     label="&scratchpad.label;"
                     key="key_scratchpad"
                     command="Tools:Scratchpad"/>
           <menuitem id="appmenu_styleeditor"
                     hidden="true"
+                    type="checkbox"
                     label="&styleeditor.label;"
                     key="key_styleeditor"
                     command="Tools:StyleEditor"/>
           <menuitem id="appmenu_pageSource"
                     label="&viewPageSourceCmd.label;"
                     command="View:PageSource"
                     key="key_viewSource"/>
           <menuitem id="appmenu_errorConsole"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -558,16 +558,17 @@
                             command="Tools:ChromeDebugger"/>
                   <menuitem id="menu_scratchpad"
                             hidden="true"
                             label="&scratchpad.label;"
                             accesskey="&scratchpad.accesskey;"
                             key="key_scratchpad"
                             command="Tools:Scratchpad"/>
                   <menuitem id="menu_styleeditor"
+                            type="checkbox"
                             hidden="true"
                             label="&styleeditor.label;"
                             accesskey="&styleeditor.accesskey;"
                             key="key_styleeditor"
                             command="Tools:StyleEditor"/>
                   <menuitem id="menu_pageSource"
                             accesskey="&pageSourceCmd.accesskey;"
                             label="&pageSourceCmd.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -90,17 +90,17 @@
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
-    <command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
+    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -10,17 +10,19 @@ let gSyncUI = {
          "weave:service:setup-complete",
          "weave:service:login:start",
          "weave:service:login:finish",
          "weave:service:logout:finish",
          "weave:service:start-over",
          "weave:ui:login:error",
          "weave:ui:sync:error",
          "weave:ui:sync:finish",
-         "weave:ui:clear-error"],
+         "weave:ui:clear-error",
+         "weave:engine:clients:display-uri",
+  ],
 
   _unloaded: false,
 
   init: function SUI_init() {
     // Proceed to set up the UI if Sync has already started up.
     // Otherwise we'll do it when Sync is firing up.
     if (Weave.Status.ready) {
       this.initUI();
@@ -390,16 +392,31 @@ let gSyncUI = {
       title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
       Weave.Notifications.removeAll(title);
       this._wasDelayed = false;
     }
 
     this.updateUI();
   },
 
+  /**
+   * Observer called when display URI command is received.
+   */
+  onDisplayURI: function onDisplayURI(data) {
+    if (!gBrowser) {
+      return;
+    }
+
+    try {
+      gBrowser.addTab(data.wrappedJSObject.object.uri);
+    } catch (ex) {
+      Cu.reportError("Error displaying tab received by Sync: " + ex);
+    }
+  },
+
   observe: function SUI_observe(subject, topic, data) {
     if (this._unloaded) {
       Cu.reportError("SyncUI observer called after unload: " + topic);
       return;
     }
 
     switch (topic) {
       case "weave:service:sync:start":
@@ -439,16 +456,19 @@ let gSyncUI = {
         this.initUI();
         break;
       case "weave:notification:added":
         this.initNotifications();
         break;
       case "weave:ui:clear-error":
         this.clearError();
         break;
+      case "weave:engine:clients:display-uri":
+        this.onDisplayURI(subject);
+        break;
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1472,16 +1472,17 @@ var gBrowserInit = {
     // Enable Style Editor?
     let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
     if (styleEditorEnabled) {
       document.getElementById("menu_styleeditor").hidden = false;
       document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
       document.getElementById("appmenu_styleeditor").hidden = false;
 #endif
+      document.getElementById("developer-toolbar-styleeditor").hidden = false;
     }
 
 #ifdef MENUBAR_CAN_AUTOHIDE
     // If the user (or the locale) hasn't enabled the top-level "Character
     // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
     // hide it.
     if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                                Ci.nsIPrefLocalizedString).data)
@@ -1491,17 +1492,16 @@ var gBrowserInit = {
     // Enable Responsive UI?
     let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
     if (responsiveUIEnabled) {
       document.getElementById("menu_responsiveUI").hidden = false;
       document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
       document.getElementById("appmenu_responsiveUI").hidden = false;
 #endif
-      document.getElementById("developer-toolbar-responsiveui").hidden = false;
     }
 
     let appMenuButton = document.getElementById("appmenu-button");
     let appMenuPopup = document.getElementById("appmenu-popup");
     if (appMenuButton && appMenuPopup) {
       let appMenuOpening = null;
       appMenuButton.addEventListener("mousedown", function(event) {
         if (event.button == 0)
@@ -7365,50 +7365,39 @@ var StyleEditor = {
    * Opens the style editor. If the UI is already open, it will be focused.
    *
    * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
    * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
    * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
    */
   openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol)
   {
-    const CHROME_URL = "chrome://browser/content/styleeditor.xul";
-    const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
-    const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
-
-    // focus currently open Style Editor window for this document, if any
     let contentWindow = gBrowser.selectedBrowser.contentWindow;
-    let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-      getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
-    let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
-    while (enumerator.hasMoreElements()) {
-      var win = enumerator.getNext();
-      if (win.styleEditorChrome.contentWindowID == contentWindowID) {
-        if (aSelectedStyleSheet) {
-          win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
-        }
-        win.focus();
-        return win;
-      }
-    }
-
-    let args = {
-      contentWindow: contentWindow,
-      selectedStyleSheet: aSelectedStyleSheet,
-      line: aLine,
-      col: aCol
-    };
-    args.wrappedJSObject = args;
-    let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
-                                              CHROME_WINDOW_FLAGS, args);
-    chromeWindow.focus();
-    return chromeWindow;
+    let win = this.StyleEditorManager.getEditorForWindow(contentWindow);
+    if (win) {
+      this.StyleEditorManager.selectEditor(win);
+      return win;
+    } else {
+      return this.StyleEditorManager.newEditor(contentWindow,
+                                               aSelectedStyleSheet, aLine, aCol);
+    }
+  },
+
+  toggle: function SE_toggle()
+  {
+    this.StyleEditorManager.toggleEditor(gBrowser.contentWindow);
   }
 };
 
+XPCOMUtils.defineLazyGetter(StyleEditor, "StyleEditorManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/StyleEditor.jsm", tmp);
+  return new tmp.StyleEditorManager(window);
+});
+
 
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1040,23 +1040,23 @@
                          label="&webConsoleButton.label;"
                          class="devtools-toolbarbutton"
                          command="Tools:WebConsole"/>
           <toolbarbutton id="developer-toolbar-inspector"
                          label="&inspectorButton.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Inspect"/>
-          <toolbarbutton id="developer-toolbar-responsiveui"
-                         label="&responsiveDesignTool.label;"
+          <toolbarbutton id="developer-toolbar-styleeditor"
+                         label="&styleeditor.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
-                         command="Tools:ResponsiveUI"/>
+                         command="Tools:StyleEditor"/>
           <toolbarbutton id="developer-toolbar-debugger"
-                         label="&scriptsButton.label;"
+                         label="&debuggerMenu.label2;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Debugger"/>
 #ifndef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
                          class="devtools-closebutton"
                          oncommand="DeveloperToolbar.hide();"
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -1,25 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-:root {
-  -moz-user-focus: normal;
-}
-
 input[type=button] {
   cursor: pointer;
 }
 
 /* SCROLLBOX */
 #newtab-scrollbox {
   display: -moz-box;
   position: relative;
   -moz-box-flex: 1;
+  -moz-user-focus: normal;
 }
 
 #newtab-scrollbox:not([page-disabled]) {
   overflow: auto;
 }
 
 /* TOGGLE */
 #newtab-toggle {
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -75,28 +75,34 @@ let gPage = {
     }.bind(this));
   },
 
   /**
    * Updates the 'page-disabled' attributes of the respective DOM nodes.
    * @param aValue Whether the New Tab Page is enabled or not.
    */
   _updateAttributes: function Page_updateAttributes(aValue) {
-    let selector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
-    let nodes = document.querySelectorAll(selector);
-
     // Set the nodes' states.
-    for (let i = 0; i < nodes.length; i++) {
-      let node = nodes[i];
+    let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
+    for (let node of document.querySelectorAll(nodeSelector)) {
       if (aValue)
         node.removeAttribute("page-disabled");
       else
         node.setAttribute("page-disabled", "true");
     }
 
+    // Enables/disables the control and link elements.
+    let inputSelector = ".newtab-control, .newtab-link";
+    for (let input of document.querySelectorAll(inputSelector)) {
+      if (aValue) 
+        input.removeAttribute("tabindex");
+      else
+        input.setAttribute("tabindex", "-1");
+    }
+
     // Update the toggle button's title.
     let toggle = document.getElementById("newtab-toggle");
     toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));
   },
 
   /**
    * Handles all page events.
    */
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -9,19 +9,17 @@ endif
 TIERS += app
 
 ifdef MOZ_EXTENSIONS
 tier_app_dirs += extensions
 endif
 
 tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
 
-ifdef MOZ_SERVICES_SYNC
 tier_app_dirs += services
-endif
 
 ifdef MOZ_WEBAPP_RUNTIME
 tier_app_dirs += webapprt
 endif
 
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -0,0 +1,14 @@
+[
+{
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
+"algorithm": "sha512", 
+"filename": "setup.sh"
+}, 
+{
+"size": 72350187, 
+"digest": "7d2fbe08aca3ae740e33b8aee872705a3b5229681dd0617ceffd6619fba75cb3cb7e1c3a071218f7cfd464003e5cd773cd8e67d16f78df9c50218fb6671580c6", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2"
+}
+]
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -0,0 +1,14 @@
+[
+{
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
+"algorithm": "sha512", 
+"filename": "setup.sh"
+}, 
+{
+"size": 71534797, 
+"digest": "66bd11bea6e1f07090e9e03c833e107088097605611fd455e80b280ce2b71ca71ff9841a66614f62da162469b222b5eefd5535373b199c60fd485959889b5dcb", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2"
+}
+]
new file mode 120000
--- /dev/null
+++ b/browser/config/tooltool-manifests/macosx32/clang.manifest
@@ -0,0 +1,1 @@
+../macosx64/clang.manifest
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -0,0 +1,14 @@
+[
+{
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
+"algorithm": "sha512", 
+"filename": "setup.sh"
+}, 
+{
+"size": 63015959, 
+"digest": "e0ac132a77b052d6494d4bf02f17aba475138ffcff07a5f92f17f45b1d5f27b7b0cea36c29473965271e60910af82ffd4989df94c10d24794d1bf8362bcb785b", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2"
+}
+]
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -12,16 +12,18 @@ if test "$OS_ARCH" = "WINNT"; then
   if ! test "$HAVE_64BIT_OS"; then
     MOZ_VERIFY_MAR_SIGNATURE=1
     MOZ_MAINTENANCE_SERVICE=1
   fi
 fi
 
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
+MOZ_SERVICES_AITC=1
+MOZ_SERVICES_NOTIFICATIONS=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gnomevfs"
 # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
 # Changing MOZ_*BRANDING_DIRECTORY requires a clobber to ensure correct results,
 # because branding dependencies are broken.
 # MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
 # specified. It should never point to the "official" branding directory.
--- a/browser/devtools/shared/test/browser_toolbar_basic.js
+++ b/browser/devtools/shared/test/browser_toolbar_basic.js
@@ -30,65 +30,100 @@ function isChecked(b) {
 }
 
 function checkOpen() {
   ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkOpen");
 
   let close = document.getElementById("developer-toolbar-closebutton");
   let webconsole = document.getElementById("developer-toolbar-webconsole");
   let inspector = document.getElementById("developer-toolbar-inspector");
+  let styleeditor = document.getElementById("developer-toolbar-styleeditor");
   let debuggr = document.getElementById("developer-toolbar-debugger");
 
   ok(close, "Close button exists");
 
   ok(!isChecked(webconsole), "web console button state 1");
   ok(!isChecked(inspector), "inspector button state 1");
   ok(!isChecked(debuggr), "debugger button state 1");
+  ok(!isChecked(styleeditor), "styleeditor button state 1");
 
   document.getElementById("Tools:WebConsole").doCommand();
 
   ok(isChecked(webconsole), "web console button state 2");
   ok(!isChecked(inspector), "inspector button state 2");
   ok(!isChecked(debuggr), "debugger button state 2");
+  ok(!isChecked(styleeditor), "styleeditor button state 2");
 
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(isChecked(webconsole), "web console button state 3");
   ok(isChecked(inspector), "inspector button state 3");
   ok(!isChecked(debuggr), "debugger button state 3");
+  ok(!isChecked(styleeditor), "styleeditor button state 3");
 
   // Christmas tree!
 
   // The web console opens synchronously, but closes asynchronously.
   let hud = imported.HUDService.getHudByWindow(content);
   imported.HUDService.disableAnimation(hud.hudId);
 
   document.getElementById("Tools:WebConsole").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 6");
   ok(isChecked(inspector), "inspector button state 6");
   ok(!isChecked(debuggr), "debugger button state 6");
+  ok(!isChecked(styleeditor), "styleeditor button state 6");
 
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 7");
   ok(!isChecked(inspector), "inspector button state 7");
   ok(!isChecked(debuggr), "debugger button state 7");
+  ok(!isChecked(styleeditor), "styleeditor button state 7");
 
   // All closed
 
   // Check we can open and close and retain button state
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 8");
   ok(isChecked(inspector), "inspector button state 8");
   ok(!isChecked(debuggr), "debugger button state 8");
+  ok(!isChecked(styleeditor), "styleeditor button state 8");
 
-  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
-  document.getElementById("Tools:DevToolbar").doCommand();
+
+  // Test Style Editor
+  document.getElementById("Tools:StyleEditor").doCommand();
+
+  ok(!isChecked(webconsole), "web console button state 9");
+  ok(isChecked(inspector), "inspector button state 9");
+  ok(!isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(styleeditor), "styleeditor button state 9");
+
+  // Test Debugger
+  document.getElementById("Tools:Debugger").doCommand();
+
+  ok(!isChecked(webconsole), "web console button state 9");
+  ok(isChecked(inspector), "inspector button state 9");
+  ok(isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(styleeditor), "styleeditor button state 9");
+
+  addTab("about:blank", function(browser, tab) {
+    info("Opening a new tab");
+
+    ok(!isChecked(webconsole), "web console button state 10");
+    ok(!isChecked(inspector), "inspector button state 10");
+    ok(!isChecked(debuggr), "debugger button state 10");
+    ok(!isChecked(styleeditor), "styleeditor button state 10");
+
+    gBrowser.removeCurrentTab();
+
+    oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
+    document.getElementById("Tools:DevToolbar").doCommand();
+  });
 }
 
 function checkClosed() {
   ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkClosed");
 
   // Check we grok state even when closed
   document.getElementById("Tools:WebConsole").doCommand();
 
@@ -97,20 +132,25 @@ function checkClosed() {
 }
 
 function checkReOpen() {
   ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkReOpen");
 
   let webconsole = document.getElementById("developer-toolbar-webconsole");
   let inspector = document.getElementById("developer-toolbar-inspector");
   let debuggr = document.getElementById("developer-toolbar-debugger");
+  let styleeditor = document.getElementById("developer-toolbar-styleeditor");
 
-  ok(isChecked(webconsole), "web console button state 9");
-  ok(isChecked(inspector), "inspector button state 9");
-  ok(!isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(webconsole), "web console button state 99");
+  ok(isChecked(inspector), "inspector button state 99");
+  ok(isChecked(debuggr), "debugger button state 99");
+  ok(isChecked(styleeditor), "styleeditor button state 99");
+
+  // We close the style editor (not automatically closed)
+  document.getElementById("Tools:StyleEditor").doCommand();
 
   oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkReClosed));
   document.getElementById("developer-toolbar-closebutton").doCommand();
 }
 
 function checkReClosed() {
   ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkReClosed");
 
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -1,16 +1,16 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags"];
+const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags", "StyleEditorManager"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
                    .getService(Ci.inIDOMUtils);
 
@@ -1155,8 +1155,138 @@ function setupBracketCompletion(aSourceE
                   getInterface(Ci.nsIDOMWindowUtils);
     let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
     utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
     utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
     // and rewind caret
     aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
   }, false);
 }
+
+/**
+  * Manage the different editors instances.
+  */
+
+function StyleEditorManager(aWindow) {
+  this.chromeWindow = aWindow;
+  this.listenToTabs();
+  this.editors = new WeakMap();
+}
+
+StyleEditorManager.prototype = {
+
+  /**
+   * Get the editor for a specific content window.
+   */
+  getEditorForWindow: function SEM_getEditorForWindow(aContentWindow) {
+    return this.editors.get(aContentWindow);
+  },
+
+  /**
+   * Focus the editor and select a stylesheet.
+   *
+   * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
+   * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
+   */
+  selectEditor: function SEM_selectEditor(aWindow, aSelectedStyleSheet, aLine, aCol) {
+    if (aSelectedStyleSheet) {
+      aWindow.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
+    }
+    aWindow.focus();
+  },
+
+  /**
+   * Open a new editor.
+   *
+   * @param {Window} content window.
+   * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
+   * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
+   */
+  newEditor: function SEM_newEditor(aContentWindow, aSelectedStyleSheet, aLine, aCol) {
+    const CHROME_URL = "chrome://browser/content/styleeditor.xul";
+    const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
+
+    let args = {
+      contentWindow: aContentWindow,
+      selectedStyleSheet: aSelectedStyleSheet,
+      line: aLine,
+      col: aCol
+    };
+    args.wrappedJSObject = args;
+    let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
+                                              CHROME_WINDOW_FLAGS, args);
+
+    chromeWindow.onunload = function() {
+      if (chromeWindow.location == CHROME_URL) {
+        // not about:blank being unloaded
+        this.unregisterEditor(aContentWindow);
+      }
+    }.bind(this);
+    chromeWindow.focus();
+
+    this.editors.set(aContentWindow, chromeWindow);
+
+    this.refreshCommand();
+
+    return chromeWindow;
+  },
+
+  /**
+   * Toggle an editor.
+   *
+   * @param {Window} associated content window.
+   */
+  toggleEditor: function SEM_toggleEditor(aContentWindow) {
+    let editor = this.getEditorForWindow(aContentWindow);
+    if (editor) {
+      editor.close();
+    } else {
+      this.newEditor(aContentWindow);
+    }
+  },
+
+  /**
+   * Close an editor.
+   *
+   * @param {Window} associated content window.
+   */
+  unregisterEditor: function SEM_unregisterEditor(aContentWindow) {
+    let chromeWindow = this.editors.get(aContentWindow);
+    if (chromeWindow) {
+      chromeWindow.close();
+    }
+    this.editors.delete(aContentWindow);
+    this.refreshCommand();
+  },
+
+  /**
+   * Update the status of tool's menuitems and buttons.
+   */
+  refreshCommand: function SEM_refreshCommand() {
+    let contentWindow = this.chromeWindow.gBrowser.contentWindow;
+    let command = this.chromeWindow.document.getElementById("Tools:StyleEditor");
+
+    let win = this.getEditorForWindow(contentWindow);
+    if (win) {
+      command.setAttribute("checked", "true");
+    } else {
+      command.setAttribute("checked", "false");
+    }
+  },
+
+  /**
+   * Trigger refreshCommand when needed.
+   */
+  listenToTabs: function SEM_listenToTabs() {
+    let win = this.chromeWindow;
+    let tabs = win.gBrowser.tabContainer;
+
+    let bound_refreshCommand = this.refreshCommand.bind(this);
+    tabs.addEventListener("TabSelect", bound_refreshCommand, true);
+
+    win.addEventListener("unload", function onClose(aEvent) {
+      tabs.removeEventListener("TabSelect", bound_refreshCommand, true);
+      win.removeEventListener("unload", onClose, false);
+    }, false);
+  },
+}
--- a/browser/devtools/webconsole/HUDService-content.js
+++ b/browser/devtools/webconsole/HUDService-content.js
@@ -492,17 +492,17 @@ let Manager = {
 
     this.hudId = null;
     this._messageHandlers = null;
 
     Manager = ConsoleAPIObserver = JSTerm = ConsoleListener = NetworkMonitor =
       NetworkResponseListener = ConsoleProgressListener = null;
 
     XPCOMUtils = gConsoleStorage = WebConsoleUtils = l10n = JSPropertyProvider =
-      NetworkHelper = NetUtil = activityDistributor = null;
+      null;
   },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 // JavaScript Terminal
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -442,21 +442,26 @@
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
+#ifdef MOZ_SERVICES_AITC
+@BINPATH@/components/AitcComponents.manifest
+@BINPATH@/components/Aitc.js
+#endif
+#ifdef MOZ_SERVICES_NOTIFICATIONS
+@BINPATH@/components/NotificationsComponents.manifest
+#endif
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
-@BINPATH@/components/AitcComponents.manifest
 @BINPATH@/components/Weave.js
-@BINPATH@/components/Aitc.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/Webapps.js
@@ -527,19 +532,24 @@
 @BINPATH@/icons/*.png
 #endif
 #endif
 
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @BINPATH@/@PREF_DIR@/firefox.js
 @BINPATH@/@PREF_DIR@/firefox-branding.js
+#ifdef MOZ_SERVICES_AITC
+@BINPATH@/@PREF_DIR@/services-aitc.js
+#endif
+#ifdef MOZ_SERVICES_NOTIFICATIONS
+@BINPATH@/@PREF_DIR@/services-notifications.js
+#endif
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/@PREF_DIR@/services-sync.js
-@BINPATH@/@PREF_DIR@/services-aitc.js
 #endif
 @BINPATH@/greprefs.js
 @BINPATH@/defaults/autoconfig/platform.js
 @BINPATH@/defaults/autoconfig/prefcalls.js
 #ifndef LIBXUL_SDK
 ; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325)
 @BINPATH@/defaults/pref/channel-prefs.js
 #else
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -237,17 +237,16 @@ These should match what Safari and other
 
 <!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
 <!ENTITY devToolbarMenu.label              "Developer Toolbar">
 <!ENTITY devToolbarMenu.accesskey          "v">
 <!ENTITY devToolbar.commandkey             "v">
 
 <!ENTITY webConsoleButton.label "Web Console">
 <!ENTITY inspectorButton.label "Inspector">
-<!ENTITY scriptsButton.label "Scripts">
 
 <!ENTITY inspectorHTMLCopyInner.label       "Copy Inner HTML">
 <!ENTITY inspectorHTMLCopyInner.accesskey   "I">
 
 <!ENTITY inspectorHTMLCopyOuter.label       "Copy Outer HTML">
 <!ENTITY inspectorHTMLCopyOuter.accesskey   "O">
 
 <!ENTITY inspectorHTMLDelete.label          "Delete Node">
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -625,16 +625,18 @@ WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCL
 
 HAVE_ARM_SIMD = @HAVE_ARM_SIMD@
 HAVE_ARM_NEON = @HAVE_ARM_NEON@
 HAVE_GCC_ALIGN_ARG_POINTER = @HAVE_GCC_ALIGN_ARG_POINTER@
 HAVE_COMPILER_FLAG_MSSSE3 = @HAVE_COMPILER_FLAG_MSSSE3@
 
 MOZ_THEME_FASTSTRIPE = @MOZ_THEME_FASTSTRIPE@
 
+MOZ_SERVICES_AITC = @MOZ_SERVICES_AITC@
+MOZ_SERVICES_NOTIFICATIONS = @MOZ_SERVICES_NOTIFICATIONS@
 MOZ_SERVICES_SYNC = @MOZ_SERVICES_SYNC@
 
 MOZ_WEBAPP_RUNTIME = @MOZ_WEBAPP_RUNTIME@
 
 MOZ_OFFICIAL_BRANDING = @MOZ_OFFICIAL_BRANDING@
 
 HAVE_CLOCK_MONOTONIC = @HAVE_CLOCK_MONOTONIC@
 REALTIME_LIBS = @REALTIME_LIBS@
--- a/configure.in
+++ b/configure.in
@@ -5591,16 +5591,19 @@ if test -n "$MOZ_CUBEB"; then
         dnl No Android implementation of libcubeb yet.
         ;;
     *-linux*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
     *-mingw*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
+    *-darwin*)
+        AC_DEFINE(MOZ_CUBEB)
+        ;;
     *-openbsd*)
         AC_DEFINE(MOZ_CUBEB)
         ;;
     *)
         dnl Other targets will be enabled soon.
         ;;
     esac
 fi
@@ -8243,16 +8246,28 @@ fi
 dnl NECKO_ configuration options are not global
 _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_"
 
 dnl Build Places if required
 if test "$MOZ_PLACES"; then
   AC_DEFINE(MOZ_PLACES)
 fi
 
+dnl Build Apps in the Cloud (AITC) if required
+AC_SUBST(MOZ_SERVICES_AITC)
+if test -n "$MOZ_SERVICES_AITC"; then
+  AC_DEFINE(MOZ_SERVICES_AITC)
+fi
+
+dnl Build Notifications if required
+AC_SUBST(MOZ_SERVICES_NOTIFICATIONS)
+if test -n "$MOZ_SERVICES_NOTIFICATIONS"; then
+  AC_DEFINE(MOZ_SERVICES_NOTIFICATIONS)
+fi
+
 dnl Build Sync Services if required
 AC_SUBST(MOZ_SERVICES_SYNC)
 if test -n "$MOZ_SERVICES_SYNC"; then
   AC_DEFINE(MOZ_SERVICES_SYNC)
 fi
 
 dnl ========================================================
 if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC"; then
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -115,17 +115,17 @@ nsContentSink::nsContentSink()
   NS_ASSERTION(!mParsing, "What?");
   NS_ASSERTION(mLastSampledUserEventTime == 0, "What?");
   NS_ASSERTION(mDeflectedCount == 0, "What?");
   NS_ASSERTION(!mDroppedTimer, "What?");
   NS_ASSERTION(mInMonolithicContainer == 0, "What?");
   NS_ASSERTION(mInNotification == 0, "What?");
   NS_ASSERTION(!mDeferredLayoutStart, "What?");
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (!gContentSinkLogModuleInfo) {
     gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink");
   }
 #endif
 }
 
 nsContentSink::~nsContentSink()
 {
@@ -920,17 +920,17 @@ nsContentSink::SelectDocAppCache(nsIAppl
       // entry. The entry will be marked as foreign to avoid loading it again.
 
       *aAction = CACHE_SELECTION_RELOAD;
     }
     else {
       // The http manifest attribute URI is equal to the manifest URI of
       // the cache the document was loaded from - associate the document with
       // that cache and invoke the cache update process.
-#ifdef NS_DEBUG
+#ifdef DEBUG
       nsCAutoString docURISpec, clientID;
       mDocumentURI->GetAsciiSpec(docURISpec);
       aLoadApplicationCache->GetClientID(clientID);
       SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
           ("Selection: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
 #endif
 
       rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
@@ -974,17 +974,17 @@ nsContentSink::SelectDocAppCacheNoManife
   if (aLoadApplicationCache) {
     // The document was loaded from an application cache, use that
     // application cache as the document's application cache.
     nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
       do_QueryInterface(mDocument);
     NS_ASSERTION(applicationCacheDocument,
                  "mDocument must implement nsIApplicationCacheContainer.");
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     nsCAutoString docURISpec, clientID;
     mDocumentURI->GetAsciiSpec(docURISpec);
     aLoadApplicationCache->GetClientID(clientID);
     SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
         ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
 #endif
 
     rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -47,17 +47,17 @@ class nsScriptLoader;
 class nsIApplicationCache;
 
 namespace mozilla {
 namespace css {
 class Loader;
 }
 }
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 
 extern PRLogModuleInfo* gContentSinkLogModuleInfo;
 
 #define SINK_TRACE_CALLS              0x1
 #define SINK_TRACE_REFLOW             0x2
 #define SINK_ALWAYS_REFLOW            0x4
 
 #define SINK_LOG_TEST(_lm, _bit) (PRIntn((_lm)->level) & (_bit))
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -405,16 +405,25 @@ nsIdentifierMapEntry::AddNameElement(nsI
 void
 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
 {
   if (mNameContentList) {
     mNameContentList->RemoveElement(aElement);
   }
 }
 
+// static
+size_t
+nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
+                                          nsMallocSizeOfFun aMallocSizeOf,
+                                          void*)
+{
+  return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
 // Helper structs for the content->subdoc map
 
 class SubDocMapEntry : public PLDHashEntryHdr
 {
 public:
   // Both of these are strong references
   Element *mKey; // must be first, to look like PLDHashEntryStub
   nsIDocument *mSubDocument;
@@ -9677,17 +9686,18 @@ nsDocument::DocSizeOfExcludingThis(nsWin
     mAttrStyleSheet ?
     mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
     0;
 
   aWindowSizes->mDOMOther +=
     mStyledLinks.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
 
   aWindowSizes->mDOMOther +=
-    mIdentifierMap.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
+    mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis,
+                                       aWindowSizes->mMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
 
 bool
 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -210,16 +210,20 @@ public:
     {
       return mozilla::HashGeneric(aKey->mCallback, aKey->mData);
     }
     enum { ALLOW_MEMMOVE = true };
     
     ChangeCallback mKey;
   };
 
+  static size_t SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
+                                    nsMallocSizeOfFun aMallocSizeOf,
+                                    void* aArg);
+
 private:
   void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
                            bool aImageOnly = false);
 
   // empty if there are no elements with this ID.
   // The elements are stored as weak pointers.
   nsSmallVoidArray mIdContentList;
   nsRefPtr<nsBaseContentList> mNameContentList;
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -80,17 +80,17 @@
 #include "nsIElementObserver.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozAutoDocUpdate.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static PRLogModuleInfo* gSinkLogModuleInfo;
 
 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
   _obj->SinkTraceNode(_bit, _msg, _tag, _sp, this)
 
 #else
 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj)
 #endif
@@ -183,17 +183,17 @@ protected:
   // If aCheckIfPresent is true, will only set an attribute in cases
   // when it's not already set.
   nsresult AddAttributes(const nsIParserNode& aNode, nsIContent* aContent,
                          bool aNotify = false,
                          bool aCheckIfPresent = false);
   already_AddRefed<nsGenericHTMLElement>
   CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   void SinkTraceNode(PRUint32 aBit,
                      const char* aMsg,
                      const nsHTMLTag aTag,
                      PRInt32 aStackPos,
                      void* aThis);
 #endif
 
   nsCOMPtr<nsIHTMLDocument> mHTMLDocument;
@@ -238,17 +238,17 @@ protected:
 
   void NotifyInsert(nsIContent* aContent,
                     nsIContent* aChildContent,
                     PRInt32 aIndexInContainer);
   void NotifyRootInsertion();
   
   bool IsMonolithicContainer(nsHTMLTag aTag);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   void ForceReflow();
 #endif
 };
 
 class SinkContext
 {
 public:
   SinkContext(HTMLContentSink* aSink);
@@ -308,17 +308,17 @@ public:
   PRInt32 mTextSize;
 
 private:
   bool mLastTextCharWasCR;
 };
 
 //----------------------------------------------------------------------
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 void
 HTMLContentSink::SinkTraceNode(PRUint32 aBit,
                                const char* aMsg,
                                const nsHTMLTag aTag,
                                PRInt32 aStackPos,
                                void* aThis)
 {
   if (SINK_LOG_TEST(gSinkLogModuleInfo, aBit)) {
@@ -577,17 +577,17 @@ SinkContext::DidAddContent(nsIContent* a
   // an insertion happen, we need to do an immediate
   // notification for that insertion.
   if (0 < mStackPos &&
       mStack[mStackPos - 1].mInsertionPoint != -1 &&
       mStack[mStackPos - 1].mNumFlushed <
       mStack[mStackPos - 1].mContent->GetChildCount()) {
     nsIContent* parent = mStack[mStackPos - 1].mContent;
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     // Tracing code
     nsIParserService *parserService = nsContentUtils::GetParserService();
     if (parserService) {
       nsHTMLTag tag = nsHTMLTag(mStack[mStackPos - 1].mType);
       NS_ConvertUTF16toUTF8 str(parserService->HTMLIdToStringTag(tag));
 
       SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
                  ("SinkContext::DidAddContent: Insertion notification for "
@@ -752,17 +752,17 @@ SinkContext::CloseContainer(const nsHTML
   // If we're in a state where we do append notifications as
   // we go up the tree, and we're at the level where the next
   // notification needs to be done, do the notification.
   if (mNotifyLevel >= mStackPos) {
     // Check to see if new content has been added after our last
     // notification
 
     if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
-#ifdef NS_DEBUG
+#ifdef DEBUG
       {
         // Tracing code
         SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
                    ("SinkContext::CloseContainer: reflow on notifyImmediate "
                     "tag=%s newIndex=%d stackPos=%d",
                     nsAtomCString(mStack[mStackPos].mContent->Tag()).get(),
                     mStack[mStackPos].mNumFlushed, mStackPos));
       }
@@ -1063,17 +1063,17 @@ SinkContext::FlushTags()
     PRUint32 childCount;
     nsGenericHTMLElement* content;
 
     while (stackPos < mStackPos) {
       content = mStack[stackPos].mContent;
       childCount = content->GetChildCount();
 
       if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
-#ifdef NS_DEBUG
+#ifdef DEBUG
         {
           // Tracing code
           SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
                      ("SinkContext::FlushTags: tag=%s from newindex=%d at "
                       "stackPos=%d",
                       nsAtomCString(mStack[stackPos].mContent->Tag()).get(),
                       mStack[stackPos].mNumFlushed, stackPos));
         }
@@ -1237,17 +1237,17 @@ NS_NewHTMLContentSink(nsIHTMLContentSink
   return NS_OK;
 }
 
 HTMLContentSink::HTMLContentSink()
 {
   // Note: operator new zeros our memory
 
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (!gSinkLogModuleInfo) {
     gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink");
   }
 #endif
 }
 
 HTMLContentSink::~HTMLContentSink()
 {
@@ -1421,17 +1421,17 @@ HTMLContentSink::Init(nsIDocument* aDoc,
 
   mRoot->AppendChildTo(mHead, false);
 
   mCurrentContext = new SinkContext(this);
   NS_ENSURE_TRUE(mCurrentContext, NS_ERROR_OUT_OF_MEMORY);
   mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
   mContextStack.AppendElement(mCurrentContext);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   nsCAutoString spec;
   (void)aURI->GetSpec(spec);
   SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_CALLS,
              ("HTMLContentSink::Init: this=%p url='%s'",
               this, spec.get()));
 #endif
   return NS_OK;
 }
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2868,18 +2868,18 @@ static const struct MidasCommand gMidasC
   { "italic",        "cmd_italic",          "", true,  false },
   { "underline",     "cmd_underline",       "", true,  false },
   { "strikethrough", "cmd_strikethrough",   "", true,  false },
   { "subscript",     "cmd_subscript",       "", true,  false },
   { "superscript",   "cmd_superscript",     "", true,  false },
   { "cut",           "cmd_cut",             "", true,  false },
   { "copy",          "cmd_copy",            "", true,  false },
   { "paste",         "cmd_paste",           "", true,  false },
-  { "delete",        "cmd_delete",          "", true,  false },
-  { "forwarddelete", "cmd_forwardDelete",   "", true,  false },
+  { "delete",        "cmd_deleteCharBackward", "", true,  false },
+  { "forwarddelete", "cmd_deleteCharForward", "", true,  false },
   { "selectall",     "cmd_selectAll",       "", true,  false },
   { "undo",          "cmd_undo",            "", true,  false },
   { "redo",          "cmd_redo",            "", true,  false },
   { "indent",        "cmd_indent",          "", true,  false },
   { "outdent",       "cmd_outdent",         "", true,  false },
   { "backcolor",     "cmd_highlight",       "", false, false },
   { "forecolor",     "cmd_fontColor",       "", false, false },
   { "hilitecolor",   "cmd_highlight",       "", false, false },
@@ -3184,16 +3184,23 @@ nsHTMLDocument::ExecCommand(const nsAStr
        cmdToDispatch.EqualsLiteral("cmd_insertImageNoUI") ||
        cmdToDispatch.EqualsLiteral("cmd_insertLinkNoUI") ||
        cmdToDispatch.EqualsLiteral("cmd_paragraphState")) &&
       paramStr.IsEmpty()) {
     // Invalid value, return false
     return NS_OK;
   }
 
+  // Return false for disabled commands (bug 760052)
+  bool enabled = false;
+  cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &enabled);
+  if (!enabled) {
+    return NS_OK;
+  }
+
   if (!isBool && paramStr.IsEmpty()) {
     rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
   } else {
     // we have a command that requires a parameter, create params
     nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
                                             NS_COMMAND_PARAMS_CONTRACTID, &rv);
     NS_ENSURE_TRUE(cmdParams, NS_ERROR_OUT_OF_MEMORY);
 
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -1209,17 +1209,17 @@ nsXMLContentSink::HandleDoctypeDecl(cons
   if (aCatalogData && mCSSLoader && mDocument) {
     // bug 124570 - we only expect additional agent sheets for now -- ignore
     // exit codes, error are not fatal here, just that the stylesheet won't apply
     nsCOMPtr<nsIURI> uri(do_QueryInterface(aCatalogData));
     if (uri) {
       nsRefPtr<nsCSSStyleSheet> sheet;
       mCSSLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet));
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
       nsCAutoString uriStr;
       uri->GetSpec(uriStr);
       printf("Loading catalog stylesheet: %s ... %s\n", uriStr.get(), sheet.get() ? "Done" : "Failed");
 #endif
       if (sheet) {
         mDocument->BeginUpdate(UPDATE_STYLE);
         mDocument->AddCatalogStyleSheet(sheet);
         mDocument->EndUpdate(UPDATE_STYLE);
--- a/content/xul/document/src/nsXULCommandDispatcher.cpp
+++ b/content/xul/document/src/nsXULCommandDispatcher.cpp
@@ -266,17 +266,17 @@ nsXULCommandDispatcher::AddCommandUpdate
   }
 
   Updater* updater = mUpdaters;
   Updater** link = &mUpdaters;
 
   while (updater) {
     if (updater->mElement == aElement) {
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
       if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
         nsCAutoString eventsC, targetsC, aeventsC, atargetsC; 
         eventsC.AssignWithConversion(updater->mEvents);
         targetsC.AssignWithConversion(updater->mTargets);
         CopyUTF16toUTF8(aEvents, aeventsC);
         CopyUTF16toUTF8(aTargets, atargetsC);
         PR_LOG(gLog, PR_LOG_NOTICE,
                ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)",
@@ -294,17 +294,17 @@ nsXULCommandDispatcher::AddCommandUpdate
       updater->mEvents  = aEvents;
       updater->mTargets = aTargets;
       return NS_OK;
     }
 
     link = &(updater->mNext);
     updater = updater->mNext;
   }
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
     nsCAutoString aeventsC, atargetsC; 
     CopyUTF16toUTF8(aEvents, aeventsC);
     CopyUTF16toUTF8(aTargets, atargetsC);
 
     PR_LOG(gLog, PR_LOG_NOTICE,
            ("xulcmd[%p] add     %p(events=%s targets=%s)",
             this, aElement,
@@ -329,17 +329,17 @@ nsXULCommandDispatcher::RemoveCommandUpd
   if (! aElement)
     return NS_ERROR_NULL_POINTER;
 
   Updater* updater = mUpdaters;
   Updater** link = &mUpdaters;
 
   while (updater) {
     if (updater->mElement == aElement) {
-#ifdef NS_DEBUG
+#ifdef DEBUG
       if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
         nsCAutoString eventsC, targetsC; 
         eventsC.AssignWithConversion(updater->mEvents);
         targetsC.AssignWithConversion(updater->mTargets);
         PR_LOG(gLog, PR_LOG_NOTICE,
                ("xulcmd[%p] remove  %p(events=%s targets=%s)",
                 this, aElement,
                 eventsC.get(),
@@ -395,17 +395,17 @@ nsXULCommandDispatcher::UpdateCommands(c
     nsIContent* content = updaters[u];
 
     nsCOMPtr<nsIDocument> document = content->GetDocument();
 
     NS_ASSERTION(document != nsnull, "element has no document");
     if (! document)
       continue;
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     if (PR_LOG_TEST(gLog, PR_LOG_NOTICE)) {
       nsCAutoString aeventnameC; 
       CopyUTF16toUTF8(aEventName, aeventnameC);
       PR_LOG(gLog, PR_LOG_NOTICE,
              ("xulcmd[%p] update %p event=%s",
               this, content,
               aeventnameC.get()));
     }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10570,17 +10570,17 @@ nsDocShell::SetHistoryEntry(nsCOMPtr<nsI
             nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
             if (rootShell) { // if we're the root just set it, nothing to swap
                 SwapEntriesData data = { this, newRootEntry };
                 nsIDocShell *rootIDocShell =
                     static_cast<nsIDocShell*>(rootShell);
                 nsDocShell *rootDocShell = static_cast<nsDocShell*>
                                                       (rootIDocShell);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
                 nsresult rv =
 #endif
                 SetChildHistoryEntry(oldRootEntry, rootDocShell,
                                                    0, &data);
                 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
             }
         }
     }
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -394,17 +394,17 @@ let DOMApplicationRegistry = {
       result[id] = app;
     }
     aCallback(result);
   },
 
   updateApps: function(aRecords, aCallback) {
     for (let i = 0; i < aRecords.length; i++) {
       let record = aRecords[i];
-      if (record.deleted) {
+      if (record.hidden) {
         if (!this.webapps[record.id])
           continue;
         let origin = this.webapps[record.id].origin;
         delete this.webapps[record.id];
         let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", record.id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -608,17 +608,17 @@ static const char kDOMStringBundleURL[] 
 
 #define IDBEVENTTARGET_SCRIPTABLE_FLAGS                                       \
   (EVENTTARGET_SCRIPTABLE_FLAGS)
 
 #define DOMCLASSINFO_STANDARD_FLAGS                                           \
   (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT)
 
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
     eDOMClassInfo_##_class##_id,
 #else
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
   // nothing
 #endif
 
 /**
@@ -4530,17 +4530,17 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
   DOM_CLASSINFO_MAP_END
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   {
     PRUint32 i = ArrayLength(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
       return NS_ERROR_NOT_INITIALIZED;
@@ -5871,17 +5871,17 @@ DefineInterfaceConstants(JSContext *cx, 
       {
         if (!JS_NewNumberValue(cx, c->GetValue()->val.u32, &v)) {
           return NS_ERROR_UNEXPECTED;
         }
         break;
       }
       default:
       {
-#ifdef NS_DEBUG
+#ifdef DEBUG
         NS_ERROR("Non-numeric constant found in interface.");
 #endif
         continue;
       }
     }
 
     if (!::JS_DefineProperty(cx, obj, c->GetName(), v, nsnull, nsnull,
                              JSPROP_ENUMERATE | JSPROP_READONLY |
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -66,17 +66,17 @@ struct nsDOMClassInfoData
                                   // so be sure to mask if necessary!
   const nsIID *mProtoChainInterface;
   const nsIID **mInterfaces;
   PRUint32 mScriptableFlags : 31; // flags must not use more than 31 bits!
   PRUint32 mHasClassInterface : 1;
   PRUint32 mInterfacesBitmap;
   bool mChromeOnly;
   bool mDisabled;
-#ifdef NS_DEBUG
+#ifdef DEBUG
   PRUint32 mDebugID;
 #endif
 };
 
 struct nsExternalDOMClassInfoData : public nsDOMClassInfoData
 {
   const nsCID *mConstructorCID;
 };
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -252,17 +252,17 @@ static PRUint32             gSerialCount
 static PRUint32             gTimeoutsRecentlySet       = 0;
 static TimeStamp            gLastRecordedRecentTimeouts;
 #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
 
 #ifdef DEBUG_jst
 PRInt32 gTimeoutCnt                                    = 0;
 #endif
 
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 static bool                 gDOMWindowDumpEnabled      = false;
 #endif
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 #define DEBUG_PAGE_CACHE
 #endif
 
 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
@@ -708,17 +708,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
   if (gRefCnt == 1) {
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
     Preferences::AddBoolVarCache(&gDOMWindowDumpEnabled,
                                  "browser.dom.window.dump.enabled");
 #endif
     Preferences::AddIntVarCache(&gMinTimeoutValue,
                                 "dom.min_timeout_value",
                                 DEFAULT_MIN_TIMEOUT_VALUE);
     Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
                                 "dom.min_background_timeout_value",
@@ -4459,17 +4459,17 @@ nsGlobalWindow::GetFullScreen(bool* aFul
   // We are the root window, or something went wrong. Return our internal value.
   *aFullScreen = mFullScreen;
   return NS_OK;
 }
 
 bool
 nsGlobalWindow::DOMWindowDumpEnabled()
 {
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   // In optimized builds we check a pref that controls if we should
   // enable output from dump() or not, in debug builds it's always
   // enabled.
   return gDOMWindowDumpEnabled;
 #else
   return true;
 #endif
 }
@@ -8626,17 +8626,17 @@ nsGlobalWindow::OpenInternal(const nsASt
                              nsIDOMWindow **aReturn)
 {
   FORWARD_TO_OUTER(OpenInternal, (aUrl, aName, aOptions, aDialog,
                                   aContentModal, aCalledNoScript, aDoJSFixups,
                                   argv, aExtraArgument, aCalleePrincipal,
                                   aJSCallerContext, aReturn),
                    NS_ERROR_NOT_INITIALIZED);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   PRUint32 argc = 0;
   if (argv)
       argv->GetLength(&argc);
 #endif
   NS_PRECONDITION(!aExtraArgument || (!argv && argc == 0),
                   "Can't pass in arguments both ways");
   NS_PRECONDITION(!aCalledNoScript || (!argv && argc == 0),
                   "Can't pass JS args when called via the noscript methods");
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1679,17 +1679,17 @@ nsJSContext::JSObjectFromInterface(nsISu
 
   // Get the jsobject associated with this target
   // We don't wrap here because we trust the JS engine to wrap the target
   // later.
   jsval v;
   nsresult rv = nsContentUtils::WrapNative(mContext, aScope, aTarget, &v);
   NS_ENSURE_SUCCESS(rv, rv);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
   nsCOMPtr<nsISupports> native =
     nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
                                                     JSVAL_TO_OBJECT(v));
   NS_ASSERTION(native == targetSupp, "Native should be the target!");
 #endif
 
   *aRet = xpc_UnmarkGrayObject(JSVAL_TO_OBJECT(v));
@@ -2219,17 +2219,17 @@ nsJSContext::ConvertSupportsTojsvals(nsI
       } else {
         // And finally, support the nsISupportsPrimitives supplied
         // by the AppShell.  It generally will pass only strings, but
         // as we have code for handling all, we may as well use it.
         rv = AddSupportsPrimitiveTojsvals(arg, thisval);
         if (rv == NS_ERROR_NO_INTERFACE) {
           // something else - probably an event object or similar -
           // just wrap it.
-#ifdef NS_DEBUG
+#ifdef DEBUG
           // but first, check its not another nsISupportsPrimitive, as
           // these are now deprecated for use with script contexts.
           nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
           NS_ASSERTION(prim == nsnull,
                        "Don't pass nsISupportsPrimitives - use nsIVariant!");
 #endif
           nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
           jsval v;
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -231,19 +231,19 @@ CollectWindowReports(nsGlobalWindow *aWi
     size_t frameSize                                                    \
       = windowSizes.mArenaStats.FRAME_ID_STAT_FIELD(classname);         \
     if (frameSize < FRAME_SUNDRIES_THRESHOLD) {                         \
       frameSundriesSize += frameSize;                                   \
     } else {                                                            \
       REPORT("/layout/frames/" # classname, frameSize,                  \
              "Memory used by frames of "                                \
              "type " #classname " within a window.");                   \
-      aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname)     \
-        += frameSize;                                                   \
     }                                                                   \
+    aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname)       \
+      += frameSize;                                                     \
   }
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 
   if (frameSundriesSize > 0) {
     REPORT("/layout/frames/sundries", frameSundriesSize,
            "The sum of all memory used by frames which were too small "
            "to be shown individually.");
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2365,21 +2365,24 @@ class Tokenizer(object):
 
     def t_error(self, t):
         raise WebIDLError("Unrecognized Input",
                Location(lexer=self.lexer,
                         lineno=self.lexer.lineno,
                         lexpos=self.lexer.lexpos,
                         filename = self.filename))
 
-    def __init__(self, outputdir):
-        self.lexer = lex.lex(object=self,
-                             outputdir=outputdir,
-                             lextab='webidllex',
-                             reflags=re.DOTALL)
+    def __init__(self, outputdir, lexer=None):
+        if lexer:
+            self.lexer = lexer
+        else:
+            self.lexer = lex.lex(object=self,
+                                 outputdir=outputdir,
+                                 lextab='webidllex',
+                                 reflags=re.DOTALL)
 
 class Parser(Tokenizer):
     def getLocation(self, p, i):
         return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
 
     def globalScope(self):
         return self._globalScope
 
@@ -3454,21 +3457,23 @@ class Parser(Tokenizer):
         p[0] = (p[1], p[3], p[5])
 
     def p_error(self, p):
         if not p:
             raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None)
         else:
             raise WebIDLError("invalid syntax", Location(self.lexer, p.lineno, p.lexpos, self._filename))
 
-    def __init__(self, outputdir=''):
-        Tokenizer.__init__(self, outputdir)
+    def __init__(self, outputdir='', lexer=None):
+        Tokenizer.__init__(self, outputdir, lexer)
         self.parser = yacc.yacc(module=self,
                                 outputdir=outputdir,
-                                tabmodule='webidlyacc')
+                                tabmodule='webidlyacc',
+                                errorlog=yacc.NullLogger(),
+                                picklefile='WebIDLGrammar.pkl')
         self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
         self._installBuiltins(self._globalScope)
         self._productions = []
 
         self._filename = "<builtin>"
         self.lexer.input(Parser._builtins)
         self._filename = None
 
@@ -3530,14 +3535,14 @@ class Parser(Tokenizer):
         result = []
         for p in self._productions:
             if p not in seen:
                 seen.add(p)
                 result.append(p)
         return result
 
     def reset(self):
-        return Parser()
+        return Parser(lexer=self.lexer)
 
     # Builtin IDL defined by WebIDL
     _builtins = """
         typedef unsigned long long DOMTimeStamp;
     """
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -53,16 +53,17 @@ bindinggen_dependencies := \
 $(CPPSRCS): ../%Binding.cpp: $(bindinggen_dependencies) \
                              ../%.webidl \
                              $(NULL)
 	$(MAKE) -C .. $*Binding.h
 	$(MAKE) -C .. $*Binding.cpp
 
 _TEST_FILES = \
   test_enums.html \
+  test_integers.html \
   test_interfaceToString.html \
   test_lookupGetter.html \
   test_InstanceOf.html \
   test_traceProtos.html \
   $(NULL)
 
 
 libs:: $(_TEST_FILES)
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_integers.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script type="application/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">
+  <canvas id="c" width="1" height="1"></canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+  function testInt64NonFinite(arg) {
+    // We can use a WebGLRenderingContext to test conversion to 64-bit signed
+    // ints edge cases.
+    try {
+      var gl = $("c").getContext("experimental-webgl");
+    } catch (ex) {
+      // No WebGL support on MacOS 10.5.  Just skip this test
+      todo(false, "WebGL not supported");
+      return;
+    }
+    is(gl.getError(), 0, "Should not start in an error state");
+
+    var b = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, b);
+
+    var a = new Float32Array(1);
+    gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW);
+
+    gl.bufferSubData(gl.ARRAY_BUFFER, arg, a);
+
+    is(gl.getError(), 0, "Should have treated non-finite double as 0");
+  }
+
+  testInt64NonFinite(NaN);
+  testInt64NonFinite(Infinity);
+  testInt64NonFinite(-Infinity);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -89,16 +89,22 @@ BrowserElementChild.prototype = {
 
     if (!!appManifestURL) {
       windowUtils.setIsApp(true);
       windowUtils.setApp(appManifestURL);
     } else {
       windowUtils.setIsApp(false);
     }
 
+    // A cache of the menuitem dom objects keyed by the id we generate
+    // and pass to the embedder
+    this._ctxHandlers = {};
+    // Counter of contextmenu events fired
+    this._ctxCounter = 0;
+
     addEventListener('DOMTitleChanged',
                      this._titleChangedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMLinkAdded',
                      this._iconChangedHandler.bind(this),
                      /* useCapture = */ true,
@@ -111,16 +117,17 @@ BrowserElementChild.prototype = {
 
     addMsgListener("get-screenshot", this._recvGetScreenshot);
     addMsgListener("set-visible", this._recvSetVisible);
     addMsgListener("get-can-go-back", this._recvCanGoBack);
     addMsgListener("get-can-go-forward", this._recvCanGoForward);
     addMsgListener("go-back", this._recvGoBack);
     addMsgListener("go-forward", this._recvGoForward);
     addMsgListener("unblock-modal-prompt", this._recvStopWaiting);
+    addMsgListener("fire-ctx-callback", this._recvFireCtxCallback);
 
     let els = Cc["@mozilla.org/eventlistenerservice;1"]
                 .getService(Ci.nsIEventListenerService);
 
     // We are using the system group for those events so if something in the
     // content called .stopPropagation() this will still be called.
     els.addSystemEventListener(global, 'keydown',
                                this._keyEventHandler.bind(this),
@@ -129,16 +136,19 @@ BrowserElementChild.prototype = {
                                this._keyEventHandler.bind(this),
                                /* useCapture = */ true);
     els.addSystemEventListener(global, 'keyup',
                                this._keyEventHandler.bind(this),
                                /* useCapture = */ true);
     els.addSystemEventListener(global, 'DOMWindowClose',
                                this._closeHandler.bind(this),
                                /* useCapture = */ false);
+    els.addSystemEventListener(global, 'contextmenu',
+                               this._contextmenuHandler.bind(this),
+                               /* useCapture = */ false);
   },
 
   _tryGetInnerWindowID: function(win) {
     let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils);
     try {
       return utils.currentInnerWindowID;
     }
@@ -307,32 +317,123 @@ BrowserElementChild.prototype = {
 
     debug("Closing window " + win);
     sendAsyncMsg('close');
 
     // Inform the window implementation that we handled this close ourselves.
     e.preventDefault();
   },
 
+  _contextmenuHandler: function(e) {
+    debug("Got contextmenu");
+
+    if (e.defaultPrevented) {
+      return;
+    }
+
+    e.preventDefault();
+
+    this._ctxCounter++;
+    this._ctxHandlers = {};
+
+    var elem = e.target;
+    var menuData = {systemTargets: [], contextmenu: null};
+    var ctxMenuId = null;
+
+    while (elem && elem.hasAttribute) {
+      var ctxData = this._getSystemCtxMenuData(elem);
+      if (ctxData) {
+        menuData.systemTargets.push({
+          nodeName: elem.nodeName,
+          data: ctxData
+        });
+      }
+
+      if (!ctxMenuId && elem.hasAttribute('contextmenu')) {
+        ctxMenuId = elem.getAttribute('contextmenu');
+      }
+      elem = elem.parentNode;
+    }
+
+    if (ctxMenuId) {
+      var menu = e.target.ownerDocument.getElementById(ctxMenuId);
+      if (menu) {
+        menuData.contextmenu = this._buildMenuObj(menu, '');
+      }
+    }
+    sendAsyncMsg('contextmenu', menuData);
+  },
+
+  _getSystemCtxMenuData: function(elem) {
+    if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
+        (elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href)) {
+      return elem.href;
+    }
+    if (elem instanceof Ci.nsIImageLoadingContent && elem.currentURI) {
+      return elem.currentURI.spec;
+    }
+    if ((elem instanceof Ci.nsIDOMHTMLMediaElement) ||
+        (elem instanceof Ci.nsIDOMHTMLImageElement)) {
+      return elem.currentSrc || elem.src;
+    }
+    return false;
+  },
+
   _recvGetScreenshot: function(data) {
     debug("Received getScreenshot message: (" + data.json.id + ")");
     var canvas = content.document
       .createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     var ctx = canvas.getContext("2d");
     canvas.mozOpaque = true;
     canvas.height = content.innerHeight;
     canvas.width = content.innerWidth;
     ctx.drawWindow(content, 0, 0, content.innerWidth,
                    content.innerHeight, "rgb(255,255,255)");
     sendAsyncMsg('got-screenshot', {
       id: data.json.id,
       rv: canvas.toDataURL("image/png")
     });
   },
 
+  _recvFireCtxCallback: function(data) {
+    debug("Received fireCtxCallback message: (" + data.json.menuitem + ")");
+    // We silently ignore if the embedder uses an incorrect id in the callback
+    if (data.json.menuitem in this._ctxHandlers) {
+      this._ctxHandlers[data.json.menuitem].click();
+      this._ctxHandlers = {};
+    } else {
+      debug("Ignored invalid contextmenu invokation");
+    }
+  },
+
+  _buildMenuObj: function(menu, idPrefix) {
+    function maybeCopyAttribute(src, target, attribute) {
+      if (src.getAttribute(attribute)) {
+        target[attribute] = src.getAttribute(attribute);
+      }
+    }
+
+    var menuObj = {type: 'menu', items: []};
+    maybeCopyAttribute(menu, menuObj, 'label');
+
+    for (var i = 0, child; child = menu.children[i++];) {
+      if (child.nodeName === 'MENU') {
+        menuObj.items.push(this._buildMenuObj(child, idPrefix + i + '_'));
+      } else if (child.nodeName === 'MENUITEM') {
+        var id = this._ctxCounter + '_' + idPrefix + i;
+        var menuitem = {id: id, type: 'menuitem'};
+        maybeCopyAttribute(child, menuitem, 'label');
+        maybeCopyAttribute(child, menuitem, 'icon');
+        this._ctxHandlers[id] = child;
+        menuObj.items.push(menuitem);
+      }
+    }
+    return menuObj;
+  },
+
   _recvSetVisible: function(data) {
     debug("Received setVisible message: (" + data.json.visible + ")");
     if (docShell.isActive !== data.json.visible) {
       docShell.isActive = data.json.visible;
     }
   },
 
   _recvCanGoBack: function(data) {
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementChild.js.orig
@@ -0,0 +1,413 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let Cu = Components.utils;
+let Ci = Components.interfaces;
+let Cc = Components.classes;
+let Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
+
+// Event whitelisted for bubbling.
+let whitelistedEvents = [
+  Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE,   // Back button.
+  Ci.nsIDOMKeyEvent.DOM_VK_SLEEP,    // Power button.
+  Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU,
+  Ci.nsIDOMKeyEvent.DOM_VK_F5,       // Search button.
+  Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP,  // Volume up.
+  Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down.
+];
+
+function debug(msg) {
+  //dump("BrowserElementChild - " + msg + "\n");
+}
+
+function sendAsyncMsg(msg, data) {
+  sendAsyncMessage('browser-element-api:' + msg, data);
+}
+
+function sendSyncMsg(msg, data) {
+  return sendSyncMessage('browser-element-api:' + msg, data);
+}
+
+/**
+ * The BrowserElementChild implements one half of <iframe mozbrowser>.
+ * (The other half is, unsurprisingly, BrowserElementParent.)
+ *
+ * This script is injected into an <iframe mozbrowser> via
+ * nsIMessageManager::LoadFrameScript().
+ *
+ * Our job here is to listen for events within this frame and bubble them up to
+ * the parent process.
+ */
+
+var global = this;
+
+function BrowserElementChild() {
+  // Maps outer window id --> weak ref to window.  Used by modal dialog code.
+  this._windowIDDict = {};
+
+  this._init();
+};
+
+BrowserElementChild.prototype = {
+  _init: function() {
+    debug("Starting up.");
+    sendAsyncMsg("hello");
+
+    BrowserElementPromptService.mapWindowToBrowserElementChild(content, this);
+
+    docShell.isBrowserFrame = true;
+    docShell.QueryInterface(Ci.nsIWebProgress)
+            .addProgressListener(this._progressListener,
+                                 Ci.nsIWebProgress.NOTIFY_LOCATION |
+                                 Ci.nsIWebProgress.NOTIFY_SECURITY |
+                                 Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
+
+    // This is necessary to get security web progress notifications.
+    var securityUI = Cc['@mozilla.org/secure_browser_ui;1']
+                       .createInstance(Ci.nsISecureBrowserUI);
+    securityUI.init(content);
+
+    // A mozbrowser iframe contained inside a mozapp iframe should return false
+    // for nsWindowUtils::IsPartOfApp (unless the mozbrowser iframe is itself
+    // also mozapp).  That is, mozapp is transitive down to its children, but
+    // mozbrowser serves as a barrier.
+    //
+    // This is because mozapp iframes have some privileges which we don't want
+    // to extend to untrusted mozbrowser content.
+    //
+    // Get the app manifest from the parent, if our frame has one.
+    let appManifestURL = sendSyncMsg('get-mozapp-manifest-url')[0];
+    let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+
+    if (!!appManifestURL) {
+      windowUtils.setIsApp(true);
+      windowUtils.setApp(appManifestURL);
+    } else {
+      windowUtils.setIsApp(false);
+    }
+
+    addEventListener('DOMTitleChanged',
+                     this._titleChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+    addEventListener('DOMLinkAdded',
+                     this._iconChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+    var self = this;
+    function addMsgListener(msg, handler) {
+      addMessageListener('browser-element-api:' + msg, handler.bind(self));
+    }
+
+    addMsgListener("get-screenshot", this._recvGetScreenshot);
+    addMsgListener("set-visible", this._recvSetVisible);
+    addMsgListener("unblock-modal-prompt", this._recvStopWaiting);
+
+    let els = Cc["@mozilla.org/eventlistenerservice;1"]
+                .getService(Ci.nsIEventListenerService);
+
+    // We are using the system group for those events so if something in the
+    // content called .stopPropagation() this will still be called.
+    els.addSystemEventListener(global, 'keydown',
+                               this._keyEventHandler.bind(this),
+                               /* useCapture = */ true);
+    els.addSystemEventListener(global, 'keypress',
+                               this._keyEventHandler.bind(this),
+                               /* useCapture = */ true);
+    els.addSystemEventListener(global, 'keyup',
+                               this._keyEventHandler.bind(this),
+                               /* useCapture = */ true);
+    els.addSystemEventListener(global, 'DOMWindowClose',
+                               this._closeHandler.bind(this),
+                               /* useCapture = */ false);
+  },
+
+  _tryGetInnerWindowID: function(win) {
+    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
+    try {
+      return utils.currentInnerWindowID;
+    }
+    catch(e) {
+      return null;
+    }
+  },
+
+  /**
+   * Show a modal prompt.  Called by BrowserElementPromptService.
+   */
+  showModalPrompt: function(win, args) {
+    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
+
+    args.windowID = { outer: utils.outerWindowID,
+                      inner: this._tryGetInnerWindowID(win) };
+    sendAsyncMsg('showmodalprompt', args);
+
+    let returnValue = this._waitForResult(win);
+
+    if (args.promptType == 'prompt' ||
+        args.promptType == 'confirm') {
+      return returnValue;
+    }
+  },
+
+  /**
+   * Spin in a nested event loop until we receive a unblock-modal-prompt message for
+   * this window.
+   */
+  _waitForResult: function(win) {
+    debug("_waitForResult(" + win + ")");
+    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
+
+    let outerWindowID = utils.outerWindowID;
+    let innerWindowID = this._tryGetInnerWindowID(win);
+    if (innerWindowID === null) {
+      // I have no idea what waiting for a result means when there's no inner
+      // window, so let's just bail.
+      debug("_waitForResult: No inner window. Bailing.");
+      return;
+    }
+
+    this._windowIDDict[outerWindowID] = Cu.getWeakReference(win);
+
+    debug("Entering modal state (outerWindowID=" + outerWindowID + ", " +
+                                "innerWindowID=" + innerWindowID + ")");
+
+    // In theory, we're supposed to pass |modalStateWin| back to
+    // leaveModalStateWithWindow.  But in practice, the window is always null,
+    // because it's the window associated with this script context, which
+    // doesn't have a window.  But we'll play along anyway in case this
+    // changes.
+    var modalStateWin = utils.enterModalStateWithWindow();
+
+    // We'll decrement win.modalDepth when we receive a unblock-modal-prompt message
+    // for the window.
+    if (!win.modalDepth) {
+      win.modalDepth = 0;
+    }
+    win.modalDepth++;
+    let origModalDepth = win.modalDepth;
+
+    let thread = Services.tm.currentThread;
+    debug("Nested event loop - begin");
+    while (win.modalDepth == origModalDepth) {
+      // Bail out of the loop if the inner window changed; that means the
+      // window navigated.
+      if (this._tryGetInnerWindowID(win) !== innerWindowID) {
+        debug("_waitForResult: Inner window ID changed " +
+              "while in nested event loop.");
+        break;
+      }
+
+      thread.processNextEvent(/* mayWait = */ true);
+    }
+    debug("Nested event loop - finish");
+
+    // If we exited the loop because the inner window changed, then bail on the
+    // modal prompt.
+    if (innerWindowID !== this._tryGetInnerWindowID(win)) {
+      throw Components.Exception("Modal state aborted by navigation",
+                                 Cr.NS_ERROR_NOT_AVAILABLE);
+    }
+
+    let returnValue = win.modalReturnValue;
+    delete win.modalReturnValue;
+
+    utils.leaveModalStateWithWindow(modalStateWin);
+
+    debug("Leaving modal state (outerID=" + outerWindowID + ", " +
+                               "innerID=" + innerWindowID + ")");
+    return returnValue;
+  },
+
+  _recvStopWaiting: function(msg) {
+    let outerID = msg.json.windowID.outer;
+    let innerID = msg.json.windowID.inner;
+    let returnValue = msg.json.returnValue;
+    debug("recvStopWaiting(outer=" + outerID + ", inner=" + innerID +
+          ", returnValue=" + returnValue + ")");
+
+    if (!this._windowIDDict[outerID]) {
+      debug("recvStopWaiting: No record of outer window ID " + outerID);
+      return;
+    }
+
+    let win = this._windowIDDict[outerID].get();
+    delete this._windowIDDict[outerID];
+
+    if (!win) {
+      debug("recvStopWaiting, but window is gone\n");
+      return;
+    }
+
+    if (innerID !== this._tryGetInnerWindowID(win)) {
+      debug("recvStopWaiting, but inner ID has changed\n");
+      return;
+    }
+
+    debug("recvStopWaiting " + win);
+    win.modalReturnValue = returnValue;
+    win.modalDepth--;
+  },
+
+  _titleChangedHandler: function(e) {
+    debug("Got titlechanged: (" + e.target.title + ")");
+    var win = e.target.defaultView;
+
+    // Ignore titlechanges which don't come from the top-level
+    // <iframe mozbrowser> window.
+    if (win == content) {
+      sendAsyncMsg('titlechange', e.target.title);
+    }
+    else {
+      debug("Not top level!");
+    }
+  },
+
+  _iconChangedHandler: function(e) {
+    debug("Got iconchanged: (" + e.target.href + ")");
+    var hasIcon = e.target.rel.split(' ').some(function(x) {
+      return x.toLowerCase() === 'icon';
+    });
+
+    if (hasIcon) {
+      var win = e.target.ownerDocument.defaultView;
+      // Ignore iconchanges which don't come from the top-level
+      // <iframe mozbrowser> window.
+      if (win == content) {
+        sendAsyncMsg('iconchange', e.target.href);
+      }
+      else {
+        debug("Not top level!");
+      }
+    }
+  },
+
+  _closeHandler: function(e) {
+    let win = e.target;
+    if (win != content || e.defaultPrevented) {
+      return;
+    }
+
+    debug("Closing window " + win);
+    sendAsyncMsg('close');
+
+    // Inform the window implementation that we handled this close ourselves.
+    e.preventDefault();
+  },
+
+  _recvGetScreenshot: function(data) {
+    debug("Received getScreenshot message: (" + data.json.id + ")");
+    var canvas = content.document
+      .createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+    var ctx = canvas.getContext("2d");
+    canvas.mozOpaque = true;
+    canvas.height = content.innerHeight;
+    canvas.width = content.innerWidth;
+    ctx.drawWindow(content, 0, 0, content.innerWidth,
+                   content.innerHeight, "rgb(255,255,255)");
+    sendAsyncMsg('got-screenshot', {
+      id: data.json.id,
+      screenshot: canvas.toDataURL("image/png")
+    });
+  },
+
+  _recvSetVisible: function(data) {
+    debug("Received setVisible message: (" + data.json.visible + ")");
+    if (docShell.isActive !== data.json.visible) {
+      docShell.isActive = data.json.visible;
+    }
+  },
+
+  _keyEventHandler: function(e) {
+    if (whitelistedEvents.indexOf(e.keyCode) != -1 && !e.defaultPrevented) {
+      sendAsyncMsg('keyevent', {
+        type: e.type,
+        keyCode: e.keyCode,
+        charCode: e.charCode,
+      });
+    }
+  },
+
+  // The docShell keeps a weak reference to the progress listener, so we need
+  // to keep a strong ref to it ourselves.
+  _progressListener: {
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                           Ci.nsISupportsWeakReference]),
+    _seenLoadStart: false,
+
+    onLocationChange: function(webProgress, request, location, flags) {
+      // We get progress events from subshells here, which is kind of weird.
+      if (webProgress != docShell) {
+        return;
+      }
+
+      // Ignore locationchange events which occur before the first loadstart.
+      // These are usually about:blank loads we don't care about.
+      if (!this._seenLoadStart) {
+        return;
+      }
+
+      sendAsyncMsg('locationchange', location.spec);
+    },
+
+    onStateChange: function(webProgress, request, stateFlags, status) {
+      if (webProgress != docShell) {
+        return;
+      }
+
+      if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
+        this._seenLoadStart = true;
+        sendAsyncMsg('loadstart');
+      }
+
+      if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+        sendAsyncMsg('loadend');
+      }
+    },
+
+    onSecurityChange: function(webProgress, request, state) {
+      if (webProgress != docShell) {
+        return;
+      }
+
+      var stateDesc;
+      if (state & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
+        stateDesc = 'secure';
+      }
+      else if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
+        stateDesc = 'broken';
+      }
+      else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
+        stateDesc = 'insecure';
+      }
+      else {
+        debug("Unexpected securitychange state!");
+        stateDesc = '???';
+      }
+
+      // XXX Until bug 764496 is fixed, this will always return false.
+      var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
+
+      sendAsyncMsg('securitychange', {state: stateDesc, extendedValidation: isEV});
+    },
+
+    onStatusChange: function(webProgress, request, status, message) {},
+    onProgressChange: function(webProgress, request, curSelfProgress,
+                               maxSelfProgress, curTotalProgress, maxTotalProgress) {},
+  },
+};
+
+var api = new BrowserElementChild();
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -135,16 +135,17 @@ function BrowserElementParent(frameLoade
   // where |data| is the message manager's data object.
 
   let self = this;
   function addMessageListener(msg, handler) {
     self._mm.addMessageListener('browser-element-api:' + msg, handler.bind(self));
   }
 
   addMessageListener("hello", this._recvHello);
+  addMessageListener("contextmenu", this._fireCtxMenuEvent);
   addMessageListener("locationchange", this._fireEventFromMsg);
   addMessageListener("loadstart", this._fireEventFromMsg);
   addMessageListener("loadend", this._fireEventFromMsg);
   addMessageListener("titlechange", this._fireEventFromMsg);
   addMessageListener("iconchange", this._fireEventFromMsg);
   addMessageListener("close", this._fireEventFromMsg);
   addMessageListener("securitychange", this._fireEventFromMsg);
   addMessageListener("get-mozapp-manifest-url", this._sendMozAppManifestURL);
@@ -182,16 +183,35 @@ BrowserElementParent.prototype = {
                       .messageManager
                       .sendAsyncMessage('browser-element-api:' + msg, data);
   },
 
   _recvHello: function(data) {
     debug("recvHello");
   },
 
+  _fireCtxMenuEvent: function(data) {
+    let evtName = data.name.substring('browser-element-api:'.length);
+    let detail = data.json;
+
+    debug('fireCtxMenuEventFromMsg: ' + evtName + ' ' + detail);
+    let evt = this._createEvent(evtName, detail);
+
+    if (detail.contextmenu) {
+      var self = this;
+      XPCNativeWrapper.unwrap(evt.detail).contextMenuItemSelected = function(id) {
+        self._sendAsyncMsg('fire-ctx-callback', {menuitem: id});
+      };
+    }
+    // The embedder may have default actions on context menu events, so
+    // we fire a context menu event even if the child didn't define a
+    // custom context menu
+    this._frameElement.dispatchEvent(evt);
+  },
+
   /**
    * Fire either a vanilla or a custom event, depending on the contents of
    * |data|.
    */
   _fireEventFromMsg: function(data) {
     let name = data.name.substring('browser-element-api:'.length);
     let detail = data.json;
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementParent.js.orig
@@ -0,0 +1,298 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let Cu = Components.utils;
+let Ci = Components.interfaces;
+let Cc = Components.classes;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
+
+function debug(msg) {
+  //dump("BrowserElementParent - " + msg + "\n");
+}
+
+/**
+ * BrowserElementParent implements one half of <iframe mozbrowser>.  (The other
+ * half is, unsurprisingly, BrowserElementChild.)
+ *
+ * BrowserElementParentFactory detects when we create a windows or docshell
+ * contained inside a <iframe mozbrowser> and creates a BrowserElementParent
+ * object for that window.
+ *
+ * BrowserElementParent injects script to listen for certain events in the
+ * child.  We then listen to messages from the child script and take
+ * appropriate action here in the parent.
+ */
+
+function BrowserElementParentFactory() {
+  this._initialized = false;
+}
+
+BrowserElementParentFactory.prototype = {
+  classID: Components.ID("{ddeafdac-cb39-47c4-9cb8-c9027ee36d26}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
+
+  /**
+   * Called on app startup, and also when the browser frames enabled pref is
+   * changed.
+   */
+  _init: function() {
+    if (this._initialized) {
+      return;
+    }
+
+    // If the pref is disabled, do nothing except wait for the pref to change.
+    // (This is important for tests, if nothing else.)
+    if (!this._browserFramesPrefEnabled()) {
+      var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+      prefs.addObserver(BROWSER_FRAMES_ENABLED_PREF, this, /* ownsWeak = */ true);
+      return;
+    }
+
+    debug("_init");
+    this._initialized = true;
+
+    // Maps frame elements to BrowserElementParent objects.  We never look up
+    // anything in this map; the purpose is to keep the BrowserElementParent
+    // alive for as long as its frame element lives.
+    this._bepMap = new WeakMap();
+
+    var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+    os.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
+    os.addObserver(this, 'in-process-browser-frame-shown', /* ownsWeak = */ true);
+  },
+
+  _browserFramesPrefEnabled: function() {
+    var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+    try {
+      return prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
+    }
+    catch(e) {
+      return false;
+    }
+  },
+
+  _observeInProcessBrowserFrameShown: function(frameLoader) {
+    debug("In-process browser frame shown " + frameLoader);
+    this._createBrowserElementParent(frameLoader);
+  },
+
+  _observeRemoteBrowserFrameShown: function(frameLoader) {
+    debug("Remote browser frame shown " + frameLoader);
+    this._createBrowserElementParent(frameLoader);
+  },
+
+  _createBrowserElementParent: function(frameLoader) {
+    let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
+    this._bepMap.set(frameElement, new BrowserElementParent(frameLoader));
+  },
+
+  observe: function(subject, topic, data) {
+    switch(topic) {
+    case 'app-startup':
+      this._init();
+      break;
+    case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+      if (data == BROWSER_FRAMES_ENABLED_PREF) {
+        this._init();
+      }
+      break;
+    case 'remote-browser-frame-shown':
+      this._observeRemoteBrowserFrameShown(subject);
+      break;
+    case 'in-process-browser-frame-shown':
+      this._observeInProcessBrowserFrameShown(subject);
+      break;
+    case 'content-document-global-created':
+      this._observeContentGlobalCreated(subject);
+      break;
+    }
+  },
+};
+
+function BrowserElementParent(frameLoader) {
+  debug("Creating new BrowserElementParent object for " + frameLoader);
+  this._screenshotListeners = {};
+  this._screenshotReqCounter = 0;
+
+  this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
+  if (!this._frameElement) {
+    debug("No frame element?");
+    return;
+  }
+
+  this._mm = frameLoader.messageManager;
+
+  // Messages we receive are handed to functions which take a (data) argument,
+  // where |data| is the message manager's data object.
+
+  let self = this;
+  function addMessageListener(msg, handler) {
+    self._mm.addMessageListener('browser-element-api:' + msg, handler.bind(self));
+  }
+
+  addMessageListener("hello", this._recvHello);
+  addMessageListener("locationchange", this._fireEventFromMsg);
+  addMessageListener("loadstart", this._fireEventFromMsg);
+  addMessageListener("loadend", this._fireEventFromMsg);
+  addMessageListener("titlechange", this._fireEventFromMsg);
+  addMessageListener("iconchange", this._fireEventFromMsg);
+  addMessageListener("close", this._fireEventFromMsg);
+  addMessageListener("securitychange", this._fireEventFromMsg);
+  addMessageListener("get-mozapp-manifest-url", this._sendMozAppManifestURL);
+  addMessageListener("keyevent", this._fireKeyEvent);
+  addMessageListener("showmodalprompt", this._handleShowModalPrompt);
+  addMessageListener('got-screenshot', this._recvGotScreenshot);
+
+  function defineMethod(name, fn) {
+    XPCNativeWrapper.unwrap(self._frameElement)[name] = fn.bind(self);
+  }
+
+  // Define methods on the frame element.
+  defineMethod('getScreenshot', this._getScreenshot);
+  defineMethod('setVisible', this._setVisible);
+
+  this._mm.loadFrameScript("chrome://global/content/BrowserElementChild.js",
+                           /* allowDelayedLoad = */ true);
+}
+
+BrowserElementParent.prototype = {
+  get _window() {
+    return this._frameElement.ownerDocument.defaultView;
+  },
+
+  _sendAsyncMsg: function(msg, data) {
+    this._frameElement.QueryInterface(Ci.nsIFrameLoaderOwner)
+                      .frameLoader
+                      .messageManager
+                      .sendAsyncMessage('browser-element-api:' + msg, data);
+  },
+
+  _recvHello: function(data) {
+    debug("recvHello");
+  },
+
+  /**
+   * Fire either a vanilla or a custom event, depending on the contents of
+   * |data|.
+   */
+  _fireEventFromMsg: function(data) {
+    let name = data.name.substring('browser-element-api:'.length);
+    let detail = data.json;
+
+    debug('fireEventFromMsg: ' + name + ', ' + detail);
+    let evt = this._createEvent(name, detail,
+                                /* cancelable = */ false);
+    this._frameElement.dispatchEvent(evt);
+  },
+
+  _handleShowModalPrompt: function(data) {
+    // Fire a showmodalprmopt event on the iframe.  When this method is called,
+    // the child is spinning in a nested event loop waiting for an
+    // unblock-modal-prompt message.
+    //
+    // If the embedder calls preventDefault() on the showmodalprompt event,
+    // we'll block the child until event.detail.unblock() is called.
+    //
+    // Otherwise, if preventDefault() is not called, we'll send the
+    // unblock-modal-prompt message to the child as soon as the event is done
+    // dispatching.
+
+    let detail = data.json;
+    debug('handleShowPrompt ' + JSON.stringify(detail));
+
+    // Strip off the windowID property from the object we send along in the
+    // event.
+    let windowID = detail.windowID;
+    delete detail.windowID;
+    debug("Event will have detail: " + JSON.stringify(detail));
+    let evt = this._createEvent('showmodalprompt', detail,
+                                /* cancelable = */ true);
+
+    let self = this;
+    let unblockMsgSent = false;
+    function sendUnblockMsg() {
+      if (unblockMsgSent) {
+        return;
+      }
+      unblockMsgSent = true;
+
+      // We don't need to sanitize evt.detail.returnValue (e.g. converting the
+      // return value of confirm() to a boolean); Gecko does that for us.
+
+      let data = { windowID: windowID,
+                   returnValue: evt.detail.returnValue };
+      self._sendAsyncMsg('unblock-modal-prompt', data);
+    }
+
+    XPCNativeWrapper.unwrap(evt.detail).unblock = function() {
+      sendUnblockMsg();
+    };
+
+    this._frameElement.dispatchEvent(evt);
+
+    if (!evt.defaultPrevented) {
+      // Unblock the inner frame immediately.  Otherwise we'll unblock upon
+      // evt.detail.unblock().
+      sendUnblockMsg();
+    }
+  },
+
+  _createEvent: function(evtName, detail, cancelable) {
+    // This will have to change if we ever want to send a CustomEvent with null
+    // detail.  For now, it's OK.
+    if (detail !== undefined && detail !== null) {
+      return new this._window.CustomEvent('mozbrowser' + evtName,
+                                          { bubbles: true,
+                                            cancelable: cancelable,
+                                            detail: detail });
+    }
+
+    return new this._window.Event('mozbrowser' + evtName,
+                                  { bubbles: true,
+                                    cancelable: cancelable });
+  },
+
+  _sendMozAppManifestURL: function(data) {
+    return this._frameElement.getAttribute('mozapp');
+  },
+
+
+  _getScreenshot: function() {
+    let id = 'req_' + this._screenshotReqCounter++;
+    let req = Services.DOMRequest.createRequest(this._window);
+    this._screenshotListeners[id] = req;
+    this._sendAsyncMsg('get-screenshot', {id: id});
+    return req;
+  },
+
+  _recvGotScreenshot: function(data) {
+    var req = this._screenshotListeners[data.json.id];
+    delete this._screenshotListeners[data.json.id];
+    Services.DOMRequest.fireSuccess(req, data.json.screenshot);
+  },
+
+  _setVisible: function(visible) {
+    this._sendAsyncMsg('set-visible', {visible: visible});
+  },
+
+  _fireKeyEvent: function(data) {
+    let evt = this._window.document.createEvent("KeyboardEvent");
+    evt.initKeyEvent(data.json.type, true, true, this._window,
+                     false, false, false, false, // modifiers
+                     data.json.keyCode,
+                     data.json.charCode);
+
+    this._frameElement.dispatchEvent(evt);
+  },
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);
--- a/dom/browser-element/mochitest/Makefile.in
+++ b/dom/browser-element/mochitest/Makefile.in
@@ -61,16 +61,18 @@ include $(topsrcdir)/config/rules.mk
 		file_browserElement_OpenWindowInFrame.html \
 		browserElement_OpenWindowRejected.js \
 		test_browserElement_inproc_OpenWindowRejected.html \
 		file_browserElement_OpenWindowRejected.html \
 		browserElement_SecurityChange.js \
 		test_browserElement_inproc_SecurityChange.html \
 		file_browserElement_SecurityChange.html \
 		browserElement_BackForward.js \
+                browserElement_ContextmenuEvents.js \
+                test_browserElement_inproc_ContextmenuEvents.html \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081).
 #
 # Note that there's no inproc equivalent of BackForward; that's intentional.
 ifneq ($(OS_ARCH),WINNT)
 _TEST_FILES += \
 		test_browserElement_oop_LoadEvents.html \
@@ -86,14 +88,15 @@ ifneq ($(OS_ARCH),WINNT)
 		test_browserElement_oop_PromptCheck.html \
 		test_browserElement_oop_PromptConfirm.html \
 		test_browserElement_oop_Close.html \
 		test_browserElement_oop_OpenWindow.html \
 		test_browserElement_oop_OpenWindowInFrame.html \
 		test_browserElement_oop_OpenWindowRejected.html \
 		test_browserElement_oop_SecurityChange.html \
 		test_browserElement_oop_BackForward.html \
+                test_browserElement_oop_ContextmenuEvents.html \
 		$(NULL)
 endif
 
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_ContextmenuEvents.js
@@ -0,0 +1,137 @@
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+var iframeScript = function() {
+
+  content.fireContextMenu = function(element) {
+    var ev = content.document.createEvent('HTMLEvents');
+    ev.initEvent('contextmenu', true, false);
+    element.dispatchEvent(ev);
+  }
+
+  XPCNativeWrapper.unwrap(content).ctxCallbackFired = function(data) {
+    sendAsyncMessage('test:callbackfired', {data: data});
+  }
+
+  XPCNativeWrapper.unwrap(content).onerror = function(e) {
+    sendAsyncMessage('test:errorTriggered', {data: e});
+  }
+
+  content.fireContextMenu(content.document.body);
+  content.fireContextMenu(content.document.getElementById('menu1-trigger'));
+  content.fireContextMenu(content.document.getElementById('inner-link'));
+  content.fireContextMenu(content.document.getElementById('menu2-trigger'));
+}
+
+var trigger1 = function() {
+  content.fireContextMenu(content.document.getElementById('menu1-trigger'));
+};
+
+function runTest() {
+
+  browserElementTestHelpers.setEnabledPref(true);
+  browserElementTestHelpers.addToWhitelist();
+
+  var iframe1 = document.createElement('iframe');
+  iframe1.mozbrowser = true;
+  document.body.appendChild(iframe1);
+  iframe1.src = 'data:text/html,<html>' +
+    '<body>' +
+    '<menu type="context" id="menu1" label="firstmenu">' +
+      '<menuitem label="foo" onclick="window.ctxCallbackFired(\'foo\')"></menuitem>' +
+      '<menuitem label="bar" onclick="throw(\'anerror\')"></menuitem>' +
+    '</menu>' +
+    '<menu type="context" id="menu2" label="secondmenu">' +
+      '<menuitem label="outer" onclick="window.ctxCallbackFired(\'err\')"></menuitem>' +
+      '<menu>' +
+        '<menuitem label="inner 1"></menuitem>' +
+        '<menuitem label="inner 2" onclick="window.ctxCallbackFired(\'inner2\')"></menuitem>' +
+      '</menu>' +
+    '</menu>' +
+    '<div id="menu1-trigger" contextmenu="menu1"><a id="inner-link" href="foo.html">Menu 1</a></div>' +
+    '<a href="bar.html" contextmenu="menu2"><img id="menu2-trigger" src="example.png" /></a>' +
+    '</body></html>';
+
+  var mm;
+  var numIframeLoaded = 0;
+  var ctxMenuEvents = 0;
+  var ctxCallbackEvents = 0;
+
+  var cachedCtxDetail = null;
+
+  // We fire off various contextmenu events to check the data that gets
+  // passed to the handler
+  function iframeContextmenuHandler(e) {
+    var detail = e.detail;
+    ctxMenuEvents++;
+    if (ctxMenuEvents === 1) {
+      ok(detail.contextmenu === null, 'body context clicks have no context menu');
+    } else if (ctxMenuEvents === 2) {
+      cachedCtxDetail = detail;
+      ok(detail.contextmenu.items.length === 2, 'trigger custom contextmenu');
+    } else if (ctxMenuEvents === 3) {
+      ok(detail.systemTargets.length === 1, 'Includes anchor data');
+      ok(detail.contextmenu.items.length === 2, 'Inner clicks trigger correct menu');
+    } else if (ctxMenuEvents === 4) {
+      var innerMenu = detail.contextmenu.items.filter(function(x) {
+        return x.type === 'menu';
+      });
+      ok(detail.systemTargets.length === 2, 'Includes anchor and img data');
+      ok(innerMenu.length > 0, 'Menu contains a nested menu');
+      ok(true, 'Got correct number of contextmenu events');
+      // Finished testing the data passed to the contextmenu handler,
+      // now we start selecting contextmenu items
+
+      // This is previously triggered contextmenu data, since we have
+      // fired subsequent contextmenus this should not be mistaken
+      // for a current menuitem
+      var prevId = cachedCtxDetail.contextmenu.items[0].id;
+      cachedCtxDetail.contextMenuItemSelected(prevId);
+      // This triggers a current menuitem
+      detail.contextMenuItemSelected(innerMenu[0].items[1].id);
+      // Once an item it selected, subsequent selections are ignored
+      detail.contextMenuItemSelected(innerMenu[0].items[0].id);
+    } else if (ctxMenuEvents === 5) {
+      ok(detail.contextmenu.label === 'firstmenu', 'Correct menu enabled');
+      detail.contextMenuItemSelected(detail.contextmenu.items[0].id);
+    } else if (ctxMenuEvents === 6) {
+      detail.contextMenuItemSelected(detail.contextmenu.items[1].id);
+    } else if (ctxMenuEvents > 6) {
+      ok(false, 'Too many events');
+    }
+  }
+
+  function ctxCallbackRecieved(msg) {
+    ctxCallbackEvents++;
+    if (ctxCallbackEvents === 1) {
+      ok(msg.json.data === 'inner2', 'Callback function got fired correctly');
+      mm.loadFrameScript('data:,(' + trigger1.toString() + ')();', false);
+    } else if (ctxCallbackEvents === 2) {
+      ok(msg.json.data === 'foo', 'Callback function got fired correctly');
+      mm.loadFrameScript('data:,(' + trigger1.toString() + ')();', false);
+    } else if (ctxCallbackEvents > 2) {
+      ok(false, 'Too many callback events');
+    }
+  }
+
+  function errorTriggered(msg) {
+    ok(true, 'An error in the callback triggers window.onerror');
+    SimpleTest.finish();
+  }
+
+  function iframeLoadedHandler() {
+    numIframeLoaded++;
+    if (numIframeLoaded === 2) {
+      mm = SpecialPowers.getBrowserFrameMessageManager(iframe1);
+      mm.addMessageListener('test:callbackfired', ctxCallbackRecieved);
+      mm.addMessageListener('test:errorTriggered', errorTriggered);
+      mm.loadFrameScript('data:,(' + iframeScript.toString() + ')();', false);
+    }
+  }
+
+  iframe1.addEventListener('mozbrowsercontextmenu', iframeContextmenuHandler);
+  iframe1.addEventListener('mozbrowserloadend', iframeLoadedHandler);
+}
+
+addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_ContextmenuEvents.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Contextmenu Events</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_ContextmenuEvents.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_ContextmenuEvents.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Contextmenu Events</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_ContextmenuEvents.js">
+</script>
+</body>
+</html>
--- a/dom/imptests/failures/editing/conformancetest/test_event.html.json
+++ b/dom/imptests/failures/editing/conformancetest/test_event.html.json
@@ -286,26 +286,10 @@
   "Command justifyRight, value \"quasit\": input event, canceled":true,
   "Command justifyRight, value \"quasit\": beforeinput event, uncanceled":true,
   "Command justifyRight, value \"quasit\": input event, uncanceled":true,
   "Command outdent, value \"\": beforeinput event, canceled":true,
   "Command outdent, value \"\": beforeinput event, uncanceled":true,
   "Command outdent, value \"\": input event, uncanceled":true,
   "Command outdent, value \"quasit\": beforeinput event, canceled":true,
   "Command outdent, value \"quasit\": beforeinput event, uncanceled":true,
-  "Command outdent, value \"quasit\": input event, uncanceled":true,
-  "Command redo, value \"\": beforeinput event, canceled":true,
-  "Command redo, value \"\": input event, canceled":true,
-  "Command redo, value \"\": beforeinput event, uncanceled":true,
-  "Command redo, value \"\": input event, uncanceled":true,
-  "Command redo, value \"quasit\": beforeinput event, canceled":true,
-  "Command redo, value \"quasit\": input event, canceled":true,
-  "Command redo, value \"quasit\": beforeinput event, uncanceled":true,
-  "Command redo, value \"quasit\": input event, uncanceled":true,
-  "Command undo, value \"\": beforeinput event, canceled":true,
-  "Command undo, value \"\": input event, canceled":true,
-  "Command undo, value \"\": beforeinput event, uncanceled":true,
-  "Command undo, value \"\": input event, uncanceled":true,
-  "Command undo, value \"quasit\": beforeinput event, canceled":true,
-  "Command undo, value \"quasit\": input event, canceled":true,
-  "Command undo, value \"quasit\": beforeinput event, uncanceled":true,
-  "Command undo, value \"quasit\": input event, uncanceled":true
+  "Command outdent, value \"quasit\": input event, uncanceled":true
 }
--- a/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
+++ b/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
@@ -104,31 +104,24 @@
   "[[\"stylewithcss\",\"false\"],[\"backcolor\",\"#00FFFF\"]] \"<span style=background-color:tan>fo[o<span style=background-color:transparent>b]ar</span></span>\" queryCommandValue(\"backcolor\") after":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span>[foo</span> <span>bar]</span>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span>[foo</span> <span>bar]</span>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span>[foo</span> <span>bar]</span>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<p>[foo</p><p> <span>bar</span> </p><p>baz]</p>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<p>[foo</p><p> <span>bar</span> </p><p>baz]</p>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<p>[foo</p><p> <span>bar</span> </p><p>baz]</p>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"bold\",\"\"]] \"<span>foo[</span><span>]bar</span>\" queryCommandState(\"bold\") after":true,
-  "[[\"bold\",\"\"]] \"foo<span contenteditable=false>[bar]</span>baz\": execCommand(\"bold\", false, \"\") return value":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"fo[o<span contenteditable=false>bar</span>b]az\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"fo[o<span contenteditable=false>bar</span>b]az\" compare innerHTML":true,
-  "[[\"bold\",\"\"]] \"foo<span contenteditable=false>ba[r</span>b]az\": execCommand(\"bold\", false, \"\") return value":true,
-  "[[\"bold\",\"\"]] \"fo[o<span contenteditable=false>b]ar</span>baz\": execCommand(\"bold\", false, \"\") return value":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" queryCommandState(\"bold\") after":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>[bar]</span>baz</span>\" queryCommandState(\"bold\") after":true,
-  "[[\"bold\",\"\"]] \"<span contenteditable=false>fo[o<span contenteditable=true>bar</span>b]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
-  "[[\"bold\",\"\"]] \"<span contenteditable=false>foo<span contenteditable=true>ba[r</span>b]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
-  "[[\"bold\",\"\"]] \"<span contenteditable=false>fo[o<span contenteditable=true>b]ar</span>baz</span>\": execCommand(\"bold\", false, \"\") return value":true,
-  "[[\"bold\",\"\"]] \"<span contenteditable=false>fo[<b>o<span contenteditable=true>bar</span>b</b>]az</span>\": execCommand(\"bold\", false, \"\") return value":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<table><tbody data-start=0 data-end=1><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<table><tbody data-start=0 data-end=1><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<table data-start=0 data-end=1><tbody><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"<table data-start=0 data-end=1><tbody><tr><td>foo<td>bar<td>baz</table>\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"{<table><tr><td>foo<td>bar<td>baz</table>}\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"bold\",\"\"]] \"{<table><tr><td>foo<td>bar<td>baz</table>}\" queryCommandIndeterm(\"bold\") before":true,
   "[[\"bold\",\"\"]] \"<b>foo</b>[bar]<strong>baz</strong>\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"bold\",\"\"]] \"<strong>foo</strong>[bar]<b>baz</b>\" compare innerHTML":true,
@@ -1334,20 +1327,18 @@
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<listing>b[ar</listing>baz]\" queryCommandValue(\"fontname\") after":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>b[ar</tt>baz]\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>b[ar</tt>baz]\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">[bar]</span>baz\" queryCommandValue(\"fontname\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">[bar]</span>baz\" queryCommandValue(\"fontname\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" queryCommandValue(\"fontname\") before":true,
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<span style=\\\"font-family: monospace\\\">b[a]r</span>baz\" queryCommandValue(\"fontname\") before":true,
-  "[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\": execCommand(\"fontname\", false, \"sans-serif\") return value":true,
   "[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\" queryCommandValue(\"fontname\") before":true,
   "[[\"fontname\",\"sans-serif\"]] \"foo<tt contenteditable=false>ba[r</tt>b]az\" queryCommandValue(\"fontname\") after":true,
-  "[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\": execCommand(\"fontname\", false, \"sans-serif\") return value":true,
   "[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\" queryCommandValue(\"fontname\") before":true,
   "[[\"fontname\",\"sans-serif\"]] \"fo[o<tt contenteditable=false>b]ar</tt>baz\" queryCommandValue(\"fontname\") after":true,
   "[[\"fontname\",\"sans-serif\"]] \"foo<tt>{}<br></tt>bar\" queryCommandValue(\"fontname\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>{<br></tt>}bar\" compare innerHTML":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>{<br></tt>}bar\" queryCommandValue(\"fontname\") before":true,
   "[[\"stylewithcss\",\"true\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>{<br></tt>}bar\" queryCommandValue(\"fontname\") after":true,
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>{<br></tt>}bar\" compare innerHTML":true,
   "[[\"stylewithcss\",\"false\"],[\"fontname\",\"sans-serif\"]] \"foo<tt>{<br></tt>}bar\" queryCommandValue(\"fontname\") before":true,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1709,17 +1709,17 @@ IDBObjectStore::CreateIndexInternal(cons
 }
 
 nsresult
 IDBObjectStore::IndexInternal(const nsAString& aName,
                               IDBIndex** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!mTransaction->IsOpen()) {
+  if (mTransaction->IsFinished()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   IndexInfo* indexInfo = nsnull;
   PRUint32 indexCount = mInfo->indexes.Length();
   for (PRUint32 index = 0; index < indexCount; index++) {
     if (mInfo->indexes[index].name == aName) {
       indexInfo = &(mInfo->indexes[index]);
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -494,20 +494,17 @@ IDBTransaction::ClearCreatedFileInfos()
   mCreatedFileInfos.Clear();
 }
 
 nsresult
 IDBTransaction::AbortWithCode(nsresult aAbortCode)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  // We can't use IsOpen here since we need it to be possible to call Abort()
-  // even from outside of transaction callbacks.
-  if (mReadyState != IDBTransaction::INITIAL &&
-      mReadyState != IDBTransaction::LOADING) {
+  if (IsFinished()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->SendAbort(aAbortCode);
   }
 
@@ -678,17 +675,17 @@ IDBTransaction::ObjectStore(const nsAStr
 }
 
 nsresult
 IDBTransaction::ObjectStoreInternal(const nsAString& aName,
                                     IDBObjectStore** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!IsOpen()) {
+  if (IsFinished()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   ObjectStoreInfo* info = nsnull;
 
   if (mMode == IDBTransaction::VERSION_CHANGE ||
       mObjectStoreNames.Contains(aName)) {
     info = mDatabaseInfo->GetObjectStore(aName);
@@ -814,28 +811,31 @@ CommitHelper::Run()
       }
     }
     else {
       event = CreateGenericEvent(NS_LITERAL_STRING(COMPLETE_EVT_STR),
                                  eDoesNotBubble, eNotCancelable);
     }
     NS_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
+    if (mListener) {
+      mListener->NotifyTransactionPreComplete(mTransaction);
+    }
+
     bool dummy;
     if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) {
       NS_WARNING("Dispatch failed!");
     }
 
 #ifdef DEBUG
     mTransaction->mFiredCompleteOrAbort = true;
 #endif
 
-    // Tell the listener (if we have one) that we're done
     if (mListener) {
-      mListener->NotifyTransactionComplete(mTransaction);
+      mListener->NotifyTransactionPostComplete(mTransaction);
     }
 
     mTransaction = nsnull;
 
     return NS_OK;
   }
 
   IDBDatabase* database = mTransaction->Database();
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -40,17 +40,20 @@ class TransactionThreadPool;
 class UpdateRefcountFunction;
 
 class IDBTransactionListener
 {
 public:
   NS_IMETHOD_(nsrefcnt) AddRef() = 0;
   NS_IMETHOD_(nsrefcnt) Release() = 0;
 
-  virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction) = 0;
+  // Called just before dispatching the final events on the transaction.
+  virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) = 0;
+  // Called just after dispatching the final events on the transaction.
+  virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) = 0;
 };
 
 class IDBTransaction : public IDBWrapperCache,
                        public nsIIDBTransaction,
                        public nsIRunnable
 {
   friend class AsyncConnectionHelper;
   friend class CommitHelper;
@@ -117,16 +120,21 @@ public:
   already_AddRefed<mozIStorageStatement>
   GetCachedStatement(const char (&aQuery)[N])
   {
     return GetCachedStatement(NS_LITERAL_CSTRING(aQuery));
   }
 
   bool IsOpen() const;
 
+  bool IsFinished() const
+  {
+    return mReadyState > LOADING;
+  }
+
   bool IsWriteAllowed() const
   {
     return mMode == READ_WRITE || mMode == VERSION_CHANGE;
   }
 
   bool IsAborted() const
   {
     return NS_FAILED(mAbortCode);
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1347,18 +1347,20 @@ protected:
   // SetVersionHelper never fires an error event at the request.  It hands that
   // responsibility back to the OpenDatabaseHelper
   virtual void OnError() MOZ_OVERRIDE
   { }
 
   // Need an upgradeneeded event here.
   virtual already_AddRefed<nsDOMEvent> CreateSuccessEvent() MOZ_OVERRIDE;
 
-  virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction)
-                                             MOZ_OVERRIDE;
+  virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction)
+                                                MOZ_OVERRIDE;
+  virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction)
+                                                 MOZ_OVERRIDE;
 
   virtual ChildProcessSendResult
   MaybeSendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
   {
     return Success_NotSent;
   }
 
   virtual nsresult UnpackResponseFromParentProcess(
@@ -1951,22 +1953,16 @@ OpenDatabaseHelper::Run()
     // We've done whatever work we need to do on the DB thread, and any
     // SetVersion/DeleteDatabase stuff is done by now.
     NS_ASSERTION(mState == eFiringEvents ||
                  mState == eSetVersionCompleted ||
                  mState == eDeleteCompleted, "Why are we here?");
 
     switch (mState) {
       case eSetVersionCompleted: {
-        // Allow transaction creation/other version change transactions to proceed
-        // before we fire events.  Other version changes will be postd to the end
-        // of the event loop, and will be behind whatever the page does in
-        // its error/success event handlers.
-        mDatabase->ExitSetVersionTransaction();
-
         mState = eFiringEvents;
         break;
       }
 
       case eDeleteCompleted: {
         // Destroy the database now (we should have the only ref).
         mDatabase = nsnull;
 
@@ -2129,16 +2125,19 @@ OpenDatabaseHelper::GetSuccessResult(JSC
 }
 
 nsresult
 OpenDatabaseHelper::NotifySetVersionFinished()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
   NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
 
+  // Allow transaction creation to proceed.
+  mDatabase->ExitSetVersionTransaction();
+
   mState = eSetVersionCompleted;
 
   // Dispatch ourself back to the main thread
   return NS_DispatchToCurrentThread(this);
 }
 
 nsresult
 OpenDatabaseHelper::NotifyDeleteFinished()
@@ -2287,17 +2286,27 @@ SetVersionHelper::CreateSuccessEvent()
 {
   NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?");
 
   return IDBVersionChangeEvent::CreateUpgradeNeeded(mCurrentVersion,
                                                     mRequestedVersion);
 }
 
 nsresult
-SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction)
+SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aTransaction, "This is unexpected.");
+  NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
+
+  return mOpenHelper->NotifySetVersionFinished();
+}
+
+nsresult
+SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aTransaction, "This is unexpected.");
   NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
 
   // If we hit an error, the OpenDatabaseHelper needs to get that error too.
   nsresult rv = GetResultCode();
   if (NS_FAILED(rv)) {
@@ -2307,17 +2316,16 @@ SetVersionHelper::NotifyTransactionCompl
   // If the transaction was aborted, we should throw an error message.
   if (aTransaction->IsAborted()) {
     mOpenHelper->SetError(aTransaction->GetAbortCode());
   }
 
   mOpenRequest->SetTransaction(nsnull);
   mOpenRequest = nsnull;
 
-  rv = mOpenHelper->NotifySetVersionFinished();
   mOpenHelper = nsnull;
 
   return rv;
 }
 
 nsresult
 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -332,17 +332,16 @@ IndexedDBDatabaseChild::RecvSuccess(
   }
 
   MOZ_ASSERT(mStrongDatabase);
   nsRefPtr<IDBDatabase> database;
   mStrongDatabase.swap(database);
 
   if (openHelper) {
     request->Reset();
-    database->ExitSetVersionTransaction();
   }
   else {
     openHelper = new IPCOpenDatabaseHelper(mDatabase, request);
   }
 
   request->SetTransaction(NULL);
 
   MainThreadEventTarget target;
@@ -365,17 +364,16 @@ IndexedDBDatabaseChild::RecvError(const 
   nsRefPtr<IDBDatabase> database;
   mStrongDatabase.swap(database);
 
   nsRefPtr<AsyncConnectionHelper> openHelper;
   mOpenHelper.swap(openHelper);
 
   if (openHelper) {
     request->Reset();
-    database->ExitSetVersionTransaction();
   }
   else {
     openHelper = new IPCOpenDatabaseHelper(NULL, request);
   }
 
   openHelper->SetError(aRv);
   request->SetTransaction(NULL);
 
@@ -535,22 +533,19 @@ void
 IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv)
 {
   MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(mStrongTransaction);
 
   nsRefPtr<IDBTransaction> transaction;
   mStrongTransaction.swap(transaction);
 
-  // This is where we should allow the database to start issuing new
-  // transactions once we fix the main thread. E.g.:
-  //
-  //   if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
-  //     transaction->Database()->ExitSetVersionTransaction();
-  //   }
+  if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) {
+    transaction->Database()->ExitSetVersionTransaction();
+  }
 
   nsRefPtr<CommitHelper> helper = new CommitHelper(transaction, aRv);
 
   MainThreadEventTarget target;
   if (NS_FAILED(target.Dispatch(helper, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Dispatch of CommitHelper failed!");
   }
 }
--- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html
+++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html
@@ -66,16 +66,17 @@
       trans.oncomplete = unexpectedSuccessHandler;
       trans.onabort = grabEventAndContinueHandler;
 
       let objectStore = db.createObjectStore("foo");
 
       is(db.objectStoreNames.length, 1, "Correct objectStoreNames length");
       ok(db.objectStoreNames.contains("foo"), "Has correct objectStore");
 
+      let originalRequest = request;
       request = objectStore.add({}, 1);
       request.onsuccess = grabEventAndContinueHandler;
       request.onerror = errorHandler;
       event = yield;
 
       request = objectStore.add({}, 1);
       request.onsuccess = unexpectedSuccessHandler;
       request.onerror = function(event) {
@@ -83,20 +84,23 @@
       }
       event = yield;
 
       is(event.type, "abort", "Got a transaction abort event");
       is(db.version, 1, "Correct version");
       is(db.objectStoreNames.length, 1, "Correct objectStoreNames length");
       is(trans.error.name, "ConstraintError", "Right error");
       ok(trans.error === request.error, "Object identity holds");
+      is(originalRequest.transaction, trans, "request.transaction should still be set");
 
       event = yield;
       is(event.type, "error", "Got request error event");
+      is(event.target, originalRequest, "error event has right target");
       is(event.target.error.name, "ConstraintError", "Right error");
+      is(originalRequest.transaction, null, "request.transaction should now be null");
 
       let request = mozIndexedDB.open(window.location.pathname, 1);
       request.onerror = errorHandler;
       request.onupgradeneeded = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.target.result;
 
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -2,17 +2,27 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
 function executeSoon(aFun)
 {
-  SimpleTest.executeSoon(aFun);
+  let comp = SpecialPowers.wrap(Components);
+
+  let thread = comp.classes["@mozilla.org/thread-manager;1"]
+                   .getService(comp.interfaces.nsIThreadManager)
+                   .mainThread;
+
+  thread.dispatch({
+    run: function() {
+      aFun();
+    }
+  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
 }
 
 function clearAllDatabases(callback) {
   function runCallback() {
     SimpleTest.executeSoon(function () { callback(); });
   }
 
   if (!SpecialPowers.isMainProcess()) {
--- a/dom/indexedDB/test/unit/test_setVersion_exclusion.js
+++ b/dom/indexedDB/test/unit/test_setVersion_exclusion.js
@@ -43,24 +43,21 @@ function testSteps()
   }
 
   request.onupgradeneeded = unexpectedSuccessHandler;
   request.transaction.oncomplete = grabEventAndContinueHandler;
 
   event = yield;
   is(event.type, "complete", "Got complete event");
 
-  // The database is still not fully open here.
   try {
     db.transaction("foo");
-    ok(false, "Transactions should be disallowed now!");
+    ok(true, "Transactions should be allowed now!");
   } catch (e) {
-    ok(e instanceof DOMException, "Expect a DOMException");
-    is(e.name, "InvalidStateError", "Expect an InvalidStateError");
-    is(e.code, DOMException.INVALID_STATE_ERR, "Expect an INVALID_STATE_ERR");
+    ok(false, "Transactions should be allowed now!");
   }
 
   request.onsuccess = grabEventAndContinueHandler;
 
   event = yield;
   is(event.type, "success", "Expect a success event");
   is(event.target.result, db, "Same database");
 
--- a/dom/indexedDB/test/unit/test_transaction_lifetimes.js
+++ b/dom/indexedDB/test/unit/test_transaction_lifetimes.js
@@ -5,27 +5,79 @@
 
 var testGenerator = testSteps();
 
 function testSteps()
 {
   let request = mozIndexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
   let event = yield;
 
   let db = event.target.result;
   db.onerror = errorHandler;
 
-  event.target.onsuccess = continueToNextStep;
+  event.target.transaction.onerror = errorHandler;
+  event.target.transaction.oncomplete = grabEventAndContinueHandler;
+
+  let os = db.createObjectStore("foo", { autoIncrement: true });
+  let index = os.createIndex("bar", "foo.bar");
+  event = yield;
+
+  is(request.transaction, event.target,
+     "request.transaction should still be set");
+
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield;
+
+  is(request.transaction, null, "request.transaction should be cleared");
+
+  let transaction = db.transaction("foo", "readwrite");
+
+  os = transaction.objectStore("foo");
+  // Place a request to keep the transaction alive long enough for our
+  // executeSoon.
+  let requestComplete = false;
 
-  db.createObjectStore("foo", { autoIncrement: true });
+  let wasAbleToGrabObjectStoreOutsideOfCallback = false;
+  let wasAbleToGrabIndexOutsideOfCallback = false;
+  executeSoon(function() {
+    ok(!requestComplete, "Ordering is correct.");
+    wasAbleToGrabObjectStoreOutsideOfCallback = !!transaction.objectStore("foo");
+    wasAbleToGrabIndexOutsideOfCallback =
+      !!transaction.objectStore("foo").index("bar");
+  });
+
+  request = os.add({});
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+
+  event = yield;
+
+  requestComplete = true;
+
+  ok(wasAbleToGrabObjectStoreOutsideOfCallback,
+     "Should be able to get objectStore");
+  ok(wasAbleToGrabIndexOutsideOfCallback,
+     "Should be able to get index");
+
+  transaction.oncomplete = grabEventAndContinueHandler;
   yield;
 
-  let transaction = db.transaction("foo");
+  try {
+    transaction.objectStore("foo");
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof DOMException, "Got database exception.");
+    is(e.name, "InvalidStateError", "Good error.");
+    is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
+  }
+
   continueToNextStep();
   yield;
 
   try {
     transaction.objectStore("foo");
     ok(false, "Should have thrown!");
   }
   catch (e) {
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -61,17 +61,17 @@ mOwner(owner)
   // create the file
   rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600);
   if (NS_FAILED(rv))
     return;
 	
   // construct the URL we'll use later in calls to GetURL()
   NS_GetURLSpecFromFile(mTempFile, mFileURL);
   
-#ifdef NS_DEBUG
+#ifdef DEBUG
   printf("File URL = %s\n", mFileURL.get());
 #endif
 }
 
 nsPluginStreamToFile::~nsPluginStreamToFile()
 {
   // should we be deleting mTempFile here?
   if (nsnull != mTarget)
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1631,17 +1631,17 @@ nsresult nsPluginHost::GetPlugin(const c
 
   nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
   if (pluginTag) {
     rv = NS_OK;
     PLUGIN_LOG(PLUGIN_LOG_BASIC,
     ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
     aMimeType, pluginTag->mFileName.get()));
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     if (aMimeType && !pluginTag->mFileName.IsEmpty())
       printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
 #endif
 
     rv = EnsurePluginLoaded(pluginTag);
     if (NS_FAILED(rv)) {
       return rv;
     }
--- a/dom/plugins/base/nsPluginNativeWindowGtk2.cpp
+++ b/dom/plugins/base/nsPluginNativeWindowGtk2.cpp
@@ -214,37 +214,37 @@ void nsPluginNativeWindowGtk2::SetAlloca
   new_allocation.width = width;
   new_allocation.height = height;
   gtk_widget_size_allocate(mSocketWidget, &new_allocation);
 }
 
 nsresult nsPluginNativeWindowGtk2::CreateXtWindow() {
   NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
 
-#ifdef NS_DEBUG      
+#ifdef DEBUG      
   printf("About to create new xtbin of %i X %i from %p...\n",
          width, height, (void*)window);
 #endif
   GdkWindow *gdkWindow = gdk_window_lookup((XID)window);
   mSocketWidget = gtk_xtbin_new(gdkWindow, 0);
   // Check to see if creating the xtbin failed for some reason.
   // if it did, we can't go any further.
   if (!mSocketWidget)
     return NS_ERROR_FAILURE;
 
   g_signal_connect(mSocketWidget, "destroy",
                    G_CALLBACK(gtk_widget_destroyed), &mSocketWidget);
 
   gtk_widget_set_size_request(mSocketWidget, width, height);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   printf("About to show xtbin(%p)...\n", (void*)mSocketWidget); fflush(NULL);
 #endif
   gtk_widget_show(mSocketWidget);
-#ifdef NS_DEBUG
+#ifdef DEBUG
   printf("completed gtk_widget_show(%p)\n", (void*)mSocketWidget); fflush(NULL);
 #endif
 
   // Fill out the ws_info structure.
   GtkXtBin* xtbin = GTK_XTBIN(mSocketWidget);
   // The xtbin has its own Display structure.
   mWsInfo.display = xtbin->xtdisplay;
   mWsInfo.colormap = xtbin->xtclient.xtcolormap;
--- a/dom/plugins/base/nsPluginsDirUnix.cpp
+++ b/dom/plugins/base/nsPluginsDirUnix.cpp
@@ -298,17 +298,17 @@ nsresult nsPluginFile::LoadPlugin(PRLibr
             return NS_ERROR_FAILURE;
         }
     }
 #else
     *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
     pLibrary = *outLibrary;
 #endif  // MOZ_WIDGET_GTK2
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     printf("LoadPlugin() %s returned %lx\n", 
            libSpec.value.pathname, (unsigned long)pLibrary);
 #endif
     
     return NS_OK;
 }
 
 nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -19,17 +19,17 @@
 #include "nsVariant.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIJSContextStack.h"
 #include "xpcpublic.h"
 #include "nsJSEnvironment.h"
 #include "nsDOMJSUtils.h"
-#ifdef NS_DEBUG
+#ifdef DEBUG
 
 #include "nspr.h" // PR_fprintf
 
 class EventListenerCounter
 {
 public:
   ~EventListenerCounter() {
   }
@@ -169,17 +169,17 @@ nsJSEventListener::HandleEvent(nsIDOMEve
     if (NS_FAILED(rv)) return rv;
     NS_ENSURE_TRUE(iargv != nsnull, NS_ERROR_OUT_OF_MEMORY);
     rv = iargv->AppendElement(aEvent, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // mContext is the same context which event listener manager pushes
   // to JS context stack.
-#ifdef NS_DEBUG
+#ifdef DEBUG
   JSContext* cx = nsnull;
   nsCOMPtr<nsIJSContextStack> stack =
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   NS_ASSERTION(stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx &&
                GetScriptContextFromJSContext(cx) == mContext,
                "JSEventListener has wrong script context?");
 #endif
   nsCOMPtr<nsIVariant> vrv;
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -60,16 +60,20 @@ interface nsIRILTelephonyCallback : nsIS
    */
   void notifyError(in long callIndex,
                    in AString error);
 };
 
 [scriptable, uuid(8a711703-1ee5-4675-9d9a-0b188e944cfe)]
 interface nsIRILDataCallInfo : nsISupports
 {
+  /**
+   * Current data call state, one of the
+   * nsINetworkInterface::NETWORK_STATE_* constants.
+   */
   readonly attribute unsigned long state;
   readonly attribute AString cid;
   readonly attribute AString apn;
   readonly attribute AString ifname;
 };
 
 [scriptable, uuid(5bcac053-c245-46f0-bb45-d0039bfb89f5)]
 interface nsIRILDataCallback : nsISupports
@@ -212,39 +216,32 @@ interface nsIRilContext : nsISupports
 
   readonly attribute nsICellLocation cell;
 
   readonly attribute nsIDOMMozMobileConnectionInfo voice;
 
   readonly attribute nsIDOMMozMobileConnectionInfo data;
 };
 
-[scriptable, uuid(92bea0af-8d75-4592-87d0-1cab88e36904)]
+[scriptable, uuid(8b649965-6687-46a8-88fa-a5495ce90735)]
 interface nsIRadioInterfaceLayer : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_BUSY = 3;
   const unsigned short CALL_STATE_CONNECTING = 4;
   const unsigned short CALL_STATE_CONNECTED = 5;
   const unsigned short CALL_STATE_HOLDING = 6;
   const unsigned short CALL_STATE_HELD = 7;
   const unsigned short CALL_STATE_RESUMING = 8;
   const unsigned short CALL_STATE_DISCONNECTING = 9;
   const unsigned short CALL_STATE_DISCONNECTED = 10;
   const unsigned short CALL_STATE_INCOMING = 11;
 
-  // Keep consistent with GECKO_DATACALL_STATE_* values in ril_consts.js
-  const unsigned short DATACALL_STATE_UNKNOWN = 0;
-  const unsigned short DATACALL_STATE_CONNECTING = 1;
-  const unsigned short DATACALL_STATE_CONNECTED = 2;
-  const unsigned short DATACALL_STATE_DISCONNECTING = 3;
-  const unsigned short DATACALL_STATE_DISCONNECTED = 4;
-
   /**
    * Activates or deactivates radio power.
    */
   void setRadioEnabled(in bool value);
 
   readonly attribute nsIRilContext rilContext;
 
   /**
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -1751,17 +1751,17 @@ let RIL = {
    *        DATACALL_AUTH_NONE        => PAP and CHAP is never performed.
    *        DATACALL_AUTH_PAP         => PAP may be performed.
    *        DATACALL_AUTH_CHAP        => CHAP may be performed.
    *        DATACALL_AUTH_PAP_OR_CHAP => PAP / CHAP may be performed.
    * @param pdptype
    *        String containing PDP type to request. ("IP", "IPV6", ...)
    */
   setupDataCall: function setupDataCall(options) {
-    let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL);
+    let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL, options);
     Buf.writeUint32(7);
     Buf.writeString(options.radioTech.toString());
     Buf.writeString(DATACALL_PROFILE_DEFAULT.toString());
     Buf.writeString(options.apn);
     Buf.writeString(options.user);
     Buf.writeString(options.passwd);
     Buf.writeString(options.chappap.toString());
     Buf.writeString(options.pdptype);
@@ -3292,51 +3292,53 @@ RIL[REQUEST_BASEBAND_VERSION] = function
 };
 RIL[REQUEST_SEPARATE_CONNECTION] = null;
 RIL[REQUEST_SET_MUTE] = null;
 RIL[REQUEST_GET_MUTE] = null;
 RIL[REQUEST_QUERY_CLIP] = null;
 RIL[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
 
 RIL.readDataCall_v5 = function readDataCall_v5() {
-  return {
-    cid: Buf.readUint32().toString(),
-    active: Buf.readUint32(), // DATACALL_ACTIVE_*
-    type: Buf.readString(),
-    apn: Buf.readString(),
-    address: Buf.readString()
-  };
-};
-
-RIL.readDataCall_v6 = function readDataCall_v6(obj) {
-  if (!obj) {
-    obj = {};
+  if (!options) {
+    options = {};
   }
-  obj.status = Buf.readUint32();  // DATACALL_FAIL_*
-  if (!RILQUIRKS_DATACALLSTATE_NO_SUGGESTEDRETRYTIME) {
-    obj.suggestedRetryTime = Buf.readUint32();
+  cid = Buf.readUint32().toString();
+  active = Buf.readUint32(); // DATACALL_ACTIVE_*
+  type = Buf.readString();
+  apn = Buf.readString();
+  address = Buf.readString();
+  return options;
+};
+
+RIL.readDataCall_v6 = function readDataCall_v6(options) {
+  if (!options) {
+    options = {};
   }
-  obj.cid = Buf.readUint32().toString();
-  obj.active = Buf.readUint32();  // DATACALL_ACTIVE_*
-  obj.type = Buf.readString();
-  obj.ifname = Buf.readString();
-  obj.ipaddr = Buf.readString();
-  obj.dns = Buf.readString();
-  obj.gw = Buf.readString();
-  if (obj.dns) {
-    obj.dns = obj.dns.split(" ");
+  options.status = Buf.readUint32();  // DATACALL_FAIL_*
+  if (!RILQUIRKS_DATACALLSTATE_NO_SUGGESTEDRETRYTIME) {
+    options.suggestedRetryTime = Buf.readUint32();
+  }
+  options.cid = Buf.readUint32().toString();
+  options.active = Buf.readUint32();  // DATACALL_ACTIVE_*
+  options.type = Buf.readString();
+  options.ifname = Buf.readString();
+  options.ipaddr = Buf.readString();
+  options.dns = Buf.readString();
+  options.gw = Buf.readString();
+  if (options.dns) {
+    options.dns = options.dns.split(" ");
   }
   //TODO for now we only support one address and gateway
-  if (obj.ipaddr) {
-    obj.ipaddr = obj.ipaddr.split(" ")[0];
+  if (options.ipaddr) {
+    options.ipaddr = options.ipaddr.split(" ")[0];
   }
-  if (obj.gw) {
-    obj.gw = obj.gw.split(" ")[0];
+  if (options.gw) {
+    options.gw = options.gw.split(" ")[0];
   }
-  return obj;
+  return options;
 };
 
 RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
   if (options.rilRequestError) {
     return;
   }
 
   this.initRILQuirks();
@@ -3349,19 +3351,19 @@ RIL[REQUEST_DATA_CALL_LIST] = function R
   if (!RILQUIRKS_V5_LEGACY) {
     version = Buf.readUint32();
   }
   let num = num = Buf.readUint32();
   let datacalls = {};
   for (let i = 0; i < num; i++) {
     let datacall;
     if (version < 6) {
-      datacall = this.readDataCall_v5();
+      datacall = this.readDataCall_v5(options);
     } else {
-      datacall = this.readDataCall_v6();
+      datacall = this.readDataCall_v6(options);
     }
     datacalls[datacall.cid] = datacall;
   }
 
   this._processDataCallList(datacalls);
 };
 RIL[REQUEST_RESET_RADIO] = null;
 RIL[REQUEST_OEM_HOOK_RAW] = null;
--- a/editor/composer/src/nsComposerCommands.cpp
+++ b/editor/composer/src/nsComposerCommands.cpp
@@ -488,28 +488,24 @@ nsIndentCommand::GetCommandStateParams(c
 
 //OUTDENT
 
 NS_IMETHODIMP
 nsOutdentCommand::IsCommandEnabled(const char * aCommandName,
                                    nsISupports *refCon,
                                    bool *outCmdEnabled)
 {
+  *outCmdEnabled = false;
+
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
-  nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(refCon);
-  if (editor && htmlEditor)
-  {
-    bool canIndent, isEditable = false;
-    nsresult rv = editor->GetIsSelectionEditable(&isEditable);
+  if (editor) {
+    nsresult rv = editor->GetIsSelectionEditable(outCmdEnabled);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (isEditable)
-      return htmlEditor->GetIndentState(&canIndent, outCmdEnabled);
   }
 
-  *outCmdEnabled = false;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsOutdentCommand::DoCommand(const char *aCommandName, nsISupports *refCon)
 {
   nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(refCon);
--- a/editor/composer/src/nsComposerDocumentCommands.cpp
+++ b/editor/composer/src/nsComposerDocumentCommands.cpp
@@ -179,22 +179,19 @@ nsSetDocumentOptionsCommand::GetCommandS
  *    We check the input command param for different behavior
  */
 
 NS_IMETHODIMP
 nsSetDocumentStateCommand::IsCommandEnabled(const char * aCommandName,
                                             nsISupports *refCon,
                                             bool *outCmdEnabled)
 {
+  // These commands are always enabled
   NS_ENSURE_ARG_POINTER(outCmdEnabled);
-  nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
-  if (editor)
-    return editor->GetIsSelectionEditable(outCmdEnabled);
-
-  *outCmdEnabled = false;
+  *outCmdEnabled = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSetDocumentStateCommand::DoCommand(const char *aCommandName,
                                      nsISupports *refCon)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/editor/libeditor/base/CreateElementTxn.cpp
+++ b/editor/libeditor/base/CreateElementTxn.cpp
@@ -8,17 +8,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeList.h"
 #include "nsISelection.h"
 #include "nsIDOMElement.h"
 #include "nsReadableUtils.h"
 
 #include "mozilla/dom/Element.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 using namespace mozilla;
 
 CreateElementTxn::CreateElementTxn()
   : EditTxn()
 {
@@ -55,17 +55,17 @@ NS_IMETHODIMP CreateElementTxn::Init(nsE
   mParent = do_QueryInterface(aParent);
   mOffsetInParent = aOffsetInParent;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP CreateElementTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     char* nodename = ToNewCString(mTag);
     printf("Do Create Element parent = %p <%s>, offset = %d\n", 
            static_cast<void*>(mParent.get()), nodename, mOffsetInParent);
     nsMemory::Free(nodename);
   }
 #endif
@@ -79,17 +79,17 @@ NS_IMETHODIMP CreateElementTxn::DoTransa
   nsresult result = mEditor->CreateHTMLContent(mTag, getter_AddRefs(newContent));
   NS_ENSURE_SUCCESS(result, result);
   NS_ENSURE_STATE(newContent);
 
   mNewNode = newContent->AsDOMNode();
   // Try to insert formatting whitespace for the new node:
   mEditor->MarkNodeDirty(mNewNode);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("  newNode = %p\n", static_cast<void*>(mNewNode.get()));
   }
 #endif
 
   // insert the new node
   if (CreateElementTxn::eAppend == PRInt32(mOffsetInParent)) {
@@ -129,17 +129,17 @@ NS_IMETHODIMP CreateElementTxn::DoTransa
   result = selection->CollapseNative(parentContent,
                                      parentContent->IndexOf(newContent) + 1);
   NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert.");
   return result;
 }
 
 NS_IMETHODIMP CreateElementTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("Undo Create Element, mParent = %p, node = %p\n",
            static_cast<void*>(mParent.get()),
            static_cast<void*>(mNewNode.get()));
   }
 #endif
 
@@ -147,17 +147,17 @@ NS_IMETHODIMP CreateElementTxn::UndoTran
   NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIDOMNode> resultNode;
   return mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode));
 }
 
 NS_IMETHODIMP CreateElementTxn::RedoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { printf("Redo Create Element\n"); }
 #endif
 
   NS_ASSERTION(mEditor && mParent, "bad state");
   NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED);
 
   // first, reset mNewNode so it has no attributes or content
   nsCOMPtr<nsIDOMCharacterData>nodeAsText = do_QueryInterface(mNewNode);
--- a/editor/libeditor/base/DeleteElementTxn.cpp
+++ b/editor/libeditor/base/DeleteElementTxn.cpp
@@ -3,21 +3,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 
 #include "DeleteElementTxn.h"
 #include "nsSelectionState.h"
-#ifdef NS_DEBUG
+#ifdef DEBUG
 #include "nsIDOMElement.h"
 #endif
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 
 DeleteElementTxn::DeleteElementTxn()
 : EditTxn()
 ,mElement()
 ,mParent()
@@ -62,30 +62,30 @@ NS_IMETHODIMP DeleteElementTxn::Init(nsI
 
   mRangeUpdater = aRangeUpdater;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Do Delete Element element = %p\n",
            static_cast<void*>(this),
            static_cast<void*>(mElement.get()));
   }
 #endif
 
   NS_ENSURE_TRUE(mElement, NS_ERROR_NOT_INITIALIZED);
 
   if (!mParent) { return NS_OK; }  // this is a no-op, there's no parent to delete mElement from
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   // begin debug output
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
   nsAutoString elementTag(NS_LITERAL_STRING("text node"));
   if (element)
     element->GetTagName(elementTag);
   nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(mParent);
   nsAutoString parentElementTag(NS_LITERAL_STRING("text node"));
   if (parentElement)
@@ -113,30 +113,30 @@ NS_IMETHODIMP DeleteElementTxn::DoTransa
     mRangeUpdater->SelAdjDeleteNode(mElement);
 
   nsCOMPtr<nsIDOMNode> resultNode;
   return mParent->RemoveChild(mElement, getter_AddRefs(resultNode));
 }
 
 NS_IMETHODIMP DeleteElementTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Undo Delete Element element = %p, parent = %p\n",
            static_cast<void*>(this),
            static_cast<void*>(mElement.get()),
            static_cast<void*>(mParent.get()));
   }
 #endif
 
   if (!mParent) { return NS_OK; } // this is a legal state, the txn is a no-op
   if (!mElement) { return NS_ERROR_NULL_POINTER; }
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   // begin debug output
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
   nsAutoString elementTag(NS_LITERAL_STRING("text node"));
   if (element)
     element->GetTagName(elementTag);
   nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(mParent);
   nsAutoString parentElementTag(NS_LITERAL_STRING("text node"));
   if (parentElement)
@@ -156,17 +156,17 @@ NS_IMETHODIMP DeleteElementTxn::UndoTran
 #endif
 
   nsCOMPtr<nsIDOMNode> resultNode;
   return mParent->InsertBefore(mElement, mRefNode, getter_AddRefs(resultNode));
 }
 
 NS_IMETHODIMP DeleteElementTxn::RedoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Redo Delete Element element = %p, parent = %p\n",
            static_cast<void*>(this),
            static_cast<void*>(mElement.get()),
            static_cast<void*>(mParent.get()));
   }
 #endif
--- a/editor/libeditor/base/DeleteRangeTxn.cpp
+++ b/editor/libeditor/base/DeleteRangeTxn.cpp
@@ -11,17 +11,17 @@
 #include "nsIContentIterator.h"
 #include "nsIContent.h"
 #include "nsComponentManagerUtils.h"
 
 #include "mozilla/Util.h"
 
 using namespace mozilla;
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 // note that aEditor is not refcounted
 DeleteRangeTxn::DeleteRangeTxn()
 : EditAggregateTxn()
 ,mRange()
 ,mStartParent()
@@ -105,17 +105,17 @@ NS_IMETHODIMP DeleteRangeTxn::Init(nsEdi
   }
 #endif // DEBUG
 
   return NS_OK;
 }
 
 NS_IMETHODIMP DeleteRangeTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { printf("Do Delete Range\n"); }
 #endif
 
   NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
 
   nsresult result; 
   // build the child transactions
 
@@ -166,28 +166,28 @@ NS_IMETHODIMP DeleteRangeTxn::DoTransact
     // do nothing - dom range gravity will adjust selection
   }
 
   return result;
 }
 
 NS_IMETHODIMP DeleteRangeTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { printf("Undo Delete Range\n"); }
 #endif
 
   NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
 
   return EditAggregateTxn::UndoTransaction();
 }
 
 NS_IMETHODIMP DeleteRangeTxn::RedoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { printf("Redo Delete Range\n"); }
 #endif
 
   NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED);
 
   return EditAggregateTxn::RedoTransaction();
 }
 
--- a/editor/libeditor/base/InsertElementTxn.cpp
+++ b/editor/libeditor/base/InsertElementTxn.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InsertElementTxn.h"
 #include "nsISelection.h"
 #include "nsIContent.h"
 #include "nsIDOMNodeList.h"
 #include "nsReadableUtils.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 
 InsertElementTxn::InsertElementTxn()
   : EditTxn()
 {
 }
@@ -50,17 +50,17 @@ NS_IMETHODIMP InsertElementTxn::Init(nsI
   mEditor = aEditor;
   NS_ENSURE_TRUE(mNode && mParent && mEditor, NS_ERROR_INVALID_ARG);
   return NS_OK;
 }
 
 
 NS_IMETHODIMP InsertElementTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) 
   { 
     nsCOMPtr<nsIContent>nodeAsContent = do_QueryInterface(mNode);
     nsCOMPtr<nsIContent>parentAsContent = do_QueryInterface(mParent);
     nsString namestr;
     mNode->GetNodeName(namestr);
     char* nodename = ToNewCString(namestr);
     printf("%p Do Insert Element of %p <%s> into parent %p at offset %d\n", 
@@ -111,17 +111,17 @@ NS_IMETHODIMP InsertElementTxn::DoTransa
   {
     // do nothing - dom range gravity will adjust selection
   }
   return result;
 }
 
 NS_IMETHODIMP InsertElementTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Undo Insert Element of %p into parent %p at offset %d\n",
            static_cast<void*>(this),
            static_cast<void*>(mNode.get()),
            static_cast<void*>(mParent.get()),
            mOffset);
   }
--- a/editor/libeditor/base/InsertTextTxn.cpp
+++ b/editor/libeditor/base/InsertTextTxn.cpp
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InsertTextTxn.h"
 #include "nsIDOMCharacterData.h"
 #include "nsISelection.h"
 #include "EditAggregateTxn.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 InsertTextTxn::InsertTextTxn()
   : EditTxn()
 {
 }
 
@@ -57,17 +57,17 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOM
   mOffset = aOffset;
   mStringToInsert = aStringToInsert;
   mEditor = aEditor;
   return NS_OK;
 }
 
 NS_IMETHODIMP InsertTextTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("Do Insert Text element = %p\n",
            static_cast<void*>(mElement.get()));
   }
 #endif
 
   NS_ASSERTION(mElement && mEditor, "bad state");
@@ -93,17 +93,17 @@ NS_IMETHODIMP InsertTextTxn::DoTransacti
     // do nothing - dom range gravity will adjust selection
   }
 
   return result;
 }
 
 NS_IMETHODIMP InsertTextTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("Undo Insert Text element = %p\n",
            static_cast<void*>(mElement.get()));
   }
 #endif
 
   NS_ASSERTION(mElement && mEditor, "bad state");
@@ -128,17 +128,17 @@ NS_IMETHODIMP InsertTextTxn::Merge(nsITr
     if (otherInsTxn)
     {
       if (IsSequentialInsert(otherInsTxn))
       {
         nsAutoString otherData;
         otherInsTxn->GetData(otherData);
         mStringToInsert += otherData;
         *aDidMerge = true;
-#ifdef NS_DEBUG
+#ifdef DEBUG
         if (gNoisy)
         {
           printf("InsertTextTxn assimilated %p\n",
                  static_cast<void*>(aTransaction));
         }
 #endif
       }
       NS_RELEASE(otherInsTxn);
--- a/editor/libeditor/base/JoinElementTxn.cpp
+++ b/editor/libeditor/base/JoinElementTxn.cpp
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "JoinElementTxn.h"
 #include "nsEditor.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMCharacterData.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 JoinElementTxn::JoinElementTxn()
   : EditTxn()
 {
 }
 
@@ -51,17 +51,17 @@ NS_IMETHODIMP JoinElementTxn::Init(nsEdi
   mRightNode = do_QueryInterface(aRightNode);
   mOffset=0;
   return NS_OK;
 }
 
 // After DoTransaction() and RedoTransaction(), the left node is removed from the content tree and right node remains.
 NS_IMETHODIMP JoinElementTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Do Join of %p and %p\n",
            static_cast<void*>(this),
            static_cast<void*>(mLeftNode.get()),
            static_cast<void*>(mRightNode.get()));
   }
 #endif
@@ -103,17 +103,17 @@ NS_IMETHODIMP JoinElementTxn::DoTransact
 
   return rv;
 }
 
 //XXX: what if instead of split, we just deleted the unneeded children of mRight
 //     and re-inserted mLeft?
 NS_IMETHODIMP JoinElementTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Undo Join, right node = %p\n",
            static_cast<void*>(this),
            static_cast<void*>(mRightNode.get()));
   }
 #endif
 
--- a/editor/libeditor/base/SplitElementTxn.cpp
+++ b/editor/libeditor/base/SplitElementTxn.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SplitElementTxn.h"
 #include "nsEditor.h"
 #include "nsIDOMNode.h"
 #include "nsISelection.h"
 #include "nsIDOMCharacterData.h"
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 static bool gNoisy = false;
 #endif
 
 
 // note that aEditor is not refcounted
 SplitElementTxn::SplitElementTxn()
   : EditTxn()
 {
@@ -47,17 +47,17 @@ NS_IMETHODIMP SplitElementTxn::Init(nsEd
   mEditor = aEditor;
   mExistingRightNode = do_QueryInterface(aNode);
   mOffset = aOffset;
   return NS_OK;
 }
 
 NS_IMETHODIMP SplitElementTxn::DoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("%p Do Split of node %p offset %d\n",
            static_cast<void*>(this),
            static_cast<void*>(mExistingRightNode.get()),
            mOffset);
   }
 #endif
@@ -67,17 +67,17 @@ NS_IMETHODIMP SplitElementTxn::DoTransac
 
   // create a new node
   nsresult result = mExistingRightNode->CloneNode(false, 1, getter_AddRefs(mNewLeftNode));
   NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element.");
   NS_ENSURE_SUCCESS(result, result);
   NS_ENSURE_TRUE(mNewLeftNode, NS_ERROR_NULL_POINTER);
   mEditor->MarkNodeDirty(mExistingRightNode);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy)
   {
     printf("  created left node = %p\n",
            static_cast<void*>(mNewLeftNode.get()));
   }
 #endif
 
   // get the parent node
@@ -103,34 +103,34 @@ NS_IMETHODIMP SplitElementTxn::DoTransac
       // do nothing - dom range gravity will adjust selection
     }
   }
   return result;
 }
 
 NS_IMETHODIMP SplitElementTxn::UndoTransaction(void)
 {
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { 
     printf("%p Undo Split of existing node %p and new node %p offset %d\n",
            static_cast<void*>(this),
            static_cast<void*>(mExistingRightNode.get()),
            static_cast<void*>(mNewLeftNode.get()),
            mOffset);
   }
 #endif
 
   NS_ASSERTION(mEditor && mExistingRightNode && mNewLeftNode && mParent, "bad state");
   if (!mEditor || !mExistingRightNode || !mNewLeftNode || !mParent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // this assumes Do inserted the new node in front of the prior existing node
   nsresult result = mEditor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, false);
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) 
   { 
     printf("** after join left child node %p into right node %p\n",
            static_cast<void*>(mNewLeftNode.get()),
            static_cast<void*>(mExistingRightNode.get()));
     if (gNoisy) {mEditor->DebugDumpContent(); } // DEBUG
   }
   if (NS_SUCCEEDED(result))
@@ -151,17 +151,17 @@ NS_IMETHODIMP SplitElementTxn::UndoTrans
  */
 NS_IMETHODIMP SplitElementTxn::RedoTransaction(void)
 {
   NS_ASSERTION(mEditor && mExistingRightNode && mNewLeftNode && mParent, "bad state");
   if (!mEditor || !mExistingRightNode || !mNewLeftNode || !mParent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) { 
     printf("%p Redo Split of existing node %p and new node %p offset %d\n",
            static_cast<void*>(this),
            static_cast<void*>(mExistingRightNode.get()),
            static_cast<void*>(mNewLeftNode.get()),
            mOffset);
     if (gNoisy) {mEditor->DebugDumpContent(); } // DEBUG
   }
@@ -169,17 +169,17 @@ NS_IMETHODIMP SplitElementTxn::RedoTrans
 
   nsresult result;
   nsCOMPtr<nsIDOMNode>resultNode;
   // first, massage the existing node so it is in its post-split state
   nsCOMPtr<nsIDOMCharacterData>rightNodeAsText = do_QueryInterface(mExistingRightNode);
   if (rightNodeAsText)
   {
     result = rightNodeAsText->DeleteData(0, mOffset);
-#ifdef NS_DEBUG
+#ifdef DEBUG
     if (gNoisy) 
     { 
       printf("** after delete of text in right text node %p offset %d\n",
              static_cast<void*>(rightNodeAsText.get()),
              mOffset);
       mEditor->DebugDumpContent();  // DEBUG
     }
 #endif
@@ -194,33 +194,33 @@ NS_IMETHODIMP SplitElementTxn::RedoTrans
     {
       if (NS_FAILED(result)) {return result;}
       if (!child) {return NS_ERROR_NULL_POINTER;}
       child->GetNextSibling(getter_AddRefs(nextSibling));
       result = mExistingRightNode->RemoveChild(child, getter_AddRefs(resultNode));
       if (NS_SUCCEEDED(result)) 
       {
         result = mNewLeftNode->AppendChild(child, getter_AddRefs(resultNode));
-#ifdef NS_DEBUG
+#ifdef DEBUG
         if (gNoisy) 
         { 
           printf("** move child node %p from right node %p to left node %p\n",
                  static_cast<void*>(child.get()),
                  static_cast<void*>(mExistingRightNode.get()),
                  static_cast<void*>(mNewLeftNode.get()));
           if (gNoisy) {mEditor->DebugDumpContent(); } // DEBUG
         }
 #endif
       }
       child = do_QueryInterface(nextSibling);
     }
   }
   // second, re-insert the left node into the tree 
   result = mParent->InsertBefore(mNewLeftNode, mExistingRightNode, getter_AddRefs(resultNode));
-#ifdef NS_DEBUG
+#ifdef DEBUG
   if (gNoisy) 
   { 
     printf("** reinsert left child node %p before right node %p\n",
            static_cast<void*>(mNewLeftNode.get()),
            static_cast<void*>(mExistingRightNode.get()));
     if (gNoisy) {mEditor->DebugDumpContent(); } // DEBUG
   }
 #endif
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/base/crashtests/766360.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var r = document.createRange();
+  r.setEnd(document.createTextNode("x"), 0);
+  window.getSelection().addRange(r);
+  document.execCommand("inserthtml", false, "y");
+}
+
+</script>
+</head>
+
+<body contenteditable="true" onload="boom();"></body>
+</html>
--- a/editor/libeditor/base/crashtests/crashtests.list
+++ b/editor/libeditor/base/crashtests/crashtests.list
@@ -6,9 +6,10 @@ load 407256-1.html
 load 430624-1.html
 load 459613.html
 load 475132-1.xhtml
 load 633709.xhtml
 load 636074-1.html
 load 713427-1.html
 load 713427-2.xhtml
 load 762183.html
+load 766360.html
 load 766413.html
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -4372,35 +4372,35 @@ nsEditor::DeleteSelectionImpl(EDirection
   return res;
 }
 
 // XXX: error handling in this routine needs to be cleaned up!
 NS_IMETHODIMP
 nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
                                            nsIDOMNode ** aNewNode)
 {
-  nsCOMPtr<nsIDOMNode> parentSelectedNode;
-  PRInt32 offsetOfNewNode;
-  nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
-                                                          offsetOfNewNode);
+  nsresult result = DeleteSelectionAndPrepareToCreateNode();
   NS_ENSURE_SUCCESS(result, result);
 
+  nsRefPtr<Selection> selection = GetSelection();
+  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
+
+  nsCOMPtr<nsINode> node = selection->GetAnchorNode();
+  PRInt32 offset = selection->GetAnchorOffset();
+
   nsCOMPtr<nsIDOMNode> newNode;
-  result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
+  result = CreateNode(aTag, node->AsDOMNode(), offset,
                       getter_AddRefs(newNode));
-  // XXX: ERROR_HANDLING  check result, and make sure aNewNode is set correctly in success/failure cases
+  // XXX: ERROR_HANDLING  check result, and make sure aNewNode is set correctly
+  // in success/failure cases
   *aNewNode = newNode;
   NS_IF_ADDREF(*aNewNode);
 
   // we want the selection to be just after the new node
-  nsCOMPtr<nsISelection> selection;
-  result = GetSelection(getter_AddRefs(selection));
-  NS_ENSURE_SUCCESS(result, result);
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  return selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
+  return selection->Collapse(node, offset + 1);
 }
 
 
 /* Non-interface, protected methods */
 
 PRInt32
 nsEditor::GetIMEBufferLength()
 {
@@ -4437,96 +4437,65 @@ nsEditor::SetIsIMEComposing(){
   return;
 }
 
 bool
 nsEditor::IsIMEComposing() {
   return mIsIMEComposing;
 }
 
-NS_IMETHODIMP
-nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
-{
-  nsresult result=NS_ERROR_NOT_INITIALIZED;
-  nsCOMPtr<nsISelection> selection;
-  result = GetSelection(getter_AddRefs(selection));
-  NS_ENSURE_SUCCESS(result, result);
+nsresult
+nsEditor::DeleteSelectionAndPrepareToCreateNode()
+{
+  nsresult res;
+  nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   if (!selection->Collapsed()) {
-    result = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
-    if (NS_FAILED(result)) {
-      return result;
-    }
-    // get the new selection
-    result = GetSelection(getter_AddRefs(selection));
-    if (NS_FAILED(result)) {
-      return result;
-    }
-
-    nsCOMPtr<nsIDOMNode> selectedNode;
-    selection->GetAnchorNode(getter_AddRefs(selectedNode));
-    // no selection is ok.
-    // if there is a selection, it must be collapsed
-    if (selectedNode && !selection->Collapsed()) {
-      result = selection->CollapseToEnd();
-      NS_ENSURE_SUCCESS(result, result);
+    res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+    NS_ENSURE_SUCCESS(res, res);
+
+    MOZ_ASSERT(selection->Collapsed(),
+               "Selection not collapsed after delete");
+  }
+
+  // If the selection is a chardata node, split it if necessary and compute
+  // where to put the new node
+  nsCOMPtr<nsINode> node = selection->GetAnchorNode();
+  MOZ_ASSERT(node, "Selection has no ranges in it");
+
+  if (node && node->IsNodeOfType(nsINode::eDATA_NODE)) {
+    NS_ASSERTION(node->GetNodeParent(),
+                 "It's impossible to insert into chardata with no parent -- "
+                 "fix the caller");
+    NS_ENSURE_STATE(node->GetNodeParent());
+
+    PRInt32 offset = selection->GetAnchorOffset();
+
+    if (offset == 0) {
+      res = selection->Collapse(node->GetNodeParent(),
+                                node->GetNodeParent()->IndexOf(node));
+      MOZ_ASSERT(NS_SUCCEEDED(res));
+      NS_ENSURE_SUCCESS(res, res);
+    } else if (offset == (PRInt32)node->Length()) {
+      res = selection->Collapse(node->GetNodeParent(),
+                                node->GetNodeParent()->IndexOf(node) + 1);
+      MOZ_ASSERT(NS_SUCCEEDED(res));
+      NS_ENSURE_SUCCESS(res, res);
+    } else {
+      nsCOMPtr<nsIDOMNode> tmp;
+      res = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp));
+      NS_ENSURE_SUCCESS(res, res);
+      res = selection->Collapse(node->GetNodeParent(),
+                                node->GetNodeParent()->IndexOf(node));
+      MOZ_ASSERT(NS_SUCCEEDED(res));
+      NS_ENSURE_SUCCESS(res, res);
     }
   }
-  // split the selected node
-  PRInt32 offsetOfSelectedNode;
-  result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
-  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
-  {
-    nsCOMPtr<nsIDOMNode> selectedNode;
-    PRUint32 selectedNodeContentCount=0;
-    nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
-    selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
-
-    offsetOfNewNode = offsetOfSelectedNode;
-    
-    /* if the selection is a text node, split the text node if necessary
-       and compute where to put the new node
-    */
-    if (selectedParentNodeAsText) 
-    { 
-      PRInt32 indexOfTextNodeInParent;
-      selectedNode = do_QueryInterface(parentSelectedNode);
-      selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
-      selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
-      GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
-
-      if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
-      {
-        nsCOMPtr<nsIDOMNode> newSiblingNode;
-        result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
-        // now get the node's offset in its parent, and insert the new tag there
-        if (NS_SUCCEEDED(result)) {
-          result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-        }
-      }
-      else 
-      { // determine where to insert the new node
-        if (0==offsetOfSelectedNode) {
-          offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
-        }
-        else {                 // insert new node as last child
-          GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
-          offsetOfNewNode++;    // offsets are 0-based, and we need the index of the new node
-        }
-      }
-    }
-    // Here's where the new node was inserted
-  }
-#ifdef DEBUG
-  else {
-    printf("InsertLineBreak into an empty document is not yet supported\n");
-  }
-#endif
-  return result;
+  return NS_OK;
 }
 
 
 
 NS_IMETHODIMP 
 nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
 {
   nsresult rv = NS_OK;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -309,18 +309,25 @@ protected:
   NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode,
                                    PRUint32    aOffset,
                                    SplitElementTxn **aTxn);
 
   NS_IMETHOD CreateTxnForJoinNode(nsIDOMNode  *aLeftNode,
                                   nsIDOMNode  *aRightNode,
                                   JoinElementTxn **aTxn);
 
-  NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, 
-                                                   PRInt32& offsetOfNewNode);
+  /**
+   * This method first deletes the selection, if it's not collapsed.  Then if
+   * the selection lies in a CharacterData node, it splits it.  If the
+   * selection is at this point collapsed in a CharacterData node, it's
+   * adjusted to be collapsed right before or after the node instead (which is
+   * always possible, since the node was split).
+   */
+  nsresult DeleteSelectionAndPrepareToCreateNode();
+
 
   // called after a transaction is done successfully
   NS_IMETHOD DoAfterDoTransaction(nsITransaction *aTxn);
   // called after a transaction is undone successfully
   NS_IMETHOD DoAfterUndoTransaction();
   // called after a transaction is redone successfully
   NS_IMETHOD DoAfterRedoTransaction();
 
--- a/editor/libeditor/base/nsEditorCommands.cpp
+++ b/editor/libeditor/base/nsEditorCommands.cpp
@@ -522,78 +522,70 @@ nsSwitchTextDirectionCommand::GetCommand
                                       nsISupports *aCommandRefCon)
 {
   bool canSwitchTextDirection = true;
   IsCommandEnabled(aCommandName, aCommandRefCon, &canSwitchTextDirection);
   return aParams->SetBooleanValue(STATE_ENABLED, canSwitchTextDirection);
 }
 
 NS_IMETHODIMP
-nsDeleteCommand::IsCommandEnabled(const char * aCommandName,
-                                  nsISupports *aCommandRefCon,
-                                  bool *outCmdEnabled)
+nsDeleteCommand::IsCommandEnabled(const char* aCommandName,
+                                  nsISupports* aCommandRefCon,
+                                  bool* outCmdEnabled)
 {
   NS_ENSURE_ARG_POINTER(outCmdEnabled);
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   *outCmdEnabled = false;
 
-  // we can delete when we can cut
   NS_ENSURE_TRUE(editor, NS_OK);
-    
-  bool isEditable = false;
-  nsresult rv = editor->GetIsSelectionEditable(&isEditable);
+
+  // We can generally delete whenever the selection is editable.  However,
+  // cmd_delete doesn't make sense if the selection is collapsed because it's
+  // directionless, which is the same condition under which we can't cut.
+  nsresult rv = editor->GetIsSelectionEditable(outCmdEnabled);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!isEditable)
-    return NS_OK;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_delete"))
-    return editor->CanCut(outCmdEnabled);
-  else if (!nsCRT::strcmp(aCommandName,"cmd_forwardDelete"))
-    return editor->CanCut(outCmdEnabled);
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteCharBackward"))
-    *outCmdEnabled = true;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteCharForward"))
-    *outCmdEnabled = true;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteWordBackward"))
-    *outCmdEnabled = true;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteWordForward"))
-    *outCmdEnabled = true;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteToBeginningOfLine"))
-    *outCmdEnabled = true;
-  else if (!nsCRT::strcmp(aCommandName,"cmd_deleteToEndOfLine"))
-    *outCmdEnabled = true;  
+  if (!nsCRT::strcmp("cmd_delete", aCommandName) && *outCmdEnabled) {
+    rv = editor->CanCut(outCmdEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsDeleteCommand::DoCommand(const char *aCommandName, nsISupports *aCommandRefCon)
+nsDeleteCommand::DoCommand(const char* aCommandName,
+                           nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE);
-    
+
   nsIEditor::EDirection deleteDir = nsIEditor::eNone;
-  
-  if (!nsCRT::strcmp("cmd_delete",aCommandName))
+
+  if (!nsCRT::strcmp("cmd_delete", aCommandName)) {
+    // Really this should probably be eNone, but it only makes a difference if
+    // the selection is collapsed, and then this command is disabled.  So let's
+    // keep it as it always was to avoid breaking things.
     deleteDir = nsIEditor::ePrevious;
-  else if (!nsCRT::strcmp("cmd_forwardDelete", aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteCharForward", aCommandName)) {
     deleteDir = nsIEditor::eNext;
-  else if (!nsCRT::strcmp("cmd_deleteCharBackward",aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteCharBackward", aCommandName)) {
     deleteDir = nsIEditor::ePrevious;
-  else if (!nsCRT::strcmp("cmd_deleteCharForward",aCommandName))
-    deleteDir = nsIEditor::eNext;
-  else if (!nsCRT::strcmp("cmd_deleteWordBackward",aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteWordBackward", aCommandName)) {
     deleteDir = nsIEditor::ePreviousWord;
-  else if (!nsCRT::strcmp("cmd_deleteWordForward",aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteWordForward", aCommandName)) {
     deleteDir = nsIEditor::eNextWord;
-  else if (!nsCRT::strcmp("cmd_deleteToBeginningOfLine",aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteToBeginningOfLine", aCommandName)) {
     deleteDir = nsIEditor::eToBeginningOfLine;
-  else if (!nsCRT::strcmp("cmd_deleteToEndOfLine",aCommandName))
+  } else if (!nsCRT::strcmp("cmd_deleteToEndOfLine", aCommandName)) {
     deleteDir = nsIEditor::eToEndOfLine;
+  } else {
+    MOZ_NOT_REACHED("Unrecognized nsDeleteCommand");
+  }
 
   return editor->DeleteSelection(deleteDir, nsIEditor::eStrip);
 }
 
 NS_IMETHODIMP 
 nsDeleteCommand::DoCommandParams(const char *aCommandName,
                                  nsICommandParams *aParams,
                                  nsISupports *aCommandRefCon)
--- a/editor/libeditor/base/nsEditorController.cpp
+++ b/editor/libeditor/base/nsEditorController.cpp
@@ -53,17 +53,16 @@ nsresult nsEditorController::RegisterEdi
   NS_REGISTER_ONE_COMMAND(nsSelectAllCommand, "cmd_selectAll");
 
   NS_REGISTER_ONE_COMMAND(nsPasteCommand, "cmd_paste");
   NS_REGISTER_ONE_COMMAND(nsPasteTransferableCommand, "cmd_pasteTransferable");
 
   NS_REGISTER_ONE_COMMAND(nsSwitchTextDirectionCommand, "cmd_switchTextDirection");
 
   NS_REGISTER_FIRST_COMMAND(nsDeleteCommand, "cmd_delete");
-  NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_forwardDelete");
   NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteCharBackward");
   NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteCharForward");
   NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteWordBackward");
   NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteWordForward");
   NS_REGISTER_NEXT_COMMAND(nsDeleteCommand, "cmd_deleteToBeginningOfLine");
   NS_REGISTER_LAST_COMMAND(nsDeleteCommand, "cmd_deleteToEndOfLine");
 
   // Insert content
--- a/editor/libeditor/base/tests/test_bug408231.html
+++ b/editor/libeditor/base/tests/test_bug408231.html
@@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 408231 **/
 
     var commandEnabledResults = [
       ["contentReadOnly", "true"],
       ["copy", "false"],
       ["createlink", "true"],
       ["cut", "false"],
       ["decreasefontsize", "true"],
-      ["delete", "false"],
+      ["delete", "true"],
       ["fontname", "true"],
       ["fontsize", "true"],
       ["formatblock", "true"],
       ["heading", "true"],
       ["hilitecolor", "true"],
       ["increasefontsize", "true"],
       ["indent", "true"],
       ["inserthorizontalrule", "true"],
@@ -39,17 +39,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       ["insertorderedlist", "true"],
       ["insertunorderedlist", "true"],
       ["insertparagraph", "true"],
       ["italic", "true"],
       ["justifycenter", "true"],
       ["justifyfull", "true"],
       ["justifyleft", "true"],
       ["justifyright", "true"],
-      ["outdent", "false"],
+      ["outdent", "true"],
       //["paste", "true"],
       ["redo", "false"],
       ["removeformat", "true"],
       ["selectall", "true"],
       ["strikethrough", "true"],
       ["styleWithCSS", "true"],
       ["subscript", "true"],
       ["superscript", "true"],
--- a/editor/libeditor/html/TextEditorTest.h
+++ b/editor/libeditor/html/TextEditorTest.h
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __TextEditorTest_h__
 #define __TextEditorTest_h__
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 
 #include "nsCOMPtr.h"
 #include "nsIEditor.h"
 #include "nsIPlaintextEditor.h"
 
 class TextEditorTest
 {
 public:
@@ -30,11 +30,11 @@ protected:
   nsresult TestInsertBreak();
 
   nsresult TestTextProperties();
 
   nsCOMPtr<nsIPlaintextEditor> mTextEditor;
   nsCOMPtr<nsIEditor> mEditor;
 };
 
-#endif /* NS_DEBUG */
+#endif /* DEBUG */
 
 #endif
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/crashtests/766387.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  window.getSelection().removeAllRanges();
+  var r = document.createRange();
+  r.setStart(document.getElementById("x"), 1);
+  r.setEnd(document.getElementById("y"), 0);
+  window.getSelection().addRange(r);
+  document.execCommand("insertorderedlist", false, null);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div id="x" contenteditable="true">a</div><div id="y" contenteditable="true"></div></body>
+</html>
--- a/editor/libeditor/html/crashtests/crashtests.list
+++ b/editor/libeditor/html/crashtests/crashtests.list
@@ -23,10 +23,11 @@ load 612565-1.html
 asserts(0-6) load 615015-1.html # Bug 439258
 load 615450-1.html
 load 639736-1.xhtml
 load 643786-1.html
 load 682650-1.html
 load 716456-1.html
 load 759748.html
 load 761861.html
+load 766305.html
+load 766387.html
 load 766795.html
-load 766305.html
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -356,17 +356,17 @@ nsHTMLEditor::DoInsertHTMLWithContext(co
     // indicate results.
     nsIDOMNode* firstNode = nodeList[0];
     if (!nsHTMLEditUtils::IsTableElement(firstNode))
       cellSelectionMode = false;
   }
 
   if (!cellSelectionMode)
   {
-    rv = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode);
+    rv = DeleteSelectionAndPrepareToCreateNode();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // pasting does not inherit local inline styles
     nsCOMPtr<nsIDOMNode> tmpNode =
       do_QueryInterface(selection->GetAnchorNode());
     PRInt32 tmpOffset = selection->GetAnchorOffset();
     rv = ClearStyle(address_of(tmpNode), &tmpOffset, nsnull, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -1715,19 +1715,17 @@ nsHTMLEditor::InsertElementAtSelection(n
         // E.g., inserting an image.  In this case we don't need to delete any
         // inline wrappers before we do the insertion.  Otherwise we let
         // DeleteSelectionAndPrepareToCreateNode do the deletion for us, which
         // calls DeleteSelection with aStripWrappers = eStrip.
         res = DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
         NS_ENSURE_SUCCESS(res, res);
       }
 
-      nsCOMPtr<nsIDOMNode> tempNode;
-      PRInt32 tempOffset;
-      nsresult result = DeleteSelectionAndPrepareToCreateNode(tempNode,tempOffset);
+      nsresult result = DeleteSelectionAndPrepareToCreateNode();
       NS_ENSURE_SUCCESS(result, result);
     }
 
     // If deleting, selection will be collapsed.
     // so if not, we collapse it
     if (!aDeleteSelection)
     {
       // Named Anchor is a special case,
@@ -3593,16 +3591,46 @@ nsHTMLEditor::IsModifiableNode(nsIDOMNod
 }
 
 bool
 nsHTMLEditor::IsModifiableNode(nsINode *aNode)
 {
   return !aNode || aNode->IsEditable();
 }
 
+NS_IMETHODIMP
+nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable)
+{
+  MOZ_ASSERT(aIsSelectionEditable);
+
+  nsRefPtr<Selection> selection = GetSelection();
+  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
+
+  // Per the editing spec as of June 2012: we have to have a selection whose
+  // start and end nodes are editable, and which share an ancestor editing
+  // host.  (Bug 766387.)
+  *aIsSelectionEditable = selection->GetRangeCount() &&
+                          selection->GetAnchorNode()->IsEditable() &&
+                          selection->GetFocusNode()->IsEditable();
+
+  if (*aIsSelectionEditable) {
+    nsINode* commonAncestor =
+      selection->GetAnchorFocusRange()->GetCommonAncestor();
+    while (commonAncestor && !commonAncestor->IsEditable()) {
+      commonAncestor = commonAncestor->GetNodeParent();
+    }
+    if (!commonAncestor) {
+      // No editable common ancestor
+      *aIsSelectionEditable = false;
+    }
+  }
+
+  return NS_OK;
+}
+
 static nsresult
 SetSelectionAroundHeadChildren(nsISelection* aSelection,
                                nsIWeakReference* aDocWeak)
 {
   // Set selection around <head> node
   nsCOMPtr<nsIDocument> doc = do_QueryReferent(aDocWeak);
   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
 
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -305,16 +305,18 @@ public:
                            PRUint32             aLength);
   NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, 
                             nsCOMPtr<nsIDOMNode> *aInOutNode, 
                             PRInt32 *aInOutOffset,
                             nsIDOMDocument *aDoc);
   NS_IMETHOD_(bool) IsModifiableNode(nsIDOMNode *aNode);
   virtual bool IsModifiableNode(nsINode *aNode);
 
+  NS_IMETHOD GetIsSelectionEditable(bool* aIsSelectionEditable);
+
   NS_IMETHOD SelectAll();
 
   NS_IMETHOD GetRootElement(nsIDOMElement **aRootElement);
 
   /* ------------ nsICSSLoaderObserver -------------- */
   NS_IMETHOD StyleSheetLoaded(nsCSSStyleSheet*aSheet, bool aWasAlternate,
                               nsresult aStatus);
 
--- a/editor/libeditor/html/tests/browserscope/lib/richtext2/currentStatus.js
+++ b/editor/libeditor/html/tests/browserscope/lib/richtext2/currentStatus.js
@@ -278,19 +278,16 @@ const knownFailures = {
     "QE-Proposed-UNSELECT_TEXT-1-body": true,
     "QE-Proposed-UNSELECT_TEXT-1-div": true,
     "QE-Proposed-REDO_TEXT-1-dM": true,
     "QE-Proposed-REDO_TEXT-1-body": true,
     "QE-Proposed-REDO_TEXT-1-div": true,
     "QE-Proposed-INSERTLINEBREAK_TEXT-1-dM": true,
     "QE-Proposed-INSERTLINEBREAK_TEXT-1-body": true,
     "QE-Proposed-INSERTLINEBREAK_TEXT-1-div": true,
-    "QE-Proposed-OUTDENT_TEXT-1-dM": true,
-    "QE-Proposed-OUTDENT_TEXT-1-body": true,
-    "QE-Proposed-OUTDENT_TEXT-1-div": true,
     "QE-Proposed-CREATEBOOKMARK_TEXT-1-dM": true,
     "QE-Proposed-CREATEBOOKMARK_TEXT-1-body": true,
     "QE-Proposed-CREATEBOOKMARK_TEXT-1-div": true,
     "QE-Proposed-UNBOOKMARK_TEXT-1-dM": true,
     "QE-Proposed-UNBOOKMARK_TEXT-1-body": true,
     "QE-Proposed-UNBOOKMARK_TEXT-1-div": true,
     "QS-Proposed-SUB_SPAN.sub-1-SI-dM": true,
     "QS-Proposed-SUB_SPAN.sub-1-SI-body": true,
--- a/editor/libeditor/html/tests/test_bug612128.html
+++ b/editor/libeditor/html/tests/test_bug612128.html
@@ -21,21 +21,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 612128 **/
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   document.querySelector("input").focus();
   var threw = false;
   try {
-    document.execCommand("inserthtml", null, "<span>f" + "oo</span>");
+    is(document.execCommand("inserthtml", null, "<span>f" + "oo</span>"),
+       false, "The insertHTML command should return false");
   } catch (e) {
-    threw = true;
+    ok(false, "insertHTML should not throw here");
   }
-  ok(threw, "The inserthtml command should fail");
   is(document.querySelectorAll("span").length, 0, "No span element should be injected inside the page");
   is(document.body.innerHTML.indexOf("f" + "oo"), -1, "No text should be injected inside the page");
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
--- a/editor/libeditor/html/tests/test_bug676401.html
+++ b/editor/libeditor/html/tests/test_bug676401.html
@@ -27,23 +27,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 676401 **/
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests);
 
 var gBlock1, gBlock2;
 
-function IsCommandEnabled(command, alwaysEnabled) {
+var alwaysEnabledCommands = [
+  "contentReadOnly",
+  "copy",
+  "enableInlineTableEditing",
+  "enableObjectResizing",
+  "insertBrOnReturn",
+  "selectAll",
+  "styleWithCSS",
+];
+
+function IsCommandEnabled(command) {
   var enabled;
 
   // non-editable div: should return false unless alwaysEnabled
   window.getSelection().selectAllChildren(gBlock1);
   enabled = document.queryCommandEnabled(command);
-  is(enabled, alwaysEnabled, "'" + command + "' should not be enabled on a non-editable block.");
+  is(enabled, alwaysEnabledCommands.indexOf(command) != -1,
+     "'" + command + "' should not be enabled on a non-editable block.");
 
   // editable div: should return true
   window.getSelection().selectAllChildren(gBlock2);
   enabled = document.queryCommandEnabled(command);
   is(enabled, true, "'" + command + "' should be enabled on an editable block.");
 }
 
 function runTests() {
@@ -62,47 +73,47 @@ function runTests() {
     "heading", "formatBlock",
     "contentReadOnly", "createLink",
     "decreaseFontSize", "increaseFontSize",
     "insertHTML", "insertHorizontalRule", "insertImage",
     "removeFormat", "selectAll", "styleWithCSS"
   ];
   document.execCommand("styleWithCSS", false, false);
   for (i = 0; i < commands.length; i++)
-    IsCommandEnabled(commands[i], commands[i] == "selectAll");
+    IsCommandEnabled(commands[i]);
   document.execCommand("styleWithCSS", false, true);
   for (i = 0; i < commands.length; i++)
-    IsCommandEnabled(commands[i], commands[i] == "selectAll");
+    IsCommandEnabled(commands[i]);
 
   // Mozilla-specific stuff
   commands = ["enableInlineTableEditing", "enableObjectResizing", "insertBrOnReturn"];
   for (i = 0; i < commands.length; i++)
-    IsCommandEnabled(commands[i], false);
+    IsCommandEnabled(commands[i]);
 
   // cut/copy/paste -- SpecialPowers required
   SpecialPowers.setCharPref("capability.policy.policynames",                      "allowclipboard");
   SpecialPowers.setCharPref("capability.policy.allowclipboard.sites",             "http://mochi.test:8888");
   SpecialPowers.setCharPref("capability.policy.allowclipboard.Clipboard.cutcopy", "allAccess");
   SpecialPowers.setCharPref("capability.policy.allowclipboard.Clipboard.paste",   "allAccess");
   commands = ["cut", "paste", "copy"];
   for (i = 0; i < commands.length; i++) {
-    IsCommandEnabled(commands[i], commands[i] == "copy");
+    IsCommandEnabled(commands[i]);
     document.execCommand(commands[i], false, false);
   }
   SpecialPowers.clearUserPref("capability.policy.policynames");
   SpecialPowers.clearUserPref("capability.policy.allowclipboard.sites");
   SpecialPowers.clearUserPref("capability.policy.allowclipboard.Clipboard.cutcopy");
   SpecialPowers.clearUserPref("capability.policy.allowclipboard.Clipboard.paste");
 
   // delete/undo/redo -- we have to execute this commands because:
   //  * there's nothing to undo if we haven't modified the selection first
   //  * there's nothing to redo if we haven't undone something first
   commands = ["delete", "undo", "redo"];
   for (i = 0; i < commands.length; i++) {
-    IsCommandEnabled(commands[i], false);
+    IsCommandEnabled(commands[i]);
     document.execCommand(commands[i], false, false);
   }
 
   // done
   SimpleTest.finish();
 }
 
 </script>
--- a/embedding/components/appstartup/src/nsAppStartupNotifier.cpp
+++ b/embedding/components/appstartup/src/nsAppStartupNotifier.cpp
@@ -75,17 +75,17 @@ NS_IMETHODIMP nsAppStartupNotifier::Obse
                     if (NS_SUCCEEDED(rv)) {
                         rv = startupObserver->Observe(nsnull, aTopic, nsnull);
      
                         // mainly for debugging if you want to know if your observer worked.
                         NS_ASSERTION(NS_SUCCEEDED(rv), "Startup Observer failed!\n");
                     }
                 }
                 else {
-                  #ifdef NS_DEBUG
+                  #ifdef DEBUG
                     nsCAutoString warnStr("Cannot create startup observer : ");
                     warnStr += contractId.get();
                     NS_WARNING(warnStr.get());
                   #endif
                 }
 
                 NS_TIME_FUNCTION_MARK("observer: category: %s cid: %s", categoryEntry.get(), nsPromiseFlatCString(contractId).get());
 
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -869,19 +869,19 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aF
     return;
   }
 
   ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
 
   IDWriteRenderingParams *params = NULL;
   if (aRenderOptions) {
     if (aRenderOptions->GetType() != FONT_DWRITE) {
-    gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
-    // This should never happen.
-    MOZ_ASSERT(false);
+      gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
+      // This should never happen.
+      MOZ_ASSERT(false);
     } else {
       params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderOptions)->mParams;
     }
   }
 
   if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA &&
       aOptions.mCompositionOp == OP_OVER && aPattern.GetType() == PATTERN_COLOR) {
     if (FillGlyphsManual(font, aBuffer,
@@ -890,17 +890,22 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aF
       return;
     }
   }
 
   ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
 
   PrepareForDrawing(rt);
 
-  rt->SetTextRenderingParams(params);
+  if (rt != mRT || params != mTextRenderingParams) {
+    rt->SetTextRenderingParams(params);
+    if (rt == mRT) {
+      mTextRenderingParams = params;
+    }
+  }
 
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   AutoDWriteGlyphRun autoRun;
   DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
 
   if (brush) {
     rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -202,16 +202,19 @@ private:
   IntSize mSize;
 
   RefPtr<ID3D10Device1> mDevice;
   RefPtr<ID3D10Texture2D> mTexture;
   RefPtr<ID3D10Texture2D> mCurrentClipMaskTexture;
   RefPtr<ID2D1PathGeometry> mCurrentClippedGeometry;
   mutable RefPtr<ID2D1RenderTarget> mRT;
 
+  // We store this to prevent excessive SetTextRenderingParams calls.
+  RefPtr<IDWriteRenderingParams> mTextRenderingParams;
+
   // Temporary texture and render target used for supporting alternative operators.
   RefPtr<ID3D10Texture2D> mTempTexture;
   RefPtr<ID3D10RenderTargetView> mRTView;
   RefPtr<ID3D10ShaderResourceView> mSRView;
   RefPtr<ID2D1RenderTarget> mTempRT;
   RefPtr<ID3D10RenderTargetView> mTempRTView;
 
   // List of pushed clips.
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -758,16 +758,18 @@ GLFormatForImage(gfxASurface::gfxImageFo
 {
     switch (aFormat) {
     case gfxASurface::ImageFormatARGB32:
     case gfxASurface::ImageFormatRGB24:
         // Thebes only supports RGBX, not packed RGB.
         return LOCAL_GL_RGBA;
     case gfxASurface::ImageFormatRGB16_565:
         return LOCAL_GL_RGB;
+    case gfxASurface::ImageFormatA8:
+        return LOCAL_GL_LUMINANCE;
     default:
         NS_WARNING("Unknown GL format for Image format");
     }
     return 0;
 }
 
 #ifdef MOZ_WIDGET_GONK
 static PixelFormat
@@ -775,29 +777,32 @@ PixelFormatForImage(gfxASurface::gfxImag
 {
     switch (aFormat) {
     case gfxASurface::ImageFormatARGB32:
         return PIXEL_FORMAT_RGBA_8888;
     case gfxASurface::ImageFormatRGB24:
         return PIXEL_FORMAT_RGBX_8888;
     case gfxASurface::ImageFormatRGB16_565:
         return PIXEL_FORMAT_RGB_565;
+    case gfxASurface::ImageFormatA8:
+        return PIXEL_FORMAT_L_8;
     default:
         MOZ_NOT_REACHED("Unknown gralloc pixel format for Image format");
     }
     return 0;
 }
 #endif
 
 static GLenum
 GLTypeForImage(gfxASurface::gfxImageFormat aFormat)
 {
     switch (aFormat) {
     case gfxASurface::ImageFormatARGB32:
     case gfxASurface::ImageFormatRGB24:
+    case gfxASurface::ImageFormatA8:
         return LOCAL_GL_UNSIGNED_BYTE;
     case gfxASurface::ImageFormatRGB16_565:
         return LOCAL_GL_UNSIGNED_SHORT_5_6_5;
     default:
         NS_WARNING("Unknown GL format for Image format");
     }
     return 0;
 }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -176,16 +176,22 @@ namespace layers {
 already_AddRefed<gfxASurface>
 LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
                                    gfxASurface::gfxImageFormat aFormat)
 {
   return gfxPlatform::GetPlatform()->
     CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat));
 }
 
+already_AddRefed<gfxASurface>
+LayerManager::CreateOptimalMaskSurface(const gfxIntSize &aSize)
+{
+  return CreateOptimalSurface(aSize, gfxASurface::ImageFormatA8);
+}
+
 TemporaryRef<DrawTarget>
 LayerManager::CreateDrawTarget(const IntSize &aSize,
                                SurfaceFormat aFormat)
 {
   return gfxPlatform::GetPlatform()->
     CreateOffscreenDrawTarget(aSize, aFormat);
 }
 
@@ -371,18 +377,16 @@ Layer::ComputeEffectiveTransformForMaskL
 {
   if (mMaskLayer) {
     mMaskLayer->mEffectiveTransform = aTransformToSurface;
 
 #ifdef DEBUG
     gfxMatrix maskTranslation;
     bool maskIs2D = mMaskLayer->GetTransform().CanDraw2D(&maskTranslation);
     NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
-    NS_ASSERTION(maskTranslation.HasOnlyIntegerTranslation(),
-                 "Mask layer has invalid transform.");
 #endif
     mMaskLayer->mEffectiveTransform.PreMultiply(mMaskLayer->GetTransform());
   }
 }
 
 void
 ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -417,28 +417,31 @@ public:
   /**
    * Type of layer manager his is. This is to be used sparsely in order to
    * avoid a lot of Layers backend specific code. It should be used only when
    * Layers backend specific functionality is necessary.
    */
   virtual LayersBackend GetBackendType() = 0;
  
   /**
-   * Creates a layer which is optimized for inter-operating with this layer
+   * Creates a surface which is optimized for inter-operating with this layer
    * manager.
    */
   virtual already_AddRefed<gfxASurface>
     CreateOptimalSurface(const gfxIntSize &aSize,
                          gfxASurface::gfxImageFormat imageFormat);
-
+ 
   /**
-   * Which image format to use as an alpha mask with this layer manager.
+   * Creates a surface for alpha masks which is optimized for inter-operating
+   * with this layer manager. In contrast to CreateOptimalSurface, this surface
+   * is optimised for drawing alpha only and we assume that drawing the mask
+   * is fairly simple.
    */
-  virtual gfxASurface::gfxImageFormat MaskImageFormat() 
-  { return gfxASurface::ImageFormatA8; }
+  virtual already_AddRefed<gfxASurface>
+    CreateOptimalMaskSurface(const gfxIntSize &aSize);
 
   /**
    * Creates a DrawTarget which is optimized for inter-operating with this
    * layermanager.
    */
   virtual TemporaryRef<mozilla::gfx::DrawTarget>
     CreateDrawTarget(const mozilla::gfx::IntSize &aSize,
                      mozilla::gfx::SurfaceFormat aFormat);
@@ -694,18 +697,17 @@ public:
    * See also ContainerState::BuildMaskLayer in FrameLayerBuilder.cpp.
    */
   void SetMaskLayer(Layer* aMaskLayer)
   {
 #ifdef DEBUG
     if (aMaskLayer) {
       gfxMatrix maskTransform;
       bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D(&maskTransform);
-      NS_ASSERTION(maskIs2D && maskTransform.HasOnlyIntegerTranslation(),
-                   "Mask layer has invalid transform.");
+      NS_ASSERTION(maskIs2D, "Mask layer has invalid transform.");
     }
 #endif
 
     mMaskLayer = aMaskLayer;
     Mutated();
   }
 
   /**
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -905,16 +905,19 @@ BasicImageLayer::GetAndPaintCurrentImage
   AutoLockImage autoLock(mContainer, getter_AddRefs(surface));
   Image *image = autoLock.GetImage();
   gfxIntSize size = mSize = autoLock.GetSize();
 
   if (!surface || surface->CairoStatus()) {
     return nsnull;
   }
 
+  NS_ASSERTION(surface->GetContentType() != gfxASurface::CONTENT_ALPHA,
+               "Image layer has alpha image");
+
   nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
   if (!pat) {
     return nsnull;
   }
 
   pat->SetFilter(mFilter);
   gfxIntSize sourceSize = surface->GetSize();
   if (mScaleMode != SCALE_NONE) {
@@ -2616,17 +2619,18 @@ BasicShadowableImageLayer::Paint(gfxCont
     BasicImageLayer::Paint(aContext, aMaskLayer);
     return;
   }
 
   if (!mContainer) {
     return;
   }
 
-  AutoLockImage autoLock(mContainer);
+  nsRefPtr<gfxASurface> surface;
+  AutoLockImage autoLock(mContainer, getter_AddRefs(surface));
 
   Image *image = autoLock.GetImage();
 
   if (!image) {
     return;
   }
 
   if (aMaskLayer) {
@@ -2686,21 +2690,26 @@ BasicShadowableImageLayer::Paint(gfxCont
 
   bool isOpaque = (GetContentFlags() & CONTENT_OPAQUE);
   if (oldSize != mSize || 
       !IsSurfaceDescriptorValid(mBackBuffer) ||
       isOpaque != mBufferIsOpaque) {
     DestroyBackBuffer();
     mBufferIsOpaque = isOpaque;
 
-    if (!BasicManager()->AllocBuffer(
-          mSize,
-          isOpaque ?
-            gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA,
-          &mBackBuffer))
+    gfxASurface::gfxContentType type = gfxASurface::CONTENT_COLOR_ALPHA;
+    if (surface) {
+      type = surface->GetContentType();
+    }
+    if (type != gfxASurface::CONTENT_ALPHA &&
+        isOpaque) {
+      type = gfxASurface::CONTENT_COLOR;
+    }
+
+    if (!BasicManager()->AllocBuffer(mSize, type, &mBackBuffer))
       NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
   }
 
   nsRefPtr<gfxASurface> backSurface =
     BasicManager()->OpenDescriptor(mBackBuffer);
   nsRefPtr<gfxContext> tmpCtx = new gfxContext(backSurface);
   tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
   PaintContext(pat,
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp
@@ -186,16 +186,20 @@ ImageLayerD3D10::RenderLayer()
   SetEffectTransformAndOpacity();
 
   ID3D10EffectTechnique *technique;
   nsRefPtr<IDXGIKeyedMutex> keyedMutex;
 
   if (image->GetFormat() == Image::CAIRO_SURFACE || image->GetFormat() == Image::REMOTE_IMAGE_BITMAP ||
       image->GetFormat() == Image::REMOTE_IMAGE_DXGI_TEXTURE)
   {
+    NS_ASSERTION(image->GetFormat() != Image::CAIRO_SURFACE ||
+                 !static_cast<CairoImage*>(image)->mSurface ||
+                 static_cast<CairoImage*>(image)->mSurface->GetContentType() != gfxASurface::CONTENT_ALPHA,
+                 "Image layer has alpha image");
     bool hasAlpha = false;
 
     nsRefPtr<ID3D10ShaderResourceView> srView = GetImageSRView(image, hasAlpha, getter_AddRefs(keyedMutex));
     if (!srView) {
       return;
     }
     
     PRUint8 shaderFlags = SHADER_PREMUL;
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -426,16 +426,24 @@ LayerManagerD3D10::CreateOptimalSurface(
 
   surface->SetData(&gKeyD3D10Texture,
                    texture.forget().get(),
                    ReleaseTexture);
 
   return surface.forget();
 }
 
+
+already_AddRefed<gfxASurface>
+LayerManagerD3D10::CreateOptimalMaskSurface(const gfxIntSize &aSize)
+{
+  return CreateOptimalSurface(aSize, gfxASurface::ImageFormatARGB32);
+}
+
+
 TemporaryRef<DrawTarget>
 LayerManagerD3D10::CreateDrawTarget(const IntSize &aSize,
                                     SurfaceFormat aFormat)
 {
   if ((aFormat != FORMAT_B8G8R8A8 &&
        aFormat != FORMAT_B8G8R8X8)) {
     return LayerManager::CreateDrawTarget(aSize, aFormat);
   }
--- a/gfx/layers/d3d10/LayerManagerD3D10.h
+++ b/gfx/layers/d3d10/LayerManagerD3D10.h
@@ -133,18 +133,18 @@ public:
   { return nsnull; }
 
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
 
   virtual already_AddRefed<gfxASurface>
     CreateOptimalSurface(const gfxIntSize &aSize,
                          gfxASurface::gfxImageFormat imageFormat);
 
-  virtual gfxASurface::gfxImageFormat MaskImageFormat() 
-  { return gfxASurface::ImageFormatARGB32; }
+  virtual already_AddRefed<gfxASurface>
+    CreateOptimalMaskSurface(const gfxIntSize &aSize);
 
   virtual TemporaryRef<mozilla::gfx::DrawTarget>
     CreateDrawTarget(const mozilla::gfx::IntSize &aSize,
                      mozilla::gfx::SurfaceFormat aFormat);
 
   virtual LayersBackend GetBackendType() { return LAYERS_D3D10; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 10"); }
 
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp
@@ -15,83 +15,93 @@
 #include "yuv_convert.h"
 #include "nsIServiceManager.h" 
 #include "nsIConsoleService.h" 
 #include "Nv3DVUtils.h"
 
 namespace mozilla {
 namespace layers {
 
+static inline _D3DFORMAT
+D3dFormatForGfxFormat(gfxImageFormat aFormat)
+{
+  if (aFormat == gfxASurface::ImageFormatA8) {
+    return D3DFMT_A8;
+  }
+
+  return D3DFMT_A8R8G8B8;
+}
+
 static already_AddRefed<IDirect3DTexture9>
 DataToTexture(IDirect3DDevice9 *aDevice,
               unsigned char *aData,
               int aStride,
-              const gfxIntSize &aSize)
+              const gfxIntSize &aSize,
+              _D3DFORMAT aFormat)
 {
   nsRefPtr<IDirect3DTexture9> texture;
   nsRefPtr<IDirect3DDevice9Ex> deviceEx;
   aDevice->QueryInterface(IID_IDirect3DDevice9Ex,
                           (void**)getter_AddRefs(deviceEx));
 
+  nsRefPtr<IDirect3DSurface9> surface;
+  D3DLOCKED_RECT lockedRect;
   if (deviceEx) {
     // D3D9Ex doesn't support managed textures. We could use dynamic textures
     // here but since Images are immutable that probably isn't such a great
     // idea.
     if (FAILED(aDevice->
                CreateTexture(aSize.width, aSize.height,
-                             1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
+                             1, 0, aFormat, D3DPOOL_DEFAULT,
                              getter_AddRefs(texture), NULL)))
     {
       return NULL;
     }
 
-    nsRefPtr<IDirect3DSurface9> surface;
+    nsRefPtr<IDirect3DTexture9> tmpTexture;
     if (FAILED(aDevice->
-               CreateOffscreenPlainSurface(aSize.width,
-                                           aSize.height,
-                                           D3DFMT_A8R8G8B8,
-                                           D3DPOOL_SYSTEMMEM,
-                                           getter_AddRefs(surface),
-                                           NULL)))
+               CreateTexture(aSize.width, aSize.height,
+                             1, 0, aFormat, D3DPOOL_SYSTEMMEM,
+                             getter_AddRefs(tmpTexture), NULL)))
     {
       return NULL;
     }
 
-    D3DLOCKED_RECT lockedRect;
+    tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surface));
     surface->LockRect(&lockedRect, NULL, 0);
-    for (int y = 0; y < aSize.height; y++) {
-      memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y,
-             aData + aStride * y,
-             aSize.width * 4);
+    NS_ASSERTION(lockedRect.pBits, "Could not lock surface");
+  } else {
+    if (FAILED(aDevice->
+               CreateTexture(aSize.width, aSize.height,
+                             1, 0, aFormat, D3DPOOL_MANAGED,
+                             getter_AddRefs(texture), NULL)))
+    {
+      return NULL;
     }
+
+    /* lock the entire texture */
+    texture->LockRect(0, &lockedRect, NULL, 0);
+  }
+
+  PRUint32 width = aSize.width;
+  if (aFormat == D3DFMT_A8R8G8B8) {
+    width *= 4;
+  }
+  for (int y = 0; y < aSize.height; y++) {
+    memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y,
+            aData + aStride * y,
+            width);
+  }
+
+  if (deviceEx) {
     surface->UnlockRect();
     nsRefPtr<IDirect3DSurface9> dstSurface;
     texture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
     aDevice->UpdateSurface(surface, NULL, dstSurface, NULL);
   } else {
-    if (FAILED(aDevice->
-               CreateTexture(aSize.width, aSize.height,
-                             1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
-                             getter_AddRefs(texture), NULL)))
-    {
-      return NULL;
-    }
-
-    D3DLOCKED_RECT lockrect;
-    /* lock the entire texture */
-    texture->LockRect(0, &lockrect, NULL, 0);
-
-    // copy over data. If we don't need to do any swaping we can
-    // use memcpy
-    for (int y = 0; y < aSize.height; y++) {
-      memcpy((char*)lockrect.pBits + lockrect.Pitch * y,
-             aData + aStride * y,
-             aSize.width * 4);
-    }
-
     texture->UnlockRect(0);
   }
 
   return texture.forget();
 }
 
 static already_AddRefed<IDirect3DTexture9>
 SurfaceToTexture(IDirect3DDevice9 *aDevice,
@@ -106,17 +116,18 @@ SurfaceToTexture(IDirect3DDevice9 *aDevi
                                        gfxASurface::ImageFormatARGB32);
     
     nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
     context->SetSource(aSurface);
     context->SetOperator(gfxContext::OPERATOR_SOURCE);
     context->Paint();
   }
 
-  return DataToTexture(aDevice, imageSurface->Data(), imageSurface->Stride(), aSize);
+  return DataToTexture(aDevice, imageSurface->Data(), imageSurface->Stride(),
+                       aSize, D3dFormatForGfxFormat(imageSurface->Format()));
 }
 
 static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage,
                                   IDirect3DDevice9 *aDevice,
                                   LayerManagerD3D9 *aManager)
 {
   nsAutoPtr<PlanarYCbCrD3D9BackendData> backendData(
     new PlanarYCbCrD3D9BackendData);
@@ -289,17 +300,17 @@ ImageLayerD3D9::GetTexture(Image *aImage
   NS_ASSERTION(aImage, "Null image.");
 
   if (aImage->GetFormat() == Image::REMOTE_IMAGE_BITMAP) {
     RemoteBitmapImage *remoteImage =
       static_cast<RemoteBitmapImage*>(aImage);
       
     if (!aImage->GetBackendData(LayerManager::LAYERS_D3D9)) {
       nsAutoPtr<TextureD3D9BackendData> dat(new TextureD3D9BackendData());
-      dat->mTexture = DataToTexture(device(), remoteImage->mData, remoteImage->mStride, remoteImage->mSize);
+      dat->mTexture = DataToTexture(device(), remoteImage->mData, remoteImage->mStride, remoteImage->mSize, D3DFMT_A8R8G8B8);
       if (dat->mTexture) {
         aImage->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget());
       }
     }
 
     aHasAlpha = remoteImage->mFormat == RemoteImageData::BGRA32;
   } else if (aImage->GetFormat() == Image::CAIRO_SURFACE) {
     CairoImage *cairoImage =
@@ -356,16 +367,21 @@ ImageLayerD3D9::RenderLayer()
 
   SetShaderTransformAndOpacity();
 
   gfxIntSize size = mScaleMode == SCALE_NONE ? image->GetSize() : mScaleToSize;
 
   if (image->GetFormat() == Image::CAIRO_SURFACE ||
       image->GetFormat() == Image::REMOTE_IMAGE_BITMAP)
   {
+    NS_ASSERTION(image->GetFormat() != Image::CAIRO_SURFACE ||
+                 !static_cast<CairoImage*>(image)->mSurface ||
+                 static_cast<CairoImage*>(image)->mSurface->GetContentType() != gfxASurface::CONTENT_ALPHA,
+                 "Image layer has alpha image");
+
     bool hasAlpha = false;
     nsRefPtr<IDirect3DTexture9> texture = GetTexture(image, hasAlpha);
 
     device()->SetVertexShaderConstantF(CBvLayerQuad,
                                        ShaderConstantRect(0,
                                                           0,
                                                           size.width,
                                                           size.height),
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -165,19 +165,16 @@ public:
    */ 
   Nv3DVUtils *GetNv3DVUtils()  { return mDeviceManager ? mDeviceManager->GetNv3DVUtils() : NULL; } 
 
   static void OnDeviceManagerDestroy(DeviceManagerD3D9 *aDeviceManager) {
     if(aDeviceManager == mDefaultDeviceManager)
       mDefaultDeviceManager = nsnull;
   }
 
-  virtual gfxASurface::gfxImageFormat MaskImageFormat() 
-  { return gfxASurface::ImageFormatARGB32; }
-
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() const { return "D3D9"; }
 #endif // MOZ_LAYERS_HAVE_LOG
 
   void ReportFailure(const nsACString &aMsg, HRESULT aCode);
 
 private:
   /* Default device manager instance */
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -266,16 +266,19 @@ ImageLayerOGL::RenderLayer(int,
   } else if (image->GetFormat() == Image::CAIRO_SURFACE) {
     CairoImage *cairoImage =
       static_cast<CairoImage*>(image);
 
     if (!cairoImage->mSurface) {
       return;
     }
 
+    NS_ASSERTION(cairoImage->mSurface->GetContentType() != gfxASurface::CONTENT_ALPHA,
+                 "Image layer has alpha image");
+
     CairoOGLBackendData *data =
       static_cast<CairoOGLBackendData*>(cairoImage->GetBackendData(LayerManager::LAYERS_OPENGL));
 
     if (data && data->mTexture.GetGLContext() != gl()) {
       // If this texture was allocated by another layer manager, clear
       // it out and re-allocate below.
       data = nsnull;
       cairoImage->SetBackendData(LayerManager::LAYERS_OPENGL, nsnull);
@@ -589,16 +592,19 @@ ImageLayerOGL::LoadAsTexture(GLuint aTex
   if (!cairoImage->mSurface) {
     return false;
   }
 
   CairoOGLBackendData *data = static_cast<CairoOGLBackendData*>(
     cairoImage->GetBackendData(LayerManager::LAYERS_OPENGL));
 
   if (!data) {
+    NS_ASSERTION(cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_ALPHA,
+                 "OpenGL mask layers must be backed by alpha surfaces");
+
     // allocate a new texture and save the details in the backend data
     data = new CairoOGLBackendData;
     data->mTextureSize = CalculatePOTSize(cairoImage->mSize, gl());
 
     GLTexture &texture = data->mTexture;
     texture.Allocate(mOGLManager->gl());
 
     if (!texture.IsAllocated()) {
@@ -770,16 +776,19 @@ ShadowImageLayerOGL::GetLayer()
 
 void
 ShadowImageLayerOGL::RenderLayer(int aPreviousFrameBuffer,
                                  const nsIntPoint& aOffset)
 {
   mOGLManager->MakeCurrent();
 
   if (mTexImage) {
+    NS_ASSERTION(mTexImage->GetContentType() != gfxASurface::CONTENT_ALPHA,
+                 "Image layer has alpha image");
+
     ShaderProgramOGL *colorProgram =
       mOGLManager->GetProgram(mTexImage->GetShaderProgramType(), GetMaskLayer());
 
     colorProgram->Activate();
     colorProgram->SetTextureUnit(0);
     colorProgram->SetLayerTransform(GetEffectiveTransform());
     colorProgram->SetLayerOpacity(GetEffectiveOpacity());
     colorProgram->SetRenderOffset(aOffset);
@@ -840,16 +849,19 @@ bool
 ShadowImageLayerOGL::LoadAsTexture(GLuint aTextureUnit, gfxIntSize* aSize)
 {
   if (!mTexImage) {
     return false;
   }
 
   mTexImage->BindTextureAndApplyFilter(aTextureUnit);
 
+  NS_ASSERTION(mTexImage->GetContentType() == gfxASurface::CONTENT_ALPHA,
+               "OpenGL mask layers must be backed by alpha surfaces");
+
   // We're assuming that the gl backend won't cheat and use NPOT
   // textures when glContext says it can't (which seems to happen
   // on a mac when you force POT textures)
   *aSize = CalculatePOTSize(mTexImage->GetSize(), gl());
   return true;
 }
 
 void
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -420,16 +420,23 @@ LayerManagerOGL::EndTransaction(DrawTheb
   mTarget = NULL;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   Log();
   MOZ_LAYERS_LOG(("]----- EndTransaction"));
 #endif
 }
 
+already_AddRefed<gfxASurface>
+LayerManagerOGL::CreateOptimalMaskSurface(const gfxIntSize &aSize)
+{
+  return gfxPlatform::GetPlatform()->
+    CreateOffscreenImageSurface(aSize, gfxASurface::CONTENT_ALPHA);
+}
+
 already_AddRefed<ThebesLayer>
 LayerManagerOGL::CreateThebesLayer()
 {
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nsnull;
   }
 
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -137,16 +137,19 @@ public:
   virtual already_AddRefed<ShadowContainerLayer> CreateShadowContainerLayer();
   virtual already_AddRefed<ShadowImageLayer> CreateShadowImageLayer();
   virtual already_AddRefed<ShadowColorLayer> CreateShadowColorLayer();
   virtual already_AddRefed<ShadowCanvasLayer> CreateShadowCanvasLayer();
 
   virtual LayersBackend GetBackendType() { return LAYERS_OPENGL; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("OpenGL"); }
 
+  virtual already_AddRefed<gfxASurface>
+    CreateOptimalMaskSurface(const gfxIntSize &aSize);
+
   /**
    * Helper methods.
    */
   void MakeCurrent(bool aForce = false) {
     if (mDestroyed) {
       NS_WARNING("Call on destroyed layer manager");
       return;
     }
@@ -309,19 +312,16 @@ public:
   }
 
   void BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
                                       const nsIntRect& aTexCoordRect,
                                       const nsIntSize& aTexSize,
                                       GLenum aWrapMode = LOCAL_GL_REPEAT,
                                       bool aFlipped = false);
 
-  virtual gfxASurface::gfxImageFormat MaskImageFormat() 
-  { return gfxASurface::ImageFormatARGB32; }
-
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() const { return "OGL"; }
 #endif // MOZ_LAYERS_HAVE_LOG
 
   const nsIntSize& GetWidgetSize() {
     return mWidgetSize;
   }
 
--- a/gfx/layers/opengl/LayerManagerOGLShaders.h
+++ b/gfx/layers/opengl/LayerManagerOGLShaders.h
@@ -146,17 +146,17 @@ varying vec2 vTexCoord;\n\
 #endif\n\
 \n\
 varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform vec4 uRenderColor;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = mask * uRenderColor;\n\
 }\n\
 ";
 
 static const char sRGBATextureLayerFS[] = "/* sRGBATextureLayerFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -197,17 +197,17 @@ varying vec2 vTexCoord;\n\
 #endif\n\
 \n\
 varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uTexture;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sRGBATextureLayerMask3DFS[] = "/* sRGBATextureLayerMask3DFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -225,17 +225,17 @@ varying vec2 vTexCoord;\n\
 \n\
 varying vec3 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uTexture;\n\
 void main()\n\
 {\n\
 vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;\n\
-float mask = texture2D(uMaskTexture, maskCoords).a;\n\
+float mask = texture2D(uMaskTexture, maskCoords).r;\n\
 \n\
 gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sRGBARectTextureLayerFS[] = "/* sRGBARectTextureLayerFS */\n\
 #extension GL_ARB_texture_rectangle : enable\n\
 /* Fragment Shader */\n\
@@ -290,17 +290,17 @@ varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 /* This should not be used on GL ES */\n\
 #ifndef GL_ES\n\
 uniform sampler2DRect uTexture;\n\
 uniform vec2 uTexCoordMultiplier;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = texture2DRect(uTexture, vec2(vTexCoord * uTexCoordMultiplier)) * uLayerOpacity * mask;\n\
 }\n\
 #else\n\
 void main()\n\
 {\n\
 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n\
 }\n\
@@ -328,17 +328,17 @@ uniform sampler2D uMaskTexture;\n\
 \n\
 /* This should not be used on GL ES */\n\
 #ifndef GL_ES\n\
 uniform sampler2DRect uTexture;\n\
 uniform vec2 uTexCoordMultiplier;\n\
 void main()\n\
 {\n\
 vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;\n\
-float mask = texture2D(uMaskTexture, maskCoords).a;\n\
+float mask = texture2D(uMaskTexture, maskCoords).r;\n\
 \n\
 gl_FragColor = texture2DRect(uTexture, vec2(vTexCoord * uTexCoordMultiplier)) * uLayerOpacity * mask;\n\
 }\n\
 #else\n\
 void main()\n\
 {\n\
 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n\
 }\n\
@@ -385,17 +385,17 @@ varying vec2 vTexCoord;\n\
 #endif\n\
 \n\
 varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uTexture;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = texture2D(uTexture, vTexCoord).bgra * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sRGBXTextureLayerFS[] = "/* sRGBXTextureLayerFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -436,17 +436,17 @@ varying vec2 vTexCoord;\n\
 #endif\n\
 \n\
 varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uTexture;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = vec4(texture2D(uTexture, vTexCoord).rgb, 1.0) * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sBGRXTextureLayerFS[] = "/* sBGRXTextureLayerFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -487,17 +487,17 @@ varying vec2 vTexCoord;\n\
 #endif\n\
 \n\
 varying vec2 vMaskCoord;\n\
 uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uTexture;\n\
 void main()\n\
 {\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = vec4(texture2D(uTexture, vTexCoord).bgr, 1.0) * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sYCbCrTextureLayerFS[] = "/* sYCbCrTextureLayerFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -566,17 +566,17 @@ vec4 yuv;\n\
 vec4 color;\n\
 yuv.r = texture2D(uCrTexture, vTexCoord).r - 0.5;\n\
 yuv.g = texture2D(uYTexture, vTexCoord).r - 0.0625;\n\
 yuv.b = texture2D(uCbTexture, vTexCoord).r - 0.5;\n\
 color.r = yuv.g * 1.164 + yuv.r * 1.596;\n\
 color.g = yuv.g * 1.164 - 0.813 * yuv.r - 0.391 * yuv.b;\n\
 color.b = yuv.g * 1.164 + yuv.b * 2.018;\n\
 color.a = 1.0;\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = color * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sComponentPass1FS[] = "/* sComponentPass1FS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -625,17 +625,17 @@ uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uBlackTexture;\n\
 uniform sampler2D uWhiteTexture;\n\
 void main()\n\
 {\n\
 vec3 onBlack = texture2D(uBlackTexture, vTexCoord).bgr;\n\
 vec3 onWhite = texture2D(uWhiteTexture, vTexCoord).bgr;\n\
 vec4 alphas = (1.0 - onWhite + onBlack).rgbg;\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = alphas * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sComponentPass2FS[] = "/* sComponentPass2FS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
@@ -684,17 +684,17 @@ uniform sampler2D uMaskTexture;\n\
 \n\
 uniform sampler2D uBlackTexture;\n\
 uniform sampler2D uWhiteTexture;\n\
 void main()\n\
 {\n\
 vec3 onBlack = texture2D(uBlackTexture, vTexCoord).bgr;\n\
 vec3 onWhite = texture2D(uWhiteTexture, vTexCoord).bgr;\n\
 vec4 alphas = (1.0 - onWhite + onBlack).rgbg;\n\
-float mask = texture2D(uMaskTexture, vMaskCoord).a;\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
 \n\
 gl_FragColor = vec4(onBlack, alphas.a) * uLayerOpacity * mask;\n\
 }\n\
 ";
 
 static const char sCopyVS[] = "/* sCopyVS */\n\
 /* Vertex Shader */\n\
 attribute vec4 aVertexCoord;\n\
--- a/gfx/layers/opengl/LayerManagerOGLShaders.txt
+++ b/gfx/layers/opengl/LayerManagerOGLShaders.txt
@@ -171,22 +171,22 @@ varying vec3 vMaskCoord;
 uniform sampler2D uMaskTexture;
 @end
 
 @define FRAGMENT_CALC_MASK<>
   float mask = 1.0;
 @end
 
 @define FRAGMENT_CALC_MASK<Mask>
-  float mask = texture2D(uMaskTexture, vMaskCoord).a;
+  float mask = texture2D(uMaskTexture, vMaskCoord).r;
 @end
 
 @define FRAGMENT_CALC_MASK<Mask3D>
   vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;
-  float mask = texture2D(uMaskTexture, maskCoords).a;
+  float mask = texture2D(uMaskTexture, maskCoords).r;
 @end
 
 // Solid color rendering.
 // texcoords are ignored (no texture to sample).
 // The layer opacity is baked in to the color.
 @shader sSolidColorLayer<mask:,Mask>FS
 #define NO_LAYER_OPACITY 1
 $LAYER_FRAGMENT<mask>$
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3406,16 +3406,18 @@ gfxFontGroup::MakeTextRun(const PRUnicha
 
 template<typename T>
 void
 gfxFontGroup::InitTextRun(gfxContext *aContext,
                           gfxTextRun *aTextRun,
                           const T *aString,
                           PRUint32 aLength)
 {
+    NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
+
     // we need to do numeral processing even on 8-bit text,
     // in case we're converting Western to Hindi/Arabic digits
     PRInt32 numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
     nsAutoArrayPtr<PRUnichar> transformedString;
     if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
         // scan the string for numerals that may need to be transformed;
         // if we find any, we'll make a local copy here and use that for
         // font matching and glyph generation/shaping
@@ -3522,16 +3524,19 @@ template<typename T>
 void
 gfxFontGroup::InitScriptRun(gfxContext *aContext,
                             gfxTextRun *aTextRun,
                             const T *aString,
                             PRUint32 aScriptRunStart,
                             PRUint32 aScriptRunEnd,
                             PRInt32 aRunScript)
 {
+    NS_ASSERTION(aScriptRunEnd > aScriptRunStart,
+                 "don't call InitScriptRun for a zero-length run");
+
     gfxFont *mainFont = GetFontAt(0);
 
     PRUint32 runStart = aScriptRunStart;
     nsAutoTArray<gfxTextRange,3> fontRanges;
     ComputeRanges(fontRanges, aString + aScriptRunStart,
                   aScriptRunEnd - aScriptRunStart, aRunScript);
     PRUint32 numRanges = fontRanges.Length();
 
@@ -3758,24 +3763,22 @@ gfxFontGroup::FindFontForChar(PRUint32 a
     return font.forget();
 }
 
 template<typename T>
 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
                                  const T *aString, PRUint32 aLength,
                                  PRInt32 aRunScript)
 {
-    aRanges.Clear();
-
-    if (aLength == 0) {
-        return;
-    }
+    NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
+    NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
 
     PRUint32 prevCh = 0;
     PRUint8 matchType = 0;
+    PRInt32 lastRangeIndex = -1;
 
     // initialize prevFont to the group's primary font, so that this will be
     // used for string-initial control chars, etc rather than risk hitting font
     // fallback for these (bug 716229)
     gfxFont *prevFont = GetFontAt(0);
 
     for (PRUint32 i = 0; i < aLength; i++) {
 
@@ -3798,41 +3801,44 @@ void gfxFontGroup::ComputeRanges(nsTArra
         }
 
         // find the font for this char
         nsRefPtr<gfxFont> font =
             FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
 
         prevCh = ch;
 
-        if (aRanges.Length() == 0) {
+        if (lastRangeIndex == -1) {
             // first char ==> make a new range
             aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
+            lastRangeIndex++;
             prevFont = font;
         } else {
             // if font has changed, make a new range
-            gfxTextRange& prevRange = aRanges[aRanges.Length() - 1];
+            gfxTextRange& prevRange = aRanges[lastRangeIndex];
             if (prevRange.font != font || prevRange.matchType != matchType) {
                 // close out the previous range
                 prevRange.end = origI;
                 aRanges.AppendElement(gfxTextRange(origI, i + 1,
                                                    font, matchType));
+                lastRangeIndex++;
 
                 // update prevFont for the next match, *unless* we switched
                 // fonts on a ZWJ, in which case propagating the changed font
                 // is probably not a good idea (see bug 619511)
                 if (sizeof(T) == sizeof(PRUint8) ||
                     !gfxFontUtils::IsJoinCauser(ch))
                 {
                     prevFont = font;
                 }
             }
         }
     }
-    aRanges[aRanges.Length() - 1].end = aLength;
+
+    aRanges[lastRangeIndex].end = aLength;
 }
 
 gfxUserFontSet* 
 gfxFontGroup::GetUserFontSet()
 {
     return mUserFontSet;
 }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -383,16 +383,26 @@ gfxPlatform::~gfxPlatform()
     // the cairo shutdown that happens in ~gfxPlatform).  It even looks
     // idempotent.  But it has fatal assertions that fire if stuff is
     // leaked, and we hit them.
     FcFini();
 #endif
 }
 
 already_AddRefed<gfxASurface>
+gfxPlatform::CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                         gfxASurface::gfxContentType aContentType)
+{ 
+  nsRefPtr<gfxASurface> newSurface;
+  newSurface = new gfxImageSurface(aSize, OptimalFormatForContent(aContentType));
+
+  return newSurface.forget();
+}
+
+already_AddRefed<gfxASurface>
 gfxPlatform::OptimizeImage(gfxImageSurface *aSurface,
                            gfxASurface::gfxImageFormat format)
 {
     const gfxIntSize& surfaceSize = aSurface->GetSize();
 
 #ifdef XP_WIN
     if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == 
         gfxWindowsPlatform::RENDER_DIRECT2D) {
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -151,16 +151,27 @@ public:
 
     /**
      * Create an offscreen surface of the given dimensions
      * and image format.
      */
     virtual already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
                                                                  gfxASurface::gfxContentType contentType) = 0;
 
+    /**
+     * Create an offscreen surface of the given dimensions and image format which
+     * can be converted to a gfxImageSurface without copying. If we can provide
+     * a platform-hosted surface, then we will return that instead of an actual
+     * gfxImageSurface.
+     * Sub-classes should override this method if CreateOffscreenSurface returns a
+     * surface which implements GetAsImageSurface
+     */
+    virtual already_AddRefed<gfxASurface>
+      CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContentType);
 
     virtual already_AddRefed<gfxASurface> OptimizeImage(gfxImageSurface *aSurface,
                                                         gfxASurface::gfxImageFormat format);
 
     virtual mozilla::RefPtr<mozilla::gfx::DrawTarget>
       CreateDrawTargetForSurface(gfxASurface *aSurface);
 
     /*
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -95,16 +95,29 @@ gfxPlatformMac::CreateOffscreenSurface(c
 
     newSurface = new gfxQuartzSurface(size, OptimalFormatForContent(contentType));
 
     NS_IF_ADDREF(newSurface);
     return newSurface;
 }
 
 already_AddRefed<gfxASurface>
+gfxPlatformMac::CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                            gfxASurface::gfxContentType aContentType)
+{
+    nsRefPtr<gfxASurface> surface = CreateOffscreenSurface(aSize, aContentType);
+#ifdef DEBUG
+    nsRefPtr<gfxImageSurface> imageSurface = surface->GetAsImageSurface();
+    NS_ASSERTION(imageSurface, "Surface cannot be converted to a gfxImageSurface");
+#endif
+    return surface.forget();
+}
+
+
+already_AddRefed<gfxASurface>
 gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
                               gfxASurface::gfxImageFormat format)
 {
     const gfxIntSize& surfaceSize = aSurface->GetSize();
     nsRefPtr<gfxImageSurface> isurf = aSurface;
 
     if (format != aSurface->Format()) {
         isurf = new gfxImageSurface (surfaceSize, format);
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -26,16 +26,19 @@ public:
     virtual ~gfxPlatformMac();
 
     static gfxPlatformMac *GetPlatform() {
         return (gfxPlatformMac*) gfxPlatform::GetPlatform();
     }
 
     already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
                                                          gfxASurface::gfxContentType contentType);
+    virtual already_AddRefed<gfxASurface>
+      CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContentType);
     
     already_AddRefed<gfxASurface> OptimizeImage(gfxImageSurface *aSurface,
                                                 gfxASurface::gfxImageFormat format);
     
     mozilla::RefPtr<mozilla::gfx::ScaledFont>
       GetScaledFontForFont(gfxFont *aFont);
 
     virtual bool SupportsAzure(mozilla::gfx::BackendType& aBackend);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -707,16 +707,34 @@ gfxWindowsPlatform::CreateOffscreenSurfa
     if (surf == nsnull)
         surf = new gfxImageSurface(size, OptimalFormatForContent(contentType));
 
     NS_IF_ADDREF(surf);
 
     return surf;
 }
 
+already_AddRefed<gfxASurface>
+gfxWindowsPlatform::CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                                gfxASurface::gfxContentType aContentType)
+{
+#ifdef CAIRO_HAS_D2D_SURFACE
+    if (mRenderMode == RENDER_DIRECT2D) {
+        return new gfxImageSurface(aSize, OptimalFormatForContent(aContentType));
+    }
+#endif
+
+    nsRefPtr<gfxASurface> surface = CreateOffscreenSurface(aSize, aContentType);
+#ifdef DEBUG
+    nsRefPtr<gfxImageSurface> imageSurface = surface->GetAsImageSurface();
+    NS_ASSERTION(imageSurface, "Surface cannot be converted to a gfxImageSurface");
+#endif
+    return surface.forget();
+}
+
 RefPtr<ScaledFont>
 gfxWindowsPlatform::GetScaledFontForFont(gfxFont *aFont)
 {
     if (aFont->GetType() == gfxFont::FONT_TYPE_DWRITE) {
         gfxDWriteFont *font = static_cast<gfxDWriteFont*>(aFont);
 
         NativeFont nativeFont;
         nativeFont.mType = NATIVE_FONT_DWRITE_FONT_FACE;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -98,16 +98,20 @@ public:
     static gfxWindowsPlatform *GetPlatform() {
         return (gfxWindowsPlatform*) gfxPlatform::GetPlatform();
     }
 
     virtual gfxPlatformFontList* CreatePlatformFontList();
 
     already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
                                                          gfxASurface::gfxContentType contentType);
+    virtual already_AddRefed<gfxASurface>
+      CreateOffscreenImageSurface(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContentType);
+
     virtual mozilla::RefPtr<mozilla::gfx::ScaledFont>
       GetScaledFontForFont(gfxFont *aFont);
     virtual already_AddRefed<gfxASurface>
       GetThebesSurfaceForDrawTarget(mozilla::gfx::DrawTarget *aTarget);
     
     virtual bool SupportsAzure(mozilla::gfx::BackendType& aBackend);
 
     enum RenderMode {
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -904,17 +904,17 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
     mGotData = true;
 
     /* look at the first few bytes and see if we can tell what the data is from that
      * since servers tend to lie. :(
      */
     PRUint32 out;
     inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
 #endif
 
     nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
     if (mContentType.IsEmpty()) {
       LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
 
       rv = NS_ERROR_FAILURE;
--- a/intl/uconv/tests/TestUConv.cpp
+++ b/intl/uconv/tests/TestUConv.cpp
@@ -338,17 +338,17 @@ nsresult nsTestUConv::DisplayCharsets()
 
     nsCOMPtr<nsIUnicodeDecoder> dec = NULL;
     res = ccMan->GetUnicodeDecoder(charset.get(), getter_AddRefs(dec));
     if (NS_FAILED(res)) printf (" "); 
     else {
       printf("D");
       decCount++;
     }
-#ifdef NS_DEBUG
+#ifdef DEBUG
     // show the "basic" decoder classes
     if (dec) {
       nsCOMPtr<nsIBasicDecoder> isBasic = do_QueryInterface(dec);
       if (isBasic) {
         basicDecCount++;
         printf("b");
       }
       else printf(" ");
@@ -359,17 +359,17 @@ nsresult nsTestUConv::DisplayCharsets()
     nsCOMPtr<nsIUnicodeEncoder> enc = NULL;
     res = ccMan->GetUnicodeEncoder(charset.get(), getter_AddRefs(enc));
     if (NS_FAILED(res)) printf (" "); 
     else {
       printf("E");
       encCount++;
     }
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     if (enc) {
       nsCOMPtr<nsIBasicEncoder> isBasic = do_QueryInterface(enc);
       if (isBasic) {
         basicEncCount++;
         printf("b");
       }
       else printf(" ");
     }
--- a/intl/uconv/util/nsUCSupport.cpp
+++ b/intl/uconv/util/nsUCSupport.cpp
@@ -24,17 +24,17 @@ nsBasicDecoderSupport::nsBasicDecoderSup
 
 nsBasicDecoderSupport::~nsBasicDecoderSupport()
 {
 }
 
 //----------------------------------------------------------------------
 // Interface nsISupports [implementation]
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 NS_IMPL_THREADSAFE_ISUPPORTS2(nsBasicDecoderSupport,
                               nsIUnicodeDecoder,
                               nsIBasicDecoder)
 #else
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsBasicDecoderSupport, nsIUnicodeDecoder)
 #endif
 
 //----------------------------------------------------------------------
@@ -328,17 +328,17 @@ nsBasicEncoder::~nsBasicEncoder()
 {
 }
 
 //----------------------------------------------------------------------
 // Interface nsISupports [implementation]
 
 NS_IMPL_ADDREF(nsBasicEncoder)
 NS_IMPL_RELEASE(nsBasicEncoder)
-#ifdef NS_DEBUG
+#ifdef DEBUG
 NS_IMPL_QUERY_INTERFACE2(nsBasicEncoder,
                          nsIUnicodeEncoder,
                          nsIBasicEncoder)
 #else
 NS_IMPL_QUERY_INTERFACE1(nsBasicEncoder,
                          nsIUnicodeEncoder)
 #endif
 //----------------------------------------------------------------------
--- a/intl/uconv/util/nsUCSupport.h
+++ b/intl/uconv/util/nsUCSupport.h
@@ -16,17 +16,17 @@
 
 inline bool WillOverrun(PRUnichar* aDest, PRUnichar* aDestEnd, PRUint32 aLength)
 {
   NS_ASSERTION(aDest <= aDestEnd, "Pointer overrun even before check");
   return (PRUint32(aDestEnd - aDest) < aLength);
 }
 #define CHECK_OVERRUN(dest, destEnd, length) (WillOverrun(dest, destEnd, length))
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
 // {7AFC9F0A-CFE1-44ea-A755-E3B86AB1226E}
 #define NS_IBASICDECODER_IID \
 { 0x7afc9f0a, 0xcfe1, 0x44ea, { 0xa7, 0x55, 0xe3, 0xb8, 0x6a, 0xb1, 0x22, 0x6e } }
 
 // {65968A7B-6467-4c4a-B50A-3E0C97A32F07}
 #define NS_IBASICENCODER_IID \
 { 0x65968a7b, 0x6467, 0x4c4a, { 0xb5, 0xa, 0x3e, 0xc, 0x97, 0xa3, 0x2f, 0x7 } }
 
@@ -60,17 +60,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIBasicEn
  * This class implements:
  * - nsISupports
  * - nsIUnicodeDecoder
  *
  * @created         19/Apr/1999
  * @author  Catalin Rotaru [CATA]
  */
 class nsBasicDecoderSupport : public nsIUnicodeDecoder
-#ifdef NS_DEBUG
+#ifdef DEBUG
                               ,public nsIBasicDecoder
 #endif
 {
   NS_DECL_ISUPPORTS
 
 public:
 
   /**
@@ -265,17 +265,17 @@ protected:
       PRInt32 * aDestLength);
   NS_IMETHOD Reset();
 };
 
 //----------------------------------------------------------------------
 // Class nsBasicEncoder [declaration]
 
 class nsBasicEncoder : public nsIUnicodeEncoder
-#ifdef NS_DEBUG
+#ifdef DEBUG
                        ,public nsIBasicEncoder
 #endif
 {
   NS_DECL_ISUPPORTS
 
 public:
   /**
    * Class constructor.
--- a/ipc/glue/SharedMemorySysV.h
+++ b/ipc/glue/SharedMemorySysV.h
@@ -95,17 +95,17 @@ public:
     }
 
     // Mark the handle as deleted so that, should this process go away, the
     // segment is cleaned up.
     shmctl(mHandle, IPC_RMID, 0);
 
     mData = mem;
 
-#ifdef NS_DEBUG
+#ifdef DEBUG
     struct shmid_ds info;
     if (shmctl(mHandle, IPC_STAT, &info) < 0)
       return false;
 
     NS_ABORT_IF_FALSE(nBytes <= info.shm_segsz,
                       "Segment doesn't have enough space!");
 #endif
 
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -25,16 +25,23 @@ namespace JS {
 
 /* Data for tracking analysis/inference memory usage. */
 struct TypeInferenceSizes
 {
     size_t scripts;
     size_t objects;
     size_t tables;
     size_t temporary;
+
+    void add(TypeInferenceSizes &sizes) {
+        this->scripts   += sizes.scripts;
+        this->objects   += sizes.objects;
+        this->tables    += sizes.tables;
+        this->temporary += sizes.temporary;
+    }
 };
 
 // These measurements relate directly to the JSRuntime, and not to
 // compartments within it.
 struct RuntimeSizes
 {
     RuntimeSizes()
       : object(0)
@@ -74,19 +81,21 @@ struct RuntimeSizes
 
 struct CompartmentStats
 {
     CompartmentStats() {
         memset(this, 0, sizeof(*this));
     }
 
     void   *extra;
-    size_t gcHeapArenaHeaders;
-    size_t gcHeapArenaPadding;
-    size_t gcHeapArenaUnused;
+
+    // If you add a new number, remember to update add() and maybe
+    // gcHeapThingsSize()!
+    size_t gcHeapArenaAdmin;
+    size_t gcHeapUnusedGcThings;
 
     size_t gcHeapObjectsNonFunction;
     size_t gcHeapObjectsFunction;
     size_t gcHeapStrings;
     size_t gcHeapShapesTree;
     size_t gcHeapShapesDict;
     size_t gcHeapShapesBase;
     size_t gcHeapScripts;
@@ -103,62 +112,108 @@ struct CompartmentStats
     size_t shapesExtraDictTables;
     size_t shapesExtraTreeShapeKids;
     size_t shapesCompartmentTables;
     size_t scriptData;
     size_t mjitData;
     size_t crossCompartmentWrappers;
 
     TypeInferenceSizes typeInferenceSizes;
+
+    // Add cStats's numbers to this object's numbers.
+    void add(CompartmentStats &cStats) {
+        #define ADD(x)  this->x += cStats.x
+
+        ADD(gcHeapArenaAdmin);
+        ADD(gcHeapUnusedGcThings);
+
+        ADD(gcHeapObjectsNonFunction);
+        ADD(gcHeapObjectsFunction);
+        ADD(gcHeapStrings);
+        ADD(gcHeapShapesTree);
+        ADD(gcHeapShapesDict);
+        ADD(gcHeapShapesBase);
+        ADD(gcHeapScripts);
+        ADD(gcHeapTypeObjects);
+    #if JS_HAS_XML_SUPPORT
+        ADD(gcHeapXML);
+    #endif
+
+        ADD(objectSlots);
+        ADD(objectElements);
+        ADD(objectMisc);
+        ADD(stringChars);
+        ADD(shapesExtraTreeTables);
+        ADD(shapesExtraDictTables);
+        ADD(shapesExtraTreeShapeKids);
+        ADD(shapesCompartmentTables);
+        ADD(scriptData);
+        ADD(mjitData);
+        ADD(crossCompartmentWrappers);
+
+        #undef ADD
+
+        typeInferenceSizes.add(cStats.typeInferenceSizes);
+    }
+
+    // The size of all the live things in the GC heap.
+    size_t gcHeapThingsSize();
 };
 
 struct RuntimeStats
 {
     RuntimeStats(JSMallocSizeOfFun mallocSizeOf)
       : runtime()
       , gcHeapChunkTotal(0)
-      , gcHeapCommitted(0)
-      , gcHeapUnused(0)
-      , gcHeapChunkCleanUnused(0)
-      , gcHeapChunkDirtyUnused(0)
-      , gcHeapChunkCleanDecommitted(0)
-      , gcHeapChunkDirtyDecommitted(0)
-      , gcHeapArenaUnused(0)
+      , gcHeapDecommittedArenas(0)
+      , gcHeapUnusedChunks(0)
+      , gcHeapUnusedArenas(0)
+      , gcHeapUnusedGcThings(0)
       , gcHeapChunkAdmin(0)
-      , totalObjects(0)
-      , totalShapes(0)
-      , totalScripts(0)
-      , totalStrings(0)
-      , totalMjit(0)
-      , totalTypeInference(0)
-      , totalAnalysisTemp(0)
+      , gcHeapGcThings(0)
+      , totals()
       , compartmentStatsVector()
       , currCompartmentStats(NULL)
       , mallocSizeOf(mallocSizeOf)
     {}
 
     RuntimeSizes runtime;
 
+    // If you add a new number, remember to update the constructor!
+
+    // Here's a useful breakdown of the GC heap.
+    //
+    // - rtStats.gcHeapChunkTotal
+    //   - decommitted bytes
+    //     - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
+    //   - unused bytes
+    //     - rtStats.gcHeapUnusedChunks (empty chunks)
+    //     - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
+    //     - rtStats.total.gcHeapUnusedGcThings (empty GC thing slots within non-empty arenas)
+    //   - used bytes
+    //     - rtStats.gcHeapChunkAdmin
+    //     - rtStats.total.gcHeapArenaAdmin
+    //     - rtStats.gcHeapGcThings (in-use GC things)
+    //
+    // It's possible that some arenas in empty chunks may be decommitted, but
+    // we don't count those under rtStats.gcHeapDecommittedArenas because (a)
+    // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
+    // multiple of the chunk size, which is good.
+
     size_t gcHeapChunkTotal;
-    size_t gcHeapCommitted;
-    size_t gcHeapUnused;
-    size_t gcHeapChunkCleanUnused;
-    size_t gcHeapChunkDirtyUnused;
-    size_t gcHeapChunkCleanDecommitted;
-    size_t gcHeapChunkDirtyDecommitted;
-    size_t gcHeapArenaUnused;
+    size_t gcHeapDecommittedArenas;
+    size_t gcHeapUnusedChunks;
+    size_t gcHeapUnusedArenas;
+    size_t gcHeapUnusedGcThings;
     size_t gcHeapChunkAdmin;
-    size_t totalObjects;
-    size_t totalShapes;
-    size_t totalScripts;
-    size_t totalStrings;
-    size_t totalMjit;
-    size_t totalTypeInference;
-    size_t totalAnalysisTemp;
+    size_t gcHeapGcThings;
 
+    // The sum of all compartment's measurements.
+    CompartmentStats totals;
+ 
     js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
     CompartmentStats *currCompartmentStats;
 
     JSMallocSizeOfFun mallocSizeOf;
 
     virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0;
 };
 
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -17,16 +17,44 @@
 #include "jsobjinlines.h"
 
 #ifdef JS_THREADSAFE
 
 namespace JS {
 
 using namespace js;
 
+size_t
+CompartmentStats::gcHeapThingsSize()
+{
+    // These are just the GC-thing measurements.
+    size_t n = 0;
+    n += gcHeapObjectsNonFunction;
+    n += gcHeapObjectsFunction;
+    n += gcHeapStrings;
+    n += gcHeapShapesTree;
+    n += gcHeapShapesDict;
+    n += gcHeapShapesBase;
+    n += gcHeapScripts;
+    n += gcHeapTypeObjects;
+#if JS_HAS_XML_SUPPORT
+    n += gcHeapXML;
+#endif
+
+#ifdef DEBUG
+    size_t n2 = n;
+    n2 += gcHeapArenaAdmin;
+    n2 += gcHeapUnusedGcThings;
+    // These numbers should sum to a multiple of the arena size.
+    JS_ASSERT(n2 % gc::ArenaSize == 0);
+#endif
+
+    return n;
+}
+
 static void
 StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
 {
     // Append a new CompartmentStats to the vector.
     RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
 
     // CollectRuntimeStats reserves enough space.
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
@@ -39,39 +67,39 @@ StatsCompartmentCallback(JSRuntime *rt, 
     cStats.shapesCompartmentTables = compartment->sizeOfShapeTable(rtStats->mallocSizeOf);
     cStats.crossCompartmentWrappers =
         compartment->crossCompartmentWrappers.sizeOfExcludingThis(rtStats->mallocSizeOf);
 }
 
 static void
 StatsChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
 {
-    // Nb: This function is only called for dirty chunks, which is why we
-    // increment gcHeapChunkDirtyDecommitted.
     RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
     for (size_t i = 0; i < gc::ArenasPerChunk; i++)
         if (chunk->decommittedArenas.get(i))
-            rtStats->gcHeapChunkDirtyDecommitted += gc::ArenaSize;
+            rtStats->gcHeapDecommittedArenas += gc::ArenaSize;
 }
 
 static void
 StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
                    JSGCTraceKind traceKind, size_t thingSize)
 {
     RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
 
-    rtStats->currCompartmentStats->gcHeapArenaHeaders += sizeof(gc::ArenaHeader);
+    // The admin space includes (a) the header and (b) the padding between the
+    // end of the header and the start of the first GC thing.
     size_t allocationSpace = arena->thingsSpan(thingSize);
-    rtStats->currCompartmentStats->gcHeapArenaPadding +=
-        gc::ArenaSize - allocationSpace - sizeof(gc::ArenaHeader);
+    rtStats->currCompartmentStats->gcHeapArenaAdmin +=
+        gc::ArenaSize - allocationSpace;
+
     // We don't call the callback on unused things.  So we compute the
     // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
     // We do this by setting arenaUnused to maxArenaUnused here, and then
     // subtracting thingSize for every used cell, in StatsCellCallback().
-    rtStats->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
+    rtStats->currCompartmentStats->gcHeapUnusedGcThings += allocationSpace;
 }
 
 static void
 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
                   size_t thingSize)
 {
     RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
     CompartmentStats *cStats = rtStats->currCompartmentStats;
@@ -147,109 +175,65 @@ StatsCellCallback(JSRuntime *rt, void *d
     case JSTRACE_XML:
     {
         cStats->gcHeapXML += thingSize;
         break;
     }
 #endif
     }
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
-    cStats->gcHeapArenaUnused -= thingSize;
+    cStats->gcHeapUnusedGcThings -= thingSize;
 }
 
 JS_PUBLIC_API(bool)
 CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
 {
     if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length()))
         return false;
 
-    rtStats->gcHeapChunkCleanDecommitted =
-        rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize;
-    rtStats->gcHeapChunkCleanUnused =
-        size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize -
-        rtStats->gcHeapChunkCleanDecommitted;
     rtStats->gcHeapChunkTotal =
         size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
 
+    rtStats->gcHeapUnusedChunks =
+        size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
+
+    // This just computes rtStats->gcHeapDecommittedArenas.
+    IterateChunks(rt, rtStats, StatsChunkCallback);
+
+    // Take the per-compartment measurements.
     IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
                                    StatsArenaCallback, StatsCellCallback);
-    IterateChunks(rt, rtStats, StatsChunkCallback);
 
+    // Take the "explcit/js/runtime/" measurements.
     rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime);
 
-    // This is initialized to all bytes stored in used chunks, and then we
-    // subtract used space from it each time around the loop.
-    rtStats->gcHeapChunkDirtyUnused = rtStats->gcHeapChunkTotal -
-                                      rtStats->gcHeapChunkCleanUnused -
-                                      rtStats->gcHeapChunkCleanDecommitted -
-                                      rtStats->gcHeapChunkDirtyDecommitted;
-
-    rtStats->totalMjit = rtStats->runtime.mjitCode;
-
-    for (size_t index = 0;
-         index < rtStats->compartmentStatsVector.length();
-         index++) {
-        CompartmentStats &cStats = rtStats->compartmentStatsVector[index];
+    rtStats->gcHeapGcThings = 0;
+    for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
+        CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
 
-        size_t used = cStats.gcHeapArenaHeaders +
-                      cStats.gcHeapArenaPadding +
-                      cStats.gcHeapArenaUnused +
-                      cStats.gcHeapObjectsNonFunction +
-                      cStats.gcHeapObjectsFunction +
-                      cStats.gcHeapStrings +
-                      cStats.gcHeapShapesTree +
-                      cStats.gcHeapShapesDict +
-                      cStats.gcHeapShapesBase +
-                      cStats.gcHeapScripts +
-#if JS_HAS_XML_SUPPORT
-                      cStats.gcHeapXML +
-#endif
-                      cStats.gcHeapTypeObjects;
-
-        rtStats->gcHeapChunkDirtyUnused -= used;
-        rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused;
-        rtStats->totalObjects += cStats.gcHeapObjectsNonFunction +
-                                 cStats.gcHeapObjectsFunction +
-                                 cStats.objectSlots +
-                                 cStats.objectElements +
-                                 cStats.objectMisc;
-        rtStats->totalShapes  += cStats.gcHeapShapesTree +
-                                 cStats.gcHeapShapesDict +
-                                 cStats.gcHeapShapesBase +
-                                 cStats.shapesExtraTreeTables +
-                                 cStats.shapesExtraDictTables +
-                                 cStats.shapesCompartmentTables;
-        rtStats->totalScripts += cStats.gcHeapScripts +
-                                 cStats.scriptData;
-        rtStats->totalStrings += cStats.gcHeapStrings +
-                                 cStats.stringChars;
-        rtStats->totalMjit    += cStats.mjitData;
-        rtStats->totalTypeInference += cStats.gcHeapTypeObjects +
-                                       cStats.typeInferenceSizes.objects +
-                                       cStats.typeInferenceSizes.scripts +
-                                       cStats.typeInferenceSizes.tables;
-        rtStats->totalAnalysisTemp  += cStats.typeInferenceSizes.temporary;
+        rtStats->totals.add(cStats);
+        rtStats->gcHeapGcThings += cStats.gcHeapThingsSize();
     }
 
-    size_t numDirtyChunks = (rtStats->gcHeapChunkTotal -
-                             rtStats->gcHeapChunkCleanUnused) /
-                            gc::ChunkSize;
+    size_t numDirtyChunks =
+        (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
     size_t perChunkAdmin =
         sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
     rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
-    rtStats->gcHeapChunkDirtyUnused -= rtStats->gcHeapChunkAdmin;
+    rtStats->gcHeapUnusedArenas -= rtStats->gcHeapChunkAdmin;
 
-    rtStats->gcHeapUnused = rtStats->gcHeapChunkDirtyUnused +
-                            rtStats->gcHeapChunkCleanUnused +
-                            rtStats->gcHeapArenaUnused;
-
-    rtStats->gcHeapCommitted = rtStats->gcHeapChunkTotal -
-                               rtStats->gcHeapChunkCleanDecommitted -
-                               rtStats->gcHeapChunkDirtyDecommitted;
-
+    // |gcHeapUnusedArenas| is the only thing left.  Compute it in terms of
+    // all the others.  See the comment in RuntimeStats for explanation.
+    rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
+                                  rtStats->gcHeapDecommittedArenas -
+                                  rtStats->gcHeapUnusedChunks -
+                                  rtStats->totals.gcHeapUnusedGcThings -
+                                  rtStats->gcHeapChunkAdmin -
+                                  rtStats->totals.gcHeapArenaAdmin -
+                                  rtStats->gcHeapGcThings;
     return true;
 }
 
 JS_PUBLIC_API(int64_t)
 GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf)
 {
     // explicit/<compartment>/gc-heap/*
     size_t n = size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -207,27 +207,27 @@ frontend::CompileScript(JSContext *cx, J
 #if JS_HAS_XML_SUPPORT
     /*
      * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
      * For background, see:
      *
      * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
      */
     if (pn && onlyXML && !callerFrame) {
-        parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
+        parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM);
         return NULL;
     }
 #endif
 
     // It's an error to use |arguments| in a function that has a rest parameter.
     if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) {
         PropertyName *arguments = cx->runtime->atomState.argumentsAtom;
         for (AtomDefnRange r = tc.lexdeps->all(); !r.empty(); r.popFront()) {
             if (r.front().key() == arguments) {
-                parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARGUMENTS_AND_REST);
+                parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
                 return NULL;
             }
         }
         // We're not in a function context, so we don't expect any bindings.
         JS_ASSERT(sc.bindings.lookup(cx, arguments, NULL) == NONE);
     }
 
     /*
@@ -322,17 +322,17 @@ frontend::CompileFunctionBody(JSContext 
      * functions, and generate code for this function, including a stop opcode
      * at the end.
      */
     ParseNode *pn = parser.functionBody(Parser::StatementListBody);
     if (!pn) 
         return false;
 
     if (!parser.tokenStream.matchToken(TOK_EOF)) {
-        parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
+        parser.reportError(NULL, JSMSG_SYNTAX_ERROR);
         return false;
     }
 
     if (!FoldConstants(cx, pn, &parser))
         return false;
 
     if (!AnalyzeFunctions(&parser, nullCallerFrame))
         return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -970,18 +970,17 @@ BytecodeEmitter::noteClosedArg(ParseNode
  */
 static int
 AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
 {
     JS_ASSERT((unsigned) slot < bce->maxStackDepth);
     if (bce->sc->inFunction()) {
         slot += bce->sc->bindings.numVars();
         if ((unsigned) slot >= SLOTNO_LIMIT) {
-            ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR,
-                                     JSMSG_TOO_MANY_LOCALS);
+            bce->reportError(NULL, JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
     return slot;
 }
 
 static bool
 EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
@@ -1174,17 +1173,17 @@ BindNameToSlot(JSContext *cx, BytecodeEm
             return true;
         }
         break;
       default:
         if (pn->isConst()) {
             if (bce->sc->needStrictChecks()) {
                 JSAutoByteString name;
                 if (!js_AtomToPrintableString(cx, atom, &name) ||
-                    !ReportStrictModeError(cx, bce->tokenStream(), pn, JSMSG_READ_ONLY, name.ptr()))
+                    !bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
                 {
                     return false;
                 }
             }
             pn->setOp(op = JSOP_NAME);
         }
     }
 
@@ -1559,16 +1558,47 @@ BytecodeEmitter::needsImplicitThis()
 
     for (StmtInfo *stmt = sc->topStmt; stmt; stmt = stmt->down) {
         if (stmt->type == STMT_WITH)
             return true;
     }
     return false;
 }
 
+bool
+BytecodeEmitter::reportError(ParseNode *pn, unsigned errorNumber, ...)
+{
+    va_list args;
+    va_start(args, errorNumber);
+    bool result = tokenStream()->reportCompileErrorNumberVA(pn, JSREPORT_ERROR, errorNumber, args);
+    va_end(args);
+    return result;
+}
+
+bool
+BytecodeEmitter::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...)
+{
+    va_list args;
+    va_start(args, errorNumber);
+    bool result = tokenStream()->reportCompileErrorNumberVA(pn, JSREPORT_STRICT | JSREPORT_WARNING,
+                                                            errorNumber, args);
+    va_end(args);
+    return result;
+}
+
+bool
+BytecodeEmitter::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
+{
+    va_list args;
+    va_start(args, errorNumber);
+    bool result = tokenStream()->reportStrictModeErrorNumberVA(pn, errorNumber, args);
+    va_end(args);
+    return result;
+}
+
 static JSBool
 EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContext)
 {
     JSOp op;
 
     if (!BindNameToSlot(cx, bce, pn))
         return JS_FALSE;
     op = pn->getOp();
@@ -2859,18 +2889,17 @@ EmitDestructuringOpsHelper(JSContext *cx
                  *   | x | y | to-be-decompiled-value |
                  * before destructuring z. This gives the loop invariant that
                  * the to-be-compiled-value is always on top of the stack.
                  */
                 JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
                 unsigned pickDistance = (unsigned)((bce->stackDepth + 1) - depthBefore);
                 if (pickDistance > 0) {
                     if (pickDistance > UINT8_MAX) {
-                        ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR,
-                                                 JSMSG_TOO_MANY_LOCALS);
+                        bce->reportError(pn3, JSMSG_TOO_MANY_LOCALS);
                         return JS_FALSE;
                     }
                     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0)
                         return false;
                 }
             }
         }
 
@@ -3001,18 +3030,17 @@ EmitGroupAssignment(JSContext *cx, Bytec
                     ParseNode *lhs, ParseNode *rhs)
 {
     unsigned depth, limit, i, nslots;
     ParseNode *pn;
 
     depth = limit = (unsigned) bce->stackDepth;
     for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
         if (limit == JS_BIT(16)) {
-            ReportCompileErrorNumber(cx, bce->tokenStream(), rhs, JSREPORT_ERROR,
-                                     JSMSG_ARRAY_INIT_TOO_BIG);
+            bce->reportError(rhs, JSMSG_ARRAY_INIT_TOO_BIG);
             return JS_FALSE;
         }
 
         /* MaybeEmitGroupAssignment won't call us if rhs is holey. */
         JS_ASSERT(!(pn->isKind(PNK_COMMA) && pn->isArity(PN_NULLARY)));
         if (!EmitTree(cx, bce, pn))
             return JS_FALSE;