Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 26 Jun 2012 15:37:34 -0700
changeset 110060 6f258bdf1b8222fd5477e91b0654320774bb7c58
parent 110059 bd0eba3ea3976721e44e7a41ebdf0cf3e1f23926 (current diff)
parent 100402 be373f4b243feda9d4b2942c690b055611e635f0 (diff)
child 110061 02c16738f77844ddfd925fac6f1d6abbbc5c0840
push id2248
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 19:23:44 +0000
treeherdermozilla-aurora@118a3b748323 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.0a1
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;
         ++limit;
@@ -3496,18 +3524,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
         return false;
     }
 
     /* Finally, emit the specialized assignment bytecode. */
     switch (lhs->getKind()) {
       case PNK_NAME:
         if (lhs->isConst()) {
             if (!rhs) {
-                ReportCompileErrorNumber(cx, bce->tokenStream(), lhs, JSREPORT_ERROR,
-                                         JSMSG_BAD_FOR_LEFTSIDE);
<