Merge mozilla-central into build-system
authorGregory Szorc <gps@mozilla.com>
Wed, 27 Feb 2013 21:02:02 -0800
changeset 133665 1e646e6ddbcd5c3dd2c8233945d14ef1154bd16f
parent 133664 eb634979fdd7ab2ddf37135cacee51d51cdfdd33 (current diff)
parent 133609 8cb9d69819783558ec4fc67c43ad59208682987d (diff)
child 133666 14ab81518d2b3b72ada9d6857a3de773f4f06f6e
push id2452
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 16:59:38 +0000
treeherdermozilla-beta@d4b152d29d8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central into build-system The 3 merged files had no conflicts and were manually verified to ensure no incompatible changes crept in.
browser/base/content/test/Makefile.in
configure.in
media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py
--- a/accessible/tests/mochitest/actions/test_link.html
+++ b/accessible/tests/mochitest/actions/test_link.html
@@ -13,17 +13,17 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../actions.js"></script>
 
   <script type="application/javascript">
     if (navigator.platform.startsWith("Win")) {
-      SimpleTest.expectAssertions(13);
+      SimpleTest.expectAssertions(13, 14);
     } else if (navigator.platform.startsWith("Linux")) {
       SimpleTest.expectAssertions(18);
     } else if (navigator.platform.startsWith("Mac")) {
       SimpleTest.expectAssertions(0, 14);
     }
 
     function getAnchorTargetDocumentAcc()
     {
--- a/accessible/tests/mochitest/states/test_tree.xul
+++ b/accessible/tests/mochitest/states/test_tree.xul
@@ -87,18 +87,22 @@
       gQueue = new eventQueue(EVENT_REORDER);
       gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
       gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
       gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    if (!MAC) {
+      SimpleTest.waitForExplicitFinish();
+      addA11yLoadEvent(doTest);
+    } else {
+      todo(false, "Re-enable on Mac after fixing bug 845095 - intermittent orange");
+    }
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
          title="Reorganize implementation of XUL tree accessibility">
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -106,16 +106,17 @@ endif
                  browser_bug533232.js \
                  browser_bug537474.js \
                  browser_bug550565.js \
                  browser_bug553455.js \
                  browser_bug555224.js \
                  browser_bug555767.js \
                  browser_bug556061.js \
                  browser_bug559991.js \
+                 browser_bug561636.js \
                  browser_bug561623.js \
                  browser_bug562649.js \
                  browser_bug563588.js \
                  browser_bug565575.js \
                  browser_bug567306.js \
                  browser_zbug569342.js \
                  browser_bug575561.js \
                  browser_bug575830.js \
@@ -322,19 +323,12 @@ ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 else
 _BROWSER_FILES += \
 		browser_bug565667.js \
 		$(NULL)
 # TODO: Activate after carbon test plugin lands, bug 628651
 # 		browser_maconly_carbon_mismatch_plugin.js
 endif
 
-# bug 766546, disable browser_bug561636.js on Windows
-ifneq ($(OS_ARCH),WINNT)
-_BROWSER_FILES += \
-                 browser_bug561636.js \
-                 $(NULL)
-endif
-
 include $(topsrcdir)/config/rules.mk
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/base/content/test/browser_bug561636.js
+++ b/browser/base/content/test/browser_bug561636.js
@@ -1,147 +1,159 @@
 var gInvalidFormPopup = document.getElementById('invalid-form-popup');
 ok(gInvalidFormPopup,
    "The browser should have a popup to show when a form is invalid");
 
 function checkPopupShow()
 {
   ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
-     "The invalid form popup should be shown");
+     "[Test " + testId + "] The invalid form popup should be shown");
 }
 
 function checkPopupHide()
 {
   ok(gInvalidFormPopup.state != 'showing' && gInvalidFormPopup.state != 'open',
-     "The invalid form popup should not be shown");
+     "[Test " + testId + "] The invalid form popup should not be shown");
 }
 
 function checkPopupMessage(doc)
 {
   is(gInvalidFormPopup.firstChild.textContent,
      doc.getElementById('i').validationMessage,
-     "The panel should show the message from validationMessage");
+     "[Test " + testId + "] The panel should show the message from validationMessage");
 }
 
 let gObserver = {
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
 
   notifyInvalidSubmit : function (aFormElement, aInvalidElements)
   {
   }
 };
 
+var testId = -1;
+
+function nextTest()
+{
+  testId++;
+  if (testId >= tests.length) {
+    finish();
+    return;
+  }
+  executeSoon(tests[testId]);
+}
+
 function test()
 {
   waitForExplicitFinish();
+  waitForFocus(nextTest);
+}
 
-  test1();
-}
+var tests = [
 
 /**
  * In this test, we check that no popup appears if the form is valid.
  */
-function test1() {
+function()
+{
   let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>";
   let tab = gBrowser.addTab();
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
     let doc = gBrowser.contentDocument;
 
     doc.getElementById('s').click();
 
     executeSoon(function() {
       checkPopupHide();
 
       // Clean-up
       gBrowser.removeTab(gBrowser.selectedTab);
-
-      // Next test
-      executeSoon(test2);
+      nextTest();
     });
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that, when an invalid form is submitted,
  * the invalid element is focused and a popup appears.
  */
-function test2()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
     is(doc.activeElement, doc.getElementById('i'),
        "First invalid element should be focused");
 
     checkPopupShow();
     checkPopupMessage(doc);
 
     // Clean-up and next test.
     gBrowser.removeTab(gBrowser.selectedTab);
-    executeSoon(test3);
+    nextTest();
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that, when an invalid form is submitted,
  * the first invalid element is focused and a popup appears.
  */
-function test3()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
     is(doc.activeElement, doc.getElementById('i'),
        "First invalid element should be focused");
 
     checkPopupShow();
     checkPopupMessage(doc);
 
     // Clean-up and next test.
     gBrowser.removeTab(gBrowser.selectedTab);
-    executeSoon(test4a);
+    nextTest();
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that, we hide the popup by interacting with the
  * invalid element if the element becomes valid.
  */
-function test4a()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -153,35 +165,35 @@ function test4a()
 
     EventUtils.synthesizeKey("a", {});
 
     executeSoon(function () {
       checkPopupHide();
 
       // Clean-up and next test.
       gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(test4b);
+      nextTest();
     });
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that, we don't hide the popup by interacting with the
  * invalid element if the element is still invalid.
  */
-function test4b()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -193,35 +205,35 @@ function test4b()
 
     EventUtils.synthesizeKey("a", {});
 
     executeSoon(function () {
       checkPopupShow();
 
       // Clean-up and next test.
       gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(test5);
+      nextTest();
     });
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that we can hide the popup by blurring the invalid
  * element.
  */
-function test5()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -233,34 +245,34 @@ function test5()
 
     doc.getElementById('i').blur();
 
     executeSoon(function () {
       checkPopupHide();
 
       // Clean-up and next test.
       gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(test6);
+      nextTest();
     });
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that we can hide the popup by pressing TAB.
  */
-function test6()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -272,34 +284,34 @@ function test6()
 
     EventUtils.synthesizeKey("VK_TAB", {});
 
     executeSoon(function () {
       checkPopupHide();
 
       // Clean-up and next test.
       gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(test7);
+      nextTest();
     });
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that the popup will hide if we move to another tab.
  */
-function test7()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -313,76 +325,81 @@ function test7()
     gBrowser.selectedTab  = gBrowser.addTab("about:blank", {skipAnimation: true});
 
     executeSoon(function() {
       checkPopupHide();
 
       // Clean-up and next test.
       gBrowser.removeTab(gBrowser.selectedTab);
       gBrowser.removeTab(gBrowser.selectedTab);
-      executeSoon(test8);
+      nextTest();
     });
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that nothing happen (no focus nor popup) if the
  * invalid form is submitted in another tab than the current focused one
  * (submitted in background).
  */
-function test8()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
-  let tab = gBrowser.addTab();
+  let tab = gBrowser.addTab(uri);
 
   gObserver.notifyInvalidSubmit = function() {
     executeSoon(function() {
       let doc = tab.linkedBrowser.contentDocument;
       isnot(doc.activeElement, doc.getElementById('i'),
             "We should not focus the invalid element when the form is submitted in background");
 
       checkPopupHide();
 
       // Clean-up
       Services.obs.removeObserver(gObserver, "invalidformsubmit");
       gObserver.notifyInvalidSubmit = function () {};
       gBrowser.removeTab(tab);
 
-      // Next test
-      executeSoon(test9);
+      nextTest();
     });
   };
 
   Services.obs.addObserver(gObserver, "invalidformsubmit", false);
 
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-
+  function doClick() {
     isnot(gBrowser.selectedTab, tab,
           "This tab should have been loaded in background");
 
     tab.linkedBrowser.contentDocument.getElementById('s').click();
-  }, true);
+  }
 
-  tab.linkedBrowser.loadURI(uri);
-}
+  if (tab.linkedBrowser.contentDocument.readyState == 'complete') {
+    doClick();
+  } else {
+    tab.linkedBrowser.addEventListener("load", function(aEvent) {
+      tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+      doClick();
+    }, true);
+  }
+},
 
 /**
  * In this test, we check that the author defined error message is shown.
  */
-function test9()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -391,33 +408,33 @@ function test9()
 
     checkPopupShow();
 
     is(gInvalidFormPopup.firstChild.textContent, "foo",
        "The panel should show the author defined error message");
 
     // Clean-up and next test.
     gBrowser.removeTab(gBrowser.selectedTab);
-    executeSoon(test10);
+    nextTest();
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
 
 /**
  * In this test, we check that the message is correctly updated when it changes.
  */
-function test10()
+function()
 {
   let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>";
   let tab = gBrowser.addTab();
 
   gInvalidFormPopup.addEventListener("popupshown", function() {
     gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
 
     let doc = gBrowser.contentDocument;
@@ -429,30 +446,32 @@ function test10()
     is(gInvalidFormPopup.firstChild.textContent, input.validationMessage,
        "The panel should show the current validation message");
 
     input.addEventListener('input', function() {
       input.removeEventListener('input', arguments.callee, false);
 
       executeSoon(function() {
         // Now, the element suffers from another error, the message should have
-	// been updated.
+        // been updated.
         is(gInvalidFormPopup.firstChild.textContent, input.validationMessage,
            "The panel should show the current validation message");
 
         // Clean-up and next test.
         gBrowser.removeTab(gBrowser.selectedTab);
-        executeSoon(finish);
+        nextTest();
       });
     }, false);
 
     EventUtils.synthesizeKey('f', {});
   }, false);
 
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     gBrowser.contentDocument.getElementById('s').click();
   }, true);
 
   gBrowser.selectedTab = tab;
   gBrowser.selectedTab.linkedBrowser.loadURI(uri);
-}
+},
+
+];
--- a/browser/components/places/src/PlacesUIUtils.jsm
+++ b/browser/components/places/src/PlacesUIUtils.jsm
@@ -394,18 +394,17 @@ this.PlacesUIUtils = {
    * By calling this before visiting an URL, the visit will be associated to a
    * TRANSITION_TYPED transition (if there is no a referrer).
    * This is used when visiting pages from the history menu, history sidebar,
    * url bar, url autocomplete results, and history searches from the places
    * organizer.  If this is not called visits will be marked as
    * TRANSITION_LINK.
    */
   markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
-    PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
-               .markPageAsTyped(this.createFixedURI(aURL));
+    PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL));
   },
 
   /**
    * By calling this before visiting an URL, the visit will be associated to a
    * TRANSITION_BOOKMARK transition.
    * This is used when visiting pages from the bookmarks menu,
    * personal toolbar, and bookmarks from within the places organizer.
    * If this is not called visits will be marked as TRANSITION_LINK.
@@ -416,18 +415,17 @@ this.PlacesUIUtils = {
 
   /**
    * By calling this before visiting an URL, any visit in frames will be
    * associated to a TRANSITION_FRAMED_LINK transition.
    * This is actually used to distinguish user-initiated visits in frames
    * so automatic visits can be correctly ignored.
    */
   markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
-    PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
-               .markPageAsFollowedLink(this.createFixedURI(aURL));
+    PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL));
   },
 
   /**
    * Allows opening of javascript/data URI only if the given node is
    * bookmarked (see bug 224521).
    * @param aURINode
    *        a URI node
    * @param aWindow
--- a/browser/metro/base/content/BrowserTouchHandler.js
+++ b/browser/metro/base/content/BrowserTouchHandler.js
@@ -22,17 +22,17 @@ const BrowserTouchHandler = {
   onContentContextMenu: function onContentContextMenu(aMessage) {
     // Note, target here is the target of the message manager message,
     // usually the browser.
     let contextInfo = { name: aMessage.name,
                         json: aMessage.json,
                         target: aMessage.target };
     // Touch input selection handling
     if (!InputSourceHelper.isPrecise) {
-      if (SelectionHelperUI.isActive()) {
+      if (SelectionHelperUI.isActive) {
         // Selection handler is active.
         if (aMessage.json.types.indexOf("selected-text") != -1) {
           // long tap on existing selection. The incoming message has the
           // string data, so reset the selection handler and invoke the
           // context menu.
           SelectionHelperUI.closeEditSession();
         } else {
           // Weird, context menu request with no selected text and
--- a/browser/metro/base/content/ContextCommands.js
+++ b/browser/metro/base/content/ContextCommands.js
@@ -28,21 +28,16 @@ var ContextCommands = {
   copy: function cc_copy() {
     let target = ContextMenuUI.popupState.target;
     if (target.localName == "browser") {
       // content
       if (ContextMenuUI.popupState.string != "undefined") {
         this.clipboard.copyString(ContextMenuUI.popupState.string,
                                   this.docRef);
         this.showToast(Strings.browser.GetStringFromName("selectionHelper.textCopied"));
-      } else {
-        let x = ContextMenuUI.popupState.x;
-        let y = ContextMenuUI.popupState.y;
-        let json = {x: x, y: y, command: "copy" };
-        target.messageManager.sendAsyncMessage("Browser:ContextCommand", json);
       }
     } else {
       // chrome
       target.editor.copy();
       this.showToast(Strings.browser.GetStringFromName("selectionHelper.textCopied"));
     }
 
     if (target)
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -492,16 +492,17 @@
           toString: function() {
             return "[View Local]";
           }
         })
         ]]>
       </field>
 
       <!-- Change client coordinates in device pixels to page-relative ones in CSS px. -->
+      <!-- (Does not take into account sub frame scroll) -->
       <method name="transformClientToBrowser">
         <parameter name="clientX"/>
         <parameter name="clientY"/>
         <body>
           <![CDATA[
             let bcr = this.getBoundingClientRect();
             let scroll = this.getRootView().getPosition();
             return { x: (clientX + scroll.x - bcr.left) / this.scale,
--- a/browser/metro/base/content/bindings/selectionoverlay.xml
+++ b/browser/metro/base/content/bindings/selectionoverlay.xml
@@ -14,21 +14,23 @@
           <xul:toolbarbutton id="selectionhandle-end" label="^" left="100" top="10" hidden="false"/>
         </xul:stack>
       </html:div>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor>
         <![CDATA[
+          this._selectionOverlay.addEventListener('contextmenu', this);
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
+          this._selectionOverlay.removeEventListener('contextmenu', this);
         ]]>
       </destructor>
 
       <field name="_selectionOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-inner").parentNode;</field>
       <field name="_selectionDebugOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-debug");</field>
 
       <property name="enabled">
         <setter>
@@ -82,16 +84,41 @@
       <method name="shutdown">
         <body>
           <![CDATA[
           this.enabled = false;
           ]]>
         </body>
       </method>
 
+      <method name="_onContextMenu">
+        <parameter name="aEvent"/>
+        <body>
+          <![CDATA[
+            // forward this over. frame script will treat this like
+            // a bubbling contextmenu event.
+            Browser.selectedTab.browser.messageManager.sendAsyncMessage("Browser:InvokeContextAtPoint", {
+             xPos: aEvent.clientX, yPos: aEvent.clientY });
+          ]]>
+        </body>
+      </method>
+
+      <method name="handleEvent">
+        <parameter name="aEvent"/>
+        <body>
+          <![CDATA[
+            switch (aEvent.type) {
+              case 'contextmenu':
+                this._onContextMenu(aEvent);
+                break;
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="addDebugRect">
         <parameter name="aLeft"/>
         <parameter name="aTop"/>
         <parameter name="aRight"/>
         <parameter name="aBottom"/>
         <parameter name="aColor"/>
         <parameter name="aFill"/>
         <parameter name="aId"/>
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -41,17 +41,18 @@ let Elements = {};
   ["tray",               "tray"],
   ["toolbar",            "toolbar"],
   ["browsers",           "browsers"],
   ["appbar",             "appbar"],
   ["contentViewport",    "content-viewport"],
   ["progress",           "progress-control"],
   ["contentNavigator",   "content-navigator"],
   ["aboutFlyout",        "about-flyoutpanel"],
-  ["prefsFlyout",        "prefs-flyoutpanel"]
+  ["prefsFlyout",        "prefs-flyoutpanel"],
+  ["syncFlyout",         "sync-flyoutpanel"]
 ].forEach(function (aElementGlobal) {
   let [name, id] = aElementGlobal;
   XPCOMUtils.defineLazyGetter(Elements, name, function() {
     return document.getElementById(id);
   });
 });
 
 /**
@@ -1301,16 +1302,28 @@ var StartUI = {
         let event = document.createEvent("Events");
         event.initEvent("MozEdgeUIGesture", true, false);
         window.dispatchEvent(event);
         break;
     }
   }
 };
 
+var SyncPanelUI = {
+  init: function() {
+    // Run some setup code the first time the panel is shown.
+    Elements.syncFlyout.addEventListener("PopupChanged", function onShow(aEvent) {
+      if (aEvent.detail && aEvent.popup === Elements.syncFlyout) {
+        Elements.syncFlyout.removeEventListener("PopupChanged", onShow, false);
+        WeaveGlue.init();
+      }
+    }, false);
+  }
+};
+
 var FlyoutPanelsUI = {
   get _aboutVersionLabel() {
     return document.getElementById('about-version-label');
   },
 
   _initAboutPanel: function() {
     // Include the build ID if this is an "a#" (nightly or aurora) build
     let version = Services.appinfo.version;
@@ -1320,21 +1333,23 @@ var FlyoutPanelsUI = {
                       "-" + buildID.slice(6,8);
       this._aboutVersionLabel.textContent +=" (" + buildDate + ")";
     }
   },
 
   init: function() {
     this._initAboutPanel();
     PreferencesPanelView.init();
+    SyncPanelUI.init();
   },
 
   hide: function() {
     Elements.aboutFlyout.hide();
     Elements.prefsFlyout.hide();
+    Elements.syncFlyout.hide();
   }
 };
 
 var PanelUI = {
   get _panels() { return document.getElementById("panel-items"); },
   get _switcher() { return document.getElementById("panel-view-switcher"); },
 
   get isVisible() {
@@ -1604,16 +1619,21 @@ var SettingsCharm = {
   init: function SettingsCharm_init() {
     Services.obs.addObserver(this, "metro-settings-entry-selected", false);
 
     // Options
     this.addEntry({
         label: Strings.browser.GetStringFromName("optionsCharm"),
         onselected: function() Elements.prefsFlyout.show()
     });
+    // Sync 
+    this.addEntry({
+        label: Strings.browser.GetStringFromName("syncCharm"),
+        onselected: function() Elements.syncFlyout.show()
+    });
     // About
     this.addEntry({
         label: Strings.browser.GetStringFromName("aboutCharm1"),
         onselected: function() Elements.aboutFlyout.show()
     });
     // Help
     this.addEntry({
         label: Strings.browser.GetStringFromName("helpOnlineCharm"),
--- a/browser/metro/base/content/browser.js
+++ b/browser/metro/base/content/browser.js
@@ -962,17 +962,17 @@ var Browser = {
           index: data.index
         };
         browser.messageManager.sendAsyncMessage("WebNavigation:LoadURI", json);
         break;
       }
 
       case "Browser:TapOnSelection":
         if (!InputSourceHelper.isPrecise) {
-          if (SelectionHelperUI.isActive()) {
+          if (SelectionHelperUI.isActive) {
             SelectionHelperUI.shutdown();
           }
           if (SelectionHelperUI.canHandle(aMessage)) {
             SelectionHelperUI.openEditSession(aMessage);
           }
         }
         break;
     }
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -423,16 +423,32 @@
         <label id="about-product-label" value="&aboutHeader.product.label;"/>
         <label value="&aboutHeader.company.label;"/>
 #expand <label id="about-version-label">__MOZ_APP_VERSION__</label>
         <label id="about-policy-label"
                onclick="if (event.button == 0) { Browser.onAboutPolicyClick(); }"
                class="text-link" value="&aboutHeader.policy.label;"/>
     </flyoutpanel>
 
+    <flyoutpanel id="sync-flyoutpanel" headertext="&syncHeader.title;">
+      <setting id="sync-connect" title="&sync.notconnected;" type="control">
+        <button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
+      </setting>
+      <setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
+        <button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
+      </setting>
+      <setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
+        <button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();"/>
+      </setting>
+      <setting id="sync-device" class="setting-subgroup" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this);" collapsed="true"/>
+      <setting id="sync-disconnect" class="setting-subgroup" type="control" collapsed="true">
+        <button label="&sync.disconnect;" oncommand="WeaveGlue.disconnect();" />
+      </setting>
+    </flyoutpanel>
+
     <flyoutpanel id="prefs-flyoutpanel" headertext="&optionsHeader.title;">
       <settings id="prefs-startup" label="&optionsHeader.startup.title;"> <!-- note, this element has a custom margin-top -->
         <setting id="prefs-homepage" title="&optionsHeader.homepage.title;" type="menulist" pref="browser.startup.sessionRestore" class="setting-expanded">
           <menulist id="prefs-homepage-options">
             <menupopup id="prefs-homepage-popup" position="after_end">
               <menuitem id="prefs-homepage-default" label="&optionsHeader.homepage.startPage.button;" value="false"/>
               <menuitem id="prefs-homepage-session" label="&optionsHeader.homepage.sessionRestore.button;" value="true"/>
             </menupopup>
@@ -449,33 +465,16 @@
         </setting>
       </settings>
       <setting pref="signon.rememberSignons" title="&optionsHeader.privacy.passwords.label;" type="bool"/>
       <settings id="prefs-donottrack" label="&optionsHeader.privacy.doNotTrack.title;">
         <setting pref="privacy.donottrackheader.enabled" title="&optionsHeader.privacy.doNotTrack.label;" type="bool"/>
       </settings>
 
       <setting id="prefs-master-password" title="&optionsHeader.privacy.masterPassword.label;" type="bool" oncommand="MasterPasswordUI.show(this.value);"/>
-
-      <settings id="prefs-sync" label="&sync.title;">
-        <setting id="sync-connect" title="&sync.notconnected;" type="control">
-          <button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
-        </setting>
-        <setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
-          <button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
-          <button id="sync-details" label="&sync.details;" type="checkbox" autocheck="false" checked="false" oncommand="WeaveGlue.showDetails();" />
-        </setting>
-        <setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
-          <button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();"/>
-        </setting>
-        <setting id="sync-device" class="setting-subgroup" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this);" collapsed="true"/>
-        <setting id="sync-disconnect" class="setting-subgroup" type="control" collapsed="true">
-          <button label="&sync.disconnect;" oncommand="WeaveGlue.disconnect();" />
-        </setting>
-      </settings>
     </flyoutpanel>
 
     <!-- Form Helper form validation helper popup -->
     <arrowbox id="form-helper-validation-container" class="arrowbox-dark" flex="1" hidden="true" offset="0" top="0" left="0">
       <label/>
     </arrowbox>
 
 #ifdef MOZ_SERVICES_SYNC
@@ -677,17 +676,18 @@
       <image id="alerts-image"/>
       <vbox flex="1">
         <label id="alerts-title" value=""/>
         <description id="alerts-text" flex="1"/>
       </vbox>
     </hbox>
 
     <!-- Selection overlay - this should be below any content that can have selectable text -->
-    <box class="selection-overlay-hidden" id="selection-overlay"/>
+    <!-- onclick addresses dom bug 835175, str in bug 832957 -->
+    <box onclick="false" class="selection-overlay-hidden" id="selection-overlay"/>
   </stack>
 
   <svg:svg height="0">
     <svg:clipPath id="forward-button-clip-path" clipPathUnits="objectBoundingBox">
       <svg:path d="M 0,0 C 0.15,0.12 0.25,0.3 0.25,0.5 0.25,0.7 0.15,0.88 0,1 L 1,1 1,0 0,0 z"/>
     </svg:clipPath>
     <svg:clipPath id="back-button-clip-path" clipPathUnits="userSpaceOnUse">
       <svg:path d="m -1,-5 0,4.03 C 3.6,1.8 18,21.4 0,40 l 0,27 10000,0 0,-55 L 0,-5 z" />
--- a/browser/metro/base/content/contenthandlers/Content.js
+++ b/browser/metro/base/content/contenthandlers/Content.js
@@ -162,42 +162,48 @@ const ElementTouchHelper = {
 };
 
 
 /*
  * Global functions
  */
 
 /*
- * elementFromPoint
+ * elementFromPoint - find the closes element at a point. searches
+ * sub-frames.
  *
- * @param x,y Browser coordinates
- * @return Element at position, null if no active browser or no element found
+ * @param aX, aY browser coordinates
+ * @return
+ *  element - element at the position, or null if no active browser or
+ *            element was found.
+ *  frameX - x position within the subframe element was found. aX if no
+ *           sub-frame was found.
+ *  frameY - y position within the subframe element was found. aY if no
+ *           sub-frame was found.
  */
-function elementFromPoint(x, y) {
+function elementFromPoint(aX, aY) {
   // browser's elementFromPoint expect browser-relative client coordinates.
   // subtract browser's scroll values to adjust
   let cwu = Util.getWindowUtils(content);
-  let elem = ElementTouchHelper.getClosest(cwu, x, y);
+  let elem = ElementTouchHelper.getClosest(cwu, aX, aY);
 
   // step through layers of IFRAMEs and FRAMES to find innermost element
   while (elem && (elem instanceof HTMLIFrameElement ||
                   elem instanceof HTMLFrameElement)) {
     // adjust client coordinates' origin to be top left of iframe viewport
     let rect = elem.getBoundingClientRect();
-    x -= rect.left;
-    y -= rect.top;
+    aX -= rect.left;
+    aY -= rect.top;
     let windowUtils = elem.contentDocument
                           .defaultView
                           .QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
-    elem = ElementTouchHelper.getClosest(windowUtils, x, y);
+    elem = ElementTouchHelper.getClosest(windowUtils, aX, aY);
   }
-
-  return elem;
+  return { element: elem, frameX: aX, frameY: aY };
 }
 
 /*
  * getBoundingContentRect
  *
  * @param aElement
  * @return Bounding content rect adjusted for scroll and frame offsets.
  */
@@ -395,34 +401,34 @@ let Content = {
   /******************************************************
    * generic input handlers
    *
    * regardless of whether the input was received via
    * message manager or sent directly via dispatch.
    */
 
   _genericMouseDown: function _genericMouseDown(x, y) {
-    let element = elementFromPoint(x, y);
+    let { element } = elementFromPoint(x, y);
     if (!element)
       return;
 
     // There is no need to have a feedback for disabled element
     let isDisabled = element instanceof HTMLOptionElement ?
       (element.disabled || element.parentNode.disabled) : element.disabled;
     if (isDisabled)
       return;
 
     // Set the target element to active
     this._doTapHighlight(element);
   },
 
   _genericMouseClick: function _genericMouseClick(aEvent) {
     ContextMenuHandler.reset();
 
-    let element = elementFromPoint(aEvent.clientX, aEvent.clientY);
+    let { element: element } = elementFromPoint(aEvent.clientX, aEvent.clientY);
     if (!element)
       return;
 
     // Only show autocomplete after the item is clicked
     if (!this.lastClickElement || this.lastClickElement != element) {
       this.lastClickElement = element;
       if (aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE &&
           !(element instanceof HTMLSelectElement)) {
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -86,18 +86,19 @@ var ContextMenuHandler = {
   },
 
   /*
    * Handler for selection overlay context menu events.
    */
   _onContextAtPoint: function _onContextCommand(aMessage) {
     // we need to find popupNode as if the context menu were
     // invoked on underlying content.
-    let elem = elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
-    this._processPopupNode(elem, aMessage.json.xPos, aMessage.json.yPos,
+    let { element, frameX, frameY } =
+      elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
+    this._processPopupNode(element, frameX, frameY,
                            Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
   },
 
   /******************************************************
    * Event handlers
    */
 
   reset: function ch_reset() {
@@ -166,30 +167,39 @@ var ContextMenuHandler = {
     while (element &&
            element.ownerDocument &&
            element.ownerDocument.defaultView != content) {
       element = element.ownerDocument.defaultView.frameElement;
       let rect = element.getBoundingClientRect();
       offsetX += rect.left;
       offsetY += rect.top;
     }
-    return { offsetX: offsetX, offsetY: offsetY };
+    let win = null;
+    if (element == aPopupNode)
+      win = content;
+    else
+      win = element.contentDocument.defaultView;
+    return { targetWindow: win, offsetX: offsetX, offsetY: offsetY };
   },
 
   /*
    * _processPopupNode - Generate and send a Content:ContextMenu message
    * to browser detailing the underlying content types at this.popupNode.
    * Note the event we receive targets the sub frame (if there is one) of
    * the page.
    */
   _processPopupNode: function _processPopupNode(aPopupNode, aX, aY, aInputSrc) {
     if (!aPopupNode)
       return;
-    let { offsetX: offsetX, offsetY: offsetY } =
+
+    let { targetWindow: targetWindow,
+          offsetX: offsetX,
+          offsetY: offsetY } =
       this._translateToTopLevelWindow(aPopupNode);
+
     let popupNode = this.popupNode = aPopupNode;
     let imageUrl = "";
 
     let state = {
       types: [],
       label: "",
       linkURL: "",
       linkTitle: "",
@@ -293,19 +303,19 @@ var ContextMenuHandler = {
 
       elem = elem.parentNode;
     }
 
     // Over arching text tests
     if (isText) {
       // If this is text and has a selection, we want to bring
       // up the copy option on the context menu.
-      if (content && content.getSelection() &&
-          content.getSelection().toString().length > 0) {
-        state.string = content.getSelection().toString();
+      let selection = targetWindow.getSelection();
+      if (selection && selection.toString().length > 0) {
+        state.string = targetWindow.getSelection().toString();
         state.types.push("copy");
         state.types.push("selected-text");
       } else {
         // Add general content text if this isn't anything specific
         if (state.types.indexOf("image") == -1 &&
             state.types.indexOf("media") == -1 &&
             state.types.indexOf("video") == -1 &&
             state.types.indexOf("link") == -1 &&
--- a/browser/metro/base/content/contenthandlers/SelectionHandler.js
+++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js
@@ -42,21 +42,21 @@ dump("### SelectionHandler.js loaded\n")
 
 var SelectionHandler = {
   _debugEvents: false,
   _cache: {},
   _targetElement: null,
   _targetIsEditable: false,
   _contentWindow: null,
   _contentOffset: { x:0, y:0 },
-  _frameOffset: { x:0, y:0 },
   _domWinUtils: null,
   _selectionMoveActive: false,
   _lastMarker: "",
   _debugOptions: { dumpRanges: false, displayRanges: false },
+  _snap: true,
 
   init: function init() {
     addMessageListener("Browser:SelectionStart", this);
     addMessageListener("Browser:SelectionAttach", this);
     addMessageListener("Browser:SelectionEnd", this);
     addMessageListener("Browser:SelectionMoveStart", this);
     addMessageListener("Browser:SelectionMove", this);
     addMessageListener("Browser:SelectionMoveEnd", this);
@@ -80,16 +80,26 @@ var SelectionHandler = {
     removeMessageListener("Browser:SelectionCopy", this);
     removeMessageListener("Browser:SelectionDebug", this);
   },
 
   isActive: function isActive() {
     return (this._contentWindow != null);
   },
 
+  /*
+   * snap - enable or disable word snap for the active marker when a
+   * SelectionMoveEnd event is received. Typically you would disable
+   * snap when zoom is < 1.0 for precision selection.
+   */
+
+  get snap() {
+    return this._snap;
+  },
+
   /*************************************************
    * Browser event handlers
    */
 
   /*
    * Selection start event handler
    */
   _onSelectionStart: function _onSelectionStart(aX, aY) {
@@ -98,21 +108,20 @@ var SelectionHandler = {
       this._onFail("failed to get frame offset");
       return;
     }
 
     // Clear any existing selection from the document
     let selection = this._contentWindow.getSelection();
     selection.removeAllRanges();
 
-    Util.dumpLn(this._targetElement);
-
     // Set our initial selection, aX and aY should be in client coordinates.
-    if (!this._domWinUtils.selectAtPoint(aX, aY, Ci.nsIDOMWindowUtils
-                                                   .SELECT_WORDNOSPACE)) {
+    let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
+    if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
+                                         Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
       this._onFail("failed to set selection at point");
       return;
     }
 
     // Update the position of our selection monocles
     this._updateSelectionUI(true, true);
   },
 
@@ -164,18 +173,17 @@ var SelectionHandler = {
 
     // Update selection in the doc
     let pos = null;
     if (aMsg.change == "start") {
       pos = aMsg.start;
     } else {
       pos = aMsg.end;
     }
-
-    this._handleSelectionPoint(aMsg.change, pos);
+    this._handleSelectionPoint(aMsg.change, pos, false);
   },
 
   /*
    * Selection monocle move finished event handler
    */
   _onSelectionMoveEnd: function _onSelectionMoveComplete(aMsg) {
     if (!this._contentWindow) {
       this._onFail("_onSelectionMove was called without proper view set up");
@@ -190,17 +198,17 @@ var SelectionHandler = {
     // Update selection in the doc
     let pos = null;
     if (aMsg.change == "start") {
       pos = aMsg.start;
     } else {
       pos = aMsg.end;
     }
 
-    this._handleSelectionPoint(aMsg.change, pos);
+    this._handleSelectionPoint(aMsg.change, pos, true);
     this._selectionMoveActive = false;
     
     // _handleSelectionPoint may set a scroll timer, so this must
     // be reset after the last call.
     this.clearTimers();
 
     // Update the position of our selection monocles
     this._updateSelectionUI(true, true);
@@ -210,25 +218,26 @@ var SelectionHandler = {
    * Selection copy event handler
    *
    * Check to see if the incoming click was on our selection rect.
    * if it was, copy to the clipboard. Incoming coordinates are
    * content values.
    */
   _onSelectionCopy: function _onSelectionCopy(aMsg) {
     let tap = {
-      xPos: aMsg.xPos, // + this._contentOffset.x,
-      yPos: aMsg.yPos, // + this._contentOffset.y,
+      xPos: aMsg.xPos,
+      yPos: aMsg.yPos,
     };
 
     let tapInSelection = (tap.xPos > this._cache.rect.left &&
                           tap.xPos < this._cache.rect.right) &&
                          (tap.yPos > this._cache.rect.top &&
                           tap.yPos < this._cache.rect.bottom);
-    // Util.dumpLn(tap.xPos, tap.yPos, "|", this._cache.rect.left,
+    // Util.dumpLn(tapInSelection,
+    //             tap.xPos, tap.yPos, "|", this._cache.rect.left,
     //             this._cache.rect.right, this._cache.rect.top,
     //             this._cache.rect.bottom);
     let success = false;
     let selectedText = this._getSelectedText();
     if (tapInSelection && selectedText.length) {
       let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
                         .getService(Ci.nsIClipboardHelper);
       clipboard.copyString(selectedText, this._contentWindow.document);
@@ -324,17 +333,17 @@ var SelectionHandler = {
   /*
    * Informs SelectionHelperUI of the current selection start and end position
    * so that our selection monocles can be positioned properly.
    */
   _updateSelectionUI: function _updateSelectionUI(aUpdateStart, aUpdateEnd) {
     let selection = this._getSelection();
 
     // If the range didn't have any text, let's bail
-    if (!selection.toString().trim().length) {
+    if (!selection || !selection.toString().trim().length) {
       this._onFail("no text was present in the current selection");
       return;
     }
 
     // Updates this._cache content selection position data which we send over
     // to SelectionHelperUI.
     this._updateUIMarkerRects(selection);
 
@@ -349,26 +358,24 @@ var SelectionHandler = {
    * Find content within frames - cache the target nsIDOMWindow,
    * client coordinate offset, target element, and dom utils interface.
    */
   _initTargetInfo: function _initTargetInfo(aX, aY) {
     // getCurrentWindowAndOffset takes client coordinates
     let { element: element,
           contentWindow: contentWindow,
           offset: offset,
-          frameOffset: frameOffset,
           utils: utils } =
       this.getCurrentWindowAndOffset(aX, aY);
     if (!contentWindow) {
       return false;
     }
     this._targetElement = element;
     this._contentWindow = contentWindow;
     this._contentOffset = offset;
-    this._frameOffset = frameOffset;
     this._domWinUtils = utils;
     this._targetIsEditable = false;
     if (this._isTextInput(this._targetElement)) {
       this._targetIsEditable = true;
       // Since we have an overlay, focus will not get set, so set it. There
       // are ways around this if this causes trouble - we have the selection
       // controller, so we can turn selection display on manually. (Selection
       // display is setup on edits when focus changes.) I think web pages will
@@ -382,22 +389,17 @@ var SelectionHandler = {
   /*
    * _updateUIMarkerRects(aSelection)
    *
    * Extracts the rects of the current selection, clips them to any text
    * input bounds, and stores them in the cache table we send over to
    * SelectionHelperUI.
    */
   _updateUIMarkerRects: function _updateUIMarkerRects(aSelection) {
-    // Extract the information we'll send over to the ui - cache holds content
-    // coordinate oriented start and end position data. Note the coordinates
-    // of the range passed in are relative the sub frame the range sits in.
-    // SelectionHelperUI calls transformBrowserToClient to get client coords.
-    this._cache = this._extractContentRectFromRange(aSelection.getRangeAt(0),
-                                                    this._contentOffset);
+    this._cache = this._extractClientRectFromRange(aSelection.getRangeAt(0));
     if (this. _debugOptions.dumpRanges)  {
        Util.dumpLn("start:", "(" + this._cache.start.xPos + "," +
                    this._cache.start.yPos + ")");
        Util.dumpLn("end:", "(" + this._cache.end.xPos + "," +
                    this._cache.end.yPos + ")");
     }
     this._restrictSelectionRectToEditBounds();
   },
@@ -405,17 +407,17 @@ var SelectionHandler = {
   /*
    * Selection bounds will fall outside the bound of a control if the control
    * can scroll. Clip UI cache data to the bounds of the target so monocles
    * don't draw outside the control.
    */
   _restrictSelectionRectToEditBounds: function _restrictSelectionRectToEditBounds() {
     if (!this._targetIsEditable)
       return;
-    let bounds = this._getTargetContentRect();
+    let bounds = this._getTargetClientRect();
     if (this._cache.start.xPos < bounds.left)
       this._cache.start.xPos = bounds.left;
     if (this._cache.end.xPos < bounds.left)
       this._cache.end.xPos = bounds.left;
     if (this._cache.start.xPos > bounds.right)
       this._cache.start.xPos = bounds.right;
     if (this._cache.end.xPos > bounds.right)
       this._cache.end.xPos = bounds.right;
@@ -426,35 +428,40 @@ var SelectionHandler = {
       this._cache.end.yPos = bounds.top;
     if (this._cache.start.yPos > bounds.bottom)
       this._cache.start.yPos = bounds.bottom;
     if (this._cache.end.yPos > bounds.bottom)
       this._cache.end.yPos = bounds.bottom;
   },
 
   /*
-   * _handleSelectionPoint(aMarker, aPoint) 
+   * _handleSelectionPoint(aMarker, aPoint, aEndOfSelection) 
    *
-   * After a monocle moves to a new point in the document, determintes
+   * After a monocle moves to a new point in the document, determines
    * what the target is and acts on its selection accordingly. If the
    * monocle is within the bounds of the target, adds or subtracts selection
    * at the monocle coordinates appropriately and then merges selection ranges
    * into a single continuous selection. If the monocle is outside the bounds
    * of the target and the underlying target is editable, uses the selection
    * controller to advance selection and visibility within the control.
    */
-  _handleSelectionPoint: function _handleSelectionPoint(aMarker, aClientPoint) {
+  _handleSelectionPoint: function _handleSelectionPoint(aMarker, aClientPoint,
+                                                        aEndOfSelection) {
     let selection = this._getSelection();
 
     let clientPoint = { xPos: aClientPoint.xPos, yPos: aClientPoint.yPos };
 
-    if (selection.rangeCount == 0 || selection.rangeCount > 1) {
-      Util.dumpLn("warning, unexpected selection state.");
+    if (selection.rangeCount == 0) {
+      this._onFail("selection.rangeCount == 0");
+      return;
+    }
+
+    // We expect continuous selection ranges.
+    if (selection.rangeCount > 1) {
       this._setContinuousSelection();
-      return;
     }
 
     // Adjust our y position up such that we are sending coordinates on
     // the text line vs. below it where the monocle is positioned. This
     // applies to free floating text areas. For text inputs we'll constrain
     // coordinates further below.
     let halfLineHeight = this._queryHalfLineHeight(aMarker, selection);
     clientPoint.yPos -= halfLineHeight;
@@ -484,32 +491,29 @@ var SelectionHandler = {
         if (!this._scrollTimer)
           this._scrollTimer = new Util.Timeout();
         this._setTextEditUpdateInterval(result.speed);
 
         // Smooth the selection
         this._setContinuousSelection();
 
         // Update the other monocle's position if we've dragged off to one side
-        if (result.start)
-          this._updateSelectionUI(true, false);
-        if (result.end)
-          this._updateSelectionUI(false, true);
+        this._updateSelectionUI(result.start, result.end);
 
         return;
       }
     }
 
     this._lastMarker = aMarker;
 
     // If we aren't out-of-bounds, clear the scroll timer if it exists.
     this.clearTimers();
 
     // Adjusts the selection based on monocle movement
-    this._adjustSelection(aMarker, clientPoint);
+    this._adjustSelection(aMarker, clientPoint, aEndOfSelection);
 
     // Update the other monocle's position. We do this because the dragging
     // monocle may reset the static monocle to a new position if the dragging
     // monocle drags ahead or behind the other.
     if (aMarker == "start") {
       this._updateSelectionUI(false, true);
     } else {
       this._updateSelectionUI(true, false);
@@ -522,36 +526,45 @@ var SelectionHandler = {
 
   /*
    * Based on a monocle marker and position, adds or subtracts from the
    * existing selection.
    *
    * @param the marker currently being manipulated
    * @param aClientPoint the point designating the new start or end
    * position for the selection.
+   * @param aEndOfSelection indicates if this is the end of a selection
+   * move, in which case we may want to snap to the end of a word or
+   * sentence.
    */
-  _adjustSelection: function _adjustSelection(aMarker, aClientPoint) {
+  _adjustSelection: function _adjustSelection(aMarker, aClientPoint,
+                                              aEndOfSelection) {
     // Make a copy of the existing range, we may need to reset it.
     this._backupRangeList();
 
     // shrinkSelectionFromPoint takes sub-frame relative coordinates.
     let framePoint = this._clientPointToFramePoint(aClientPoint);
 
     // Tests to see if the user is trying to shrink the selection, and if so
     // collapses it down to the appropriate side such that our calls below
     // will reset the selection to the proper range.
-    this._shrinkSelectionFromPoint(aMarker, framePoint);
+    let shrunk = this._shrinkSelectionFromPoint(aMarker, framePoint);
 
     let selectResult = false;
     try {
+      // If we're at the end of a selection (touchend) snap to the word.
+      let type = ((aEndOfSelection && this._snap) ?
+        Ci.nsIDOMWindowUtils.SELECT_WORD :
+        Ci.nsIDOMWindowUtils.SELECT_CHARACTER);
+
       // Select a character at the point.
       selectResult = 
-        this._domWinUtils.selectAtPoint(aClientPoint.xPos,
-                                        aClientPoint.yPos,
-                                        Ci.nsIDOMWindowUtils.SELECT_CHARACTER);
+        this._domWinUtils.selectAtPoint(framePoint.xPos,
+                                        framePoint.yPos,
+                                        type);
     } catch (ex) {
     }
 
     // If selectAtPoint failed (which can happen if there's nothing to select)
     // reset our range back before we shrunk it.
     if (!selectResult) {
       this._restoreRangeList();
     }
@@ -755,17 +768,17 @@ var SelectionHandler = {
       aClientPoint.y = (clientRect.bottom - aHalfLineHeight);
       this._setDebugPoint(aClientPoint, "red");
     }
   },
 
   /*
    * _setContinuousSelection()
    *
-   * Smoothes a selection with multiple ranges into a single
+   * Smooths a selection with multiple ranges into a single
    * continuous range.
    */
   _setContinuousSelection: function _setContinuousSelection() {
     let selection = this._getSelection();
     try {
       if (selection.rangeCount > 1) {
         let startRange = selection.getRangeAt(0);
         if (this. _debugOptions.displayRanges) {
@@ -793,18 +806,18 @@ var SelectionHandler = {
               break;
           }
           switch (startRange.compareBoundaryPoints(Ci.nsIDOMRange.END_TO_END, range)) {
             case -1: // startRange is before
               newEndNode = range.endContainer;
               newEndOffset = range.endOffset;
               break;
             case 0: // startRange is equal
-              newEndNode = startNode.endContainer;
-              newEndOffset = startNode.endOffset;
+              newEndNode = range.endContainer;
+              newEndOffset = range.endOffset;
               break;
             case 1: // startRange is after
               newEndNode = startRange.endContainer;
               newEndOffset = startRange.endOffset;
               break;
           }
           if (this. _debugOptions.displayRanges) {
             let clientRect = range.getBoundingClientRect();
@@ -827,54 +840,61 @@ var SelectionHandler = {
   /*
    * _shrinkSelectionFromPoint(aMarker, aFramePoint)
    *
    * Tests to see if aFramePoint intersects the current selection and if so,
    * collapses selection down to the opposite start or end point leaving a
    * character of selection at the collapse point.
    *
    * @param aMarker the marker that is being relocated. ("start" or "end")
-   * @param aFramePoint position of the marker. Should be relative to the
-   * inner frame so that it matches selection range coordinates.
+   * @param aFramePoint position of the marker.
    */
   _shrinkSelectionFromPoint: function _shrinkSelectionFromPoint(aMarker, aFramePoint) {
+    let result = false;
     try {
       let selection = this._getSelection();
-      let rects = selection.getRangeAt(0).getClientRects();
-      for (let idx = 0; idx < rects.length; idx++) {
-        if (Util.pointWithinDOMRect(aFramePoint.xPos, aFramePoint.yPos, rects[idx])) {
-          if (aMarker == "start") {
-            selection.collapseToEnd();
-          } else {
-            selection.collapseToStart();
+      for (let range = 0; range < selection.rangeCount; range++) {
+        // relative to frame
+        let rects = selection.getRangeAt(range).getClientRects();
+        for (let idx = 0; idx < rects.length; idx++) {
+          // Util.dumpLn("[" + idx + "]", aFramePoint.xPos, aFramePoint.yPos, rects[idx].left,
+          // rects[idx].top, rects[idx].right, rects[idx].bottom);
+          if (Util.pointWithinDOMRect(aFramePoint.xPos, aFramePoint.yPos, rects[idx])) {
+            result = true;
+            if (aMarker == "start") {
+              selection.collapseToEnd();
+            } else {
+              selection.collapseToStart();
+            }
+            // collapseToStart and collapseToEnd leave an empty range in the
+            // selection at the collapse point. Therefore we need to add some
+            // selection such that the selection added by selectAtPoint and
+            // the current empty range will get merged properly when we smooth
+            // the selection ranges out.
+            let selCtrl = this._getSelectController();
+            // Expand the collapsed range such that it occupies a little space.
+            if (aMarker == "start") {
+              // State: focus = anchor (collapseToEnd does this)
+              selCtrl.characterMove(false, true);
+              // State: focus = (anchor - 1)
+              selection.collapseToStart();
+              // State: focus = anchor and both are -1 from the original offset
+              selCtrl.characterMove(true, true);
+              // State: focus = anchor + 1, both have been moved back one char
+            } else {
+              selCtrl.characterMove(true, true);
+            }
+            break;
           }
-          // collapseToStart and collapseToEnd leave an empty range in the
-          // selection at the collapse point. Therefore we need to add some
-          // selection such that the selection added by selectAtPoint and
-          // the current empty range will get merged properly when we smooth
-          // the selection rnages out.
-          let selCtrl = this._getSelectController();
-          // Expand the collapsed range such that it occupies a little space.
-          if (aMarker == "start") {
-            // State: focus = anchor (collapseToEnd does this)
-            selCtrl.characterMove(false, true);
-            // State: focus = (anchor - 1)
-            selection.collapseToStart();
-            // State: focus = anchor and both are -1 from the original offset
-            selCtrl.characterMove(true, true);
-            // State: focus = anchor + 1, both have been moved back one char
-          } else {
-            selCtrl.characterMove(true, true);
-          }
-          break;
         }
       }
     } catch (ex) {
       Util.dumpLn("error shrinking selection:", ex.message);
     }
+    return result;
   },
 
   /*
    * Scroll + selection advancement timer when the monocle is
    * outside the bounds of an input control.
    */
   scrollTimerCallback: function scrollTimerCallback() {
     let result = SelectionHandler.updateTextEditSelection();
@@ -949,121 +969,102 @@ var SelectionHandler = {
    * Utilities
    */
 
   /*
    * Returns data on the position of a selection using the relative
    * coordinates in a range extracted from any sub frames. If aRange
    * is in the root frame offset should be zero. 
    */
-  _extractContentRectFromRange: function _extractContentRectFromRange(aRange, aOffset) {
+  _extractClientRectFromRange: function _extractClientRectFromRange(aRange) {
     let cache = {
       start: {}, end: {},
       rect: { left: 0, top: 0, right: 0, bottom: 0 }
     };
 
     // When in an iframe, aRange coordinates are relative to the frame origin.
     let rects = aRange.getClientRects();
 
     let startSet = false;
     for (let idx = 0; idx < rects.length; idx++) {
       if (this. _debugOptions.dumpRanges) Util.dumpDOMRect(idx, rects[idx]);
       if (!startSet && !Util.isEmptyDOMRect(rects[idx])) {
-        cache.start.xPos = rects[idx].left + aOffset.x;
-        cache.start.yPos = rects[idx].bottom + aOffset.y;
+        cache.start.xPos = rects[idx].left + this._contentOffset.x;
+        cache.start.yPos = rects[idx].bottom + this._contentOffset.y;
         startSet = true;
         if (this. _debugOptions.dumpRanges) Util.dumpLn("start set");
       }
       if (!Util.isEmptyDOMRect(rects[idx])) {
-        cache.end.xPos = rects[idx].right + aOffset.x;
-        cache.end.yPos = rects[idx].bottom + aOffset.y;
+        cache.end.xPos = rects[idx].right + this._contentOffset.x;
+        cache.end.yPos = rects[idx].bottom + this._contentOffset.y;
         if (this. _debugOptions.dumpRanges) Util.dumpLn("end set");
       }
     }
 
     let r = aRange.getBoundingClientRect();
-    cache.rect.left = r.left + aOffset.x;
-    cache.rect.top = r.top + aOffset.y;
-    cache.rect.right = r.right + aOffset.x;
-    cache.rect.bottom = r.bottom + aOffset.y;
+    cache.rect.left = r.left + this._contentOffset.x;
+    cache.rect.top = r.top + this._contentOffset.y;
+    cache.rect.right = r.right + this._contentOffset.x;
+    cache.rect.bottom = r.bottom + this._contentOffset.y;
 
     if (!rects.length) {
       Util.dumpLn("no rects in selection range. unexpected.");
     }
 
     return cache;
   },
 
-  _getTargetContentRect: function _getTargetContentRect() {
-    let client = this._targetElement.getBoundingClientRect();
-    let rect = {};
-    rect.left = client.left + this._contentOffset.x;
-    rect.top = client.top + this._contentOffset.y;
-    rect.right = client.right + this._contentOffset.x;
-    rect.bottom = client.bottom + this._contentOffset.y;
-
-    return rect;
-  },
-
   _getTargetClientRect: function _getTargetClientRect() {
     return this._targetElement.getBoundingClientRect();
   },
 
    /*
     * Translate a top level client point to frame relative client point.
     */
   _clientPointToFramePoint: function _clientPointToFramePoint(aClientPoint) {
     let point = {
-      xPos: aClientPoint.xPos - this._frameOffset.x,
-      yPos: aClientPoint.yPos - this._frameOffset.y
+      xPos: aClientPoint.xPos - this._contentOffset.x,
+      yPos: aClientPoint.yPos - this._contentOffset.y
     };
     return point;
   },
 
   /*
    * Retrieve the total offset from the window's origin to the sub frame
    * element including frame and scroll offsets. The resulting offset is
    * such that:
    * sub frame coords + offset = root frame position
    */
   getCurrentWindowAndOffset: function(x, y) {
-    // elementFromPoint: If the element at the given point belongs to another
-    // document (such as an iframe's subdocument), the element in the calling
-    // document's DOM (e.g. the iframe) is returned.
+    // If the element at the given point belongs to another document (such
+    // as an iframe's subdocument), the element in the calling document's
+    // DOM (e.g. the iframe) is returned.
     let utils = Util.getWindowUtils(content);
     let element = utils.elementFromPoint(x, y, true, false);
-
     let offset = { x:0, y:0 };
-    let frameOffset = { x:0, y:0 };
-    let scrollOffset = ContentScroll.getScrollOffset(content);
-    offset.x += scrollOffset.x;
-    offset.y += scrollOffset.y;
 
     while (element && (element instanceof HTMLIFrameElement ||
                        element instanceof HTMLFrameElement)) {
-      //Util.dumpLn("found child frame:", element.contentDocument.location);
-
-      // Get the content scroll offset in the child frame
-      scrollOffset = ContentScroll.getScrollOffset(element.contentDocument.defaultView);
       // get the child frame position in client coordinates
       let rect = element.getBoundingClientRect();
 
-      // subtract frame offset from our elementFromPoint coordinates
-      x -= rect.left;
-      // subtract frame and scroll offset and from elementFromPoint coordinates
+      // calculate offsets for digging down into sub frames
+      // using elementFromPoint:
+
+      // Get the content scroll offset in the child frame
+      scrollOffset = ContentScroll.getScrollOffset(element.contentDocument.defaultView);
+      // subtract frame and scroll offset from our elementFromPoint coordinates
+      x -= rect.left + scrollOffset.x;
       y -= rect.top + scrollOffset.y;
 
+      // calculate offsets we'll use to translate to client coords:
+
       // add frame client offset to our total offset result
       offset.x += rect.left;
-      // add the frame's y offset + scroll offset to our total offset result
-      offset.y += rect.top + scrollOffset.y;
-
-      // Track the offset to the origin of the sub-frame as well
-      frameOffset.x += rect.left;
-      frameOffset.y += rect.top
+      offset.y += rect.top;
 
       // get the frame's nsIDOMWindowUtils
       utils = element.contentDocument
                      .defaultView
                      .QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils);
 
       // retrieve the target element in the sub frame at x, y
@@ -1072,17 +1073,16 @@ var SelectionHandler = {
 
     if (!element)
       return {};
 
     return {
       element: element,
       contentWindow: element.ownerDocument.defaultView,
       offset: offset,
-      frameOffset: frameOffset,
       utils: utils
     };
   },
 
   _isTextInput: function _isTextInput(aElement) {
     return ((aElement instanceof Ci.nsIDOMHTMLInputElement &&
              aElement.mozIsTextField(false)) ||
             aElement instanceof Ci.nsIDOMHTMLTextAreaElement);
@@ -1093,26 +1093,29 @@ var SelectionHandler = {
       return null;
     return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIWebNavigation)
                   .QueryInterface(Ci.nsIDocShell);
   },
 
   _getSelectedText: function _getSelectedText() {
     let selection = this._getSelection();
-    return selection.toString();
+    if (selection)
+      return selection.toString();
+    return "";
   },
 
   _getSelection: function _getSelection() {
     if (this._targetElement instanceof Ci.nsIDOMNSEditableElement)
       return this._targetElement
                  .QueryInterface(Ci.nsIDOMNSEditableElement)
                  .editor.selection;
-    else
+    else if (this._contentWindow)
       return this._contentWindow.getSelection();
+    return null;
   },
 
   _getSelectController: function _getSelectController() {
     if (this._targetElement instanceof Ci.nsIDOMNSEditableElement) {
       return this._targetElement
                  .QueryInterface(Ci.nsIDOMNSEditableElement)
                  .editor.selectionController;
     } else {
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -66,17 +66,25 @@ MarkerDragger.prototype = {
       return false;
     this.marker.moveBy(aDx, aDy, aClientX, aClientY);
     // return true if we moved, false otherwise. The result
     // is used in deciding if we should repaint between drags.
     return true;
   }
 }
 
-function Marker() {}
+function Marker(aParent, aTag, aElementId, xPos, yPos) {
+  this._xPos = xPos;
+  this._yPos = yPos;
+  this._selectionHelperUI = aParent;
+  this._element = document.getElementById(aElementId);
+  // These get picked in input.js and receives drag input
+  this._element.customDragger = new MarkerDragger(this);
+  this.tag = aTag;
+}
 
 Marker.prototype = {
   _element: null,
   _selectionHelperUI: null,
   _xPos: 0,
   _yPos: 0,
   _tag: "",
   _hPlane: 0,
@@ -102,23 +110,16 @@ Marker.prototype = {
   set tag(aVal) {
     this._tag = aVal;
   },
 
   get dragging() {
     return this._element.customDragger.dragging;
   },
 
-  init: function init(aParent, aElementId, xPos, yPos) {
-    this._selectionHelperUI = aParent;
-    this._element = document.getElementById(aElementId);
-    // These get picked in input.js and receives drag input
-    this._element.customDragger = new MarkerDragger(this);
-  },
-
   shutdown: function shutdown() {
     this._element.hidden = true;
     this._element.customDragger.shutdown = true;
     delete this._element.customDragger;
     this._selectionHelperUI = null;
     this._element = null;
   },
 
@@ -191,31 +192,28 @@ Marker.prototype = {
 var SelectionHelperUI = {
   _debugEvents: false,
   _popupState: null,
   _startMark: null,
   _endMark: null,
   _target: null,
   _movement: { active: false, x:0, y: 0 },
   _activeSelectionRect: null,
+  _selectionHandlerActive: false,
 
   get startMark() {
     if (this._startMark == null) {
-      this._startMark = new Marker();
-      this._startMark.tag = "start";
-      this._startMark.init(this, "selectionhandle-start");
+      this._startMark = new Marker(this, "start", "selectionhandle-start", 0, 0);
     }
     return this._startMark;
   },
 
   get endMark() {
     if (this._endMark == null) {
-      this._endMark = new Marker();
-      this._endMark.tag = "end";
-      this._endMark.init(this, "selectionhandle-end");
+      this._endMark = new Marker(this, "end", "selectionhandle-end", 0, 0);
     }
     return this._endMark;
   },
 
   get overlay() {
     return document.getElementById("selection-overlay");
   },
 
@@ -241,51 +239,53 @@ var SelectionHelperUI = {
      *   yPos: aEvent.y
      */
 
     this._popupState = aMessage.json;
     this._popupState._target = aMessage.target;
 
     this._init();
 
-    Util.dumpLn("enableSelection target:", this._popupState._target);
-
     // Set the track bounds for each marker NIY
     this.startMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
     this.endMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
 
     // Send this over to SelectionHandler in content, they'll message us
-    // back with information on the current selection.
-    this._popupState._target.messageManager.sendAsyncMessage(
-      "Browser:SelectionStart",
-      { xPos: this._popupState.xPos,
-        yPos: this._popupState.yPos });
+    // back with information on the current selection. SelectionStart
+    // takes client coordinates.
+    this._selectionHandlerActive = false;
+    this._sendAsyncMessage("Browser:SelectionStart", {
+      xPos: this._popupState.xPos,
+      yPos: this._popupState.yPos
+    });
 
     this._setupDebugOptions();
   },
 
   /*
    * attachEditSession
    * 
    * Attaches to existing selection and begins editing.
    */
   attachEditSession: function attachEditSession(aMessage) {
+    if (aMessage.target == undefined)
+      return;
     this._popupState = aMessage.json;
     this._popupState._target = aMessage.target;
 
     this._init();
 
-    Util.dumpLn("enableSelection target:", this._popupState._target);
-
     // Set the track bounds for each marker NIY
     this.startMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
     this.endMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
 
     // Send this over to SelectionHandler in content, they'll message us
-    // back with information on the current selection.
+    // back with information on the current selection. SelectionAttach
+    // takes client coordinates.
+    this._selectionHandlerActive = false;
     this._popupState._target.messageManager.sendAsyncMessage(
       "Browser:SelectionAttach",
       { xPos: this._popupState.xPos,
         yPos: this._popupState.yPos });
 
     this._setupDebugOptions();
   },
 
@@ -296,45 +296,45 @@ var SelectionHelperUI = {
    */
   canHandle: function canHandle(aMessage) {
     if (aMessage.json.types.indexOf("content-text") != -1)
       return true;
     return false;
   },
 
   /*
-   * isActive
+   * isActive (prop)
    *
    * Determines if an edit session is currently active.
    */
-  isActive: function isActive() {
-    return (this._popupState && this._popupState._target);
+  get isActive() {
+    return (this._popupState != null &&
+            this._popupState._target != null &&
+            this._selectionHandlerActive);
   },
 
   /*
    * closeEditSession
    *
    * Closes an active edit session and shuts down. Does not clear existing
    * selection regions if they exist.
    */
   closeEditSession: function closeEditSession() {
-    if (this._popupState._target)
-      this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionClose");
+    this._sendAsyncMessage("Browser:SelectionClose");
     this._shutdown();
   },
 
   /*
    * closeEditSessionAndClear
    * 
    * Closes an active edit session and shuts down. Clears any selection region
    * associated with the edit session.
    */
   closeEditSessionAndClear: function closeEditSessionAndClear() {
-    if (this._popupState._target)
-      this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionClear");
+    this._sendAsyncMessage("Browser:SelectionClear");
     this.closeEditSession();
   },
 
   /*
    * Internal
    */
 
   _init: function _init() {
@@ -342,23 +342,26 @@ var SelectionHelperUI = {
     messageManager.addMessageListener("Content:SelectionRange", this);
     messageManager.addMessageListener("Content:SelectionCopied", this);
     messageManager.addMessageListener("Content:SelectionFail", this);
     messageManager.addMessageListener("Content:SelectionDebugRect", this);
 
     // selection related events
     window.addEventListener("click", this, true);
     window.addEventListener("dblclick", this, true);
-    window.addEventListener("MozPressTapGesture", this, true);
 
     // Picking up scroll attempts
     window.addEventListener("touchstart", this, true);
     window.addEventListener("touchend", this, true);
     window.addEventListener("touchmove", this, true);
 
+    // context ui display events
+    window.addEventListener("MozContextUIShow", this, true);
+    window.addEventListener("MozContextUIDismiss", this, true);
+
     // cancellation related events
     window.addEventListener("keypress", this, true);
     Elements.browsers.addEventListener("URLChanged", this, true);
     Elements.browsers.addEventListener("SizeChanged", this, true);
     Elements.browsers.addEventListener("ZoomChanged", this, true);
 
     window.addEventListener("MozPrecisePointer", this, true);
 
@@ -368,22 +371,24 @@ var SelectionHelperUI = {
   _shutdown: function _shutdown() {
     messageManager.removeMessageListener("Content:SelectionRange", this);
     messageManager.removeMessageListener("Content:SelectionCopied", this);
     messageManager.removeMessageListener("Content:SelectionFail", this);
     messageManager.removeMessageListener("Content:SelectionDebugRect", this);
 
     window.removeEventListener("click", this, true);
     window.removeEventListener("dblclick", this, true);
-    window.removeEventListener("MozPressTapGesture", this, true);
 
     window.removeEventListener("touchstart", this, true);
     window.removeEventListener("touchend", this, true);
     window.removeEventListener("touchmove", this, true);
 
+    window.removeEventListener("MozContextUIShow", this, true);
+    window.removeEventListener("MozContextUIDismiss", this, true);
+
     window.removeEventListener("keypress", this, true);
     Elements.browsers.removeEventListener("URLChanged", this, true);
     Elements.browsers.removeEventListener("SizeChanged", this, true);
     Elements.browsers.removeEventListener("ZoomChanged", this, true);
 
     window.removeEventListener("MozPrecisePointer", this, true);
 
     if (this.startMark != null)
@@ -391,16 +396,17 @@ var SelectionHelperUI = {
     if (this.endMark != null)
       this.endMark.shutdown();
 
     delete this._startMark;
     delete this._endMark;
 
     this._popupState = null;
     this._activeSelectionRect = null;
+    this._selectionHandlerActive = false;
 
     this.overlay.displayDebugLayer = false;
     this.overlay.enabled = false;
   },
 
   /*
    * _setupDebugOptions
    *
@@ -424,128 +430,120 @@ var SelectionHelperUI = {
         this._debugEvents = true;
       }
     } catch (ex) {}
 
     if (debugOpts.displayRanges || debugOpts.dumpRanges || debugOpts.dumpEvents) {
       // Turn on the debug layer
       this.overlay.displayDebugLayer = true;
       // Tell SelectionHandler what to do
-      this._popupState
-          ._target
-          .messageManager
-          .sendAsyncMessage("Browser:SelectionDebug", debugOpts);
+      this._sendAsyncMessage("Browser:SelectionDebug", debugOpts);
     }
   },
 
   /*
-   * Event handlers for document events
+   * _sendAsyncMessage - helper for sending a message to
+   * SelectionHandler.
    */
+  _sendAsyncMessage: function _sendAsyncMessage(aMsg, aJson) {
+    if (!this._popupState || !this._popupState._target) {
+      if (this._debugEvents)
+        Util.dumpLn("SelectionHelperUI sendAsyncMessage could not send", aMsg);
+      return;
+    }
+    this._popupState._target.messageManager.sendAsyncMessage(aMsg, aJson);
+  },
 
   _checkForActiveDrag: function _checkForActiveDrag() {
     return (this.startMark.dragging || this.endMark.dragging);
   },
 
   _hitTestSelection: function _hitTestSelection(aEvent) {
-    let clickPos =
-      this._popupState._target.transformClientToBrowser(aEvent.clientX,
-                                                        aEvent.clientY);
     // Ignore if the double tap isn't on our active selection rect.
     if (this._activeSelectionRect &&
-        Util.pointWithinRect(clickPos.x, clickPos.y, this._activeSelectionRect)) {
+        Util.pointWithinRect(aEvent.clientX, aEvent.clientY, this._activeSelectionRect)) {
       return true;
     }
     return false;
   },
 
+  /*
+   * Event handlers for document events
+   */
+
   _onTap: function _onTap(aEvent) {
     // Trap single clicks which if forwarded to content will clear selection.
     aEvent.stopPropagation();
     aEvent.preventDefault();
-
     if (this.startMark.hitTest(aEvent.clientX, aEvent.clientY) ||
         this.endMark.hitTest(aEvent.clientX, aEvent.clientY)) {
       // NIY
-      this._popupState._target.messageManager.sendAsyncMessage("Browser:ChangeMode", {});
-    }
-  },
-
-  _onLongTap: function _onLongTap(aEvent) {
-    // Check to see if this tap is on one of our monocles. Tap is
-    // easy to trigger when dragging them around.
-    if (this.startMark.hitTest(aEvent.clientX, aEvent.clientY) ||
-        this.endMark.hitTest(aEvent.clientX, aEvent.clientY)) {
-      aEvent.stopPropagation();
-      aEvent.preventDefault();
-      return;
+      // this._sendAsyncMessage("Browser:ChangeMode", {});
     }
   },
 
   /*
    * Checks to see if the tap event was on our selection rect.
    * If it is, we select the underlying text and shutdown.
    */
   _onDblTap: function _onDblTap(aEvent) {
     if (!this._hitTestSelection(aEvent)) {
       // Clear and close
       this.closeEditSessionAndClear();
       return;
     }
 
     // Select and close    
-    let clickPos =
-      this._popupState._target.transformClientToBrowser(aEvent.clientX,
-                                                        aEvent.clientY);
-    let json = {
-      xPos: clickPos.x,
-      yPos: clickPos.y,
-    };
-
-    this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionCopy", json);
+    this._sendAsyncMessage("Browser:SelectionCopy", {
+      xPos: aEvent.clientX,
+      yPos: aEvent.clientY,
+    });
 
     aEvent.stopPropagation();
     aEvent.preventDefault();
   },
 
   _onSelectionCopied: function _onSelectionCopied(json) {
     if (json.succeeded) {
       this.showToast(Strings.browser.GetStringFromName("selectionHelper.textCopied"));
     }
     this.closeEditSessionAndClear();
   },
 
   _onSelectionRangeChange: function _onSelectionRangeChange(json) {
+    // start and end contain client coordinates.
     if (json.updateStart) {
-      let pos =
-        this._popupState._target.transformBrowserToClient(json.start.xPos, json.start.yPos);
-      this.startMark.position(pos.x, pos.y);
+      this.startMark.position(json.start.xPos, json.start.yPos);
       this.startMark.show();
     }
     if (json.updateEnd) {
-      let pos =
-        this._popupState._target.transformBrowserToClient(json.end.xPos, json.end.yPos);
-      this.endMark.position(pos.x, pos.y);
+      this.endMark.position(json.end.xPos, json.end.yPos);
       this.endMark.show();
     }
     this._activeSelectionRect = json.rect;
   },
 
   _onSelectionFail: function _onSelectionFail() {
     Util.dumpLn("failed to get a selection.");
     this.closeEditSession();
   },
 
   _onKeypress: function _onKeypress() {
     this.closeEditSession();
   },
 
   _onResize: function _onResize() {
-    this._popupState._target
-                    .messageManager
-                    .sendAsyncMessage("Browser:SelectionUpdate", {});
+    this._sendAsyncMessage("Browser:SelectionUpdate", {});
+  },
+
+  _onContextUIVisibilityEvent: function _onContextUIVisibilityEvent(aType) {
+    // Manage display of monocles when the context ui is displayed.
+    if (!this.isActive)
+      return;
+    this.overlay.hidden = (aType == "MozContextUIShow");
   },
 
   _onDebugRectRequest: function _onDebugRectRequest(aMsg) {
     this.overlay.addDebugRect(aMsg.left, aMsg.top, aMsg.right, aMsg.bottom,
                               aMsg.color, aMsg.fill, aMsg.id);
   },
 
   /*
@@ -556,32 +554,28 @@ var SelectionHelperUI = {
     event.initNSMouseEvent(aType, true, true, content, 0,
                            aSrcEvent.screenX, aSrcEvent.screenY, aSrcEvent.clientX, aSrcEvent.clientY,
                            false, false, false, false,
                            aSrcEvent.button, aSrcEvent.relatedTarget, 1.0,
                            Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
   },
 
   handleEvent: function handleEvent(aEvent) {
-    if (this._debugEvents && aEvent.type != "touchmove") {
+    if (this._debugEvents) {
       Util.dumpLn("SelectionHelperUI:", aEvent.type);
     }
     switch (aEvent.type) {
       case "click":
         this._onTap(aEvent);
         break;
 
       case "dblclick":
         this._onDblTap(aEvent);
         break;
 
-      case "MozPressTapGesture":
-        this._onLongTap(aEvent);
-        break;
-
       case "touchstart": {
         if (aEvent.touches.length != 1)
           break;
         let touch = aEvent.touches[0];
         this._movement.x = this._movement.y = 0;
         this._movement.x = touch.clientX;
         this._movement.y = touch.clientY;
         this._movement.active = true;
@@ -614,31 +608,39 @@ var SelectionHelperUI = {
       case "SizeChanged":
         this._onResize(aEvent);
       break;
 
       case "ZoomChanged":
       case "URLChanged":
       case "MozPrecisePointer":
         this.closeEditSessionAndClear();
-        break;
+      break;
+
+      case "MozContextUIShow":
+      case "MozContextUIDismiss":
+        this._onContextUIVisibilityEvent(aEvent.type);
+      break;
     }
   },
 
   receiveMessage: function sh_receiveMessage(aMessage) {
     if (this._debugEvents) Util.dumpLn("SelectionHelperUI:", aMessage.name);
     let json = aMessage.json;
     switch (aMessage.name) {
       case "Content:SelectionFail":
+        this._selectionHandlerActive = false;
         this._onSelectionFail();
         break;
       case "Content:SelectionRange":
+        this._selectionHandlerActive = true;
         this._onSelectionRangeChange(json);
         break;
       case "Content:SelectionCopied":
+        this._selectionHandlerActive = true;
         this._onSelectionCopied(json);
         break;
       case "Content:SelectionDebugRect":
         this._onDebugRectRequest(json);
         break;
     }
   },
 
@@ -665,30 +667,30 @@ var SelectionHelperUI = {
       start: { xPos: this.startMark.xPos, yPos: this.startMark.yPos },
       end: { xPos: this.endMark.xPos, yPos: this.endMark.yPos },
     };
   },
 
   markerDragStart: function markerDragStart(aMarker) {
     let json = this._getMarkerBaseMessage();
     json.change = aMarker.tag;
-    this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMoveStart", json);
+    this._sendAsyncMessage("Browser:SelectionMoveStart", json);
   },
 
   markerDragStop: function markerDragStop(aMarker) {
     //aMarker.show();
     let json = this._getMarkerBaseMessage();
     json.change = aMarker.tag;
-    this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMoveEnd", json);
+    this._sendAsyncMessage("Browser:SelectionMoveEnd", json);
   },
 
   markerDragMove: function markerDragMove(aMarker) {
     let json = this._getMarkerBaseMessage();
     json.change = aMarker.tag;
-    this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMove", json);
+    this._sendAsyncMessage("Browser:SelectionMove", json);
   },
 
   showToast: function showToast(aString) {
     let toaster =
       Cc["@mozilla.org/toaster-alerts-service;1"]
         .getService(Ci.nsIAlertsService);
     toaster.showAlertNotification(null, aString, "", false, "", null);
   },
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -29,18 +29,16 @@ const kMaxVelocity = 6;
 
 // A debug pref that when set makes us treat all precise pointer input
 // as imprecise touch input. For debugging purposes only. Note there are
 // subtle event sequencing differences in this feature when running on
 // the desktop using the win32 widget backend and the winrt widget backend
 // in metro. Fixing something in this mode does not insure the bug is
 // in metro.
 const kDebugMouseInputPref = "metro.debug.treatmouseastouch";
-// Colorizes the touch input overlay so you know when it is active.
-const kDebugMouseLayerPref = "metro.debug.colorizeInputOverlay";
 // Display rects around selection ranges. Useful in debugging
 // selection problems.
 const kDebugSelectionDisplayPref = "metro.debug.selection.displayRanges";
 // Dump range rect data to the console. Very useful, but also slows
 // things down a lot.
 const kDebugSelectionDumpPref = "metro.debug.selection.dumpRanges";
 // Dump message manager event traffic for selection.
 const kDebugSelectionDumpEvents = "metro.debug.selection.dumpEvents";
@@ -89,16 +87,17 @@ var TouchModule = {
     this._targetScrollbox = null;
     this._targetScrollInterface = null;
 
     this._kinetic = new KineticController(this._dragBy.bind(this),
                                           this._kineticStop.bind(this));
 
     // capture phase events
     window.addEventListener("CancelTouchSequence", this, true);
+    window.addEventListener("dblclick", this, true);
 
     // bubble phase
     window.addEventListener("contextmenu", this, false);
     window.addEventListener("touchstart", this, false);
     window.addEventListener("touchmove", this, false);
     window.addEventListener("touchend", this, false);
 
     try {
@@ -137,16 +136,28 @@ var TouchModule = {
             this._onTouchStart(aEvent);
             break;
           case "touchmove":
             this._onTouchMove(aEvent);
             break;
           case "touchend":
             this._onTouchEnd(aEvent);
             break;
+          case "dblclick":
+            // XXX This will get picked up somewhere below us for "double tap to zoom"
+            // once we get omtc and the apzc. Currently though dblclick is delivered to
+            // content and triggers selection of text, so fire up the SelectionHelperUI
+            // once selection is present.
+            setTimeout(function () {
+              let contextInfo = { name: "",
+                                  json: { xPos: aEvent.clientX, yPos: aEvent.clientY },
+                                  target: Browser.selectedTab.browser };
+              SelectionHelperUI.attachEditSession(contextInfo);
+            }, 50);
+            break;
         }
       }
     }
   },
 
   sample: function sample(aTimeStamp) {
     this._waitingForPaint = false;
   },
--- a/browser/metro/base/content/preferences.js
+++ b/browser/metro/base/content/preferences.js
@@ -5,13 +5,12 @@
 
 var PreferencesPanelView = {
   init: function pv_init() {
     // Run some setup code the first time the panel is shown.
     Elements.prefsFlyout.addEventListener("PopupChanged", function onShow(aEvent) {
       if (aEvent.detail && aEvent.popup === Elements.prefsFlyout) {
         Elements.prefsFlyout.removeEventListener("PopupChanged", onShow, false);
         MasterPasswordUI.updatePreference();
-        WeaveGlue.init();
       }
     }, false);
   }
 };
--- a/browser/metro/base/content/sync.js
+++ b/browser/metro/base/content/sync.js
@@ -280,25 +280,16 @@ let WeaveGlue = {
     let account = this._elements.account.value;
     let password = this._elements.password.value;
     let synckey = this._elements.synckey.value;
 
     let disabled = !(account && password && synckey);
     document.getElementById("syncsetup-button-connect").disabled = disabled;
   },
 
-  showDetails: function showDetails() {
-    // Show the connect UI detail settings
-    let show = this._elements.sync.collapsed;
-    this._elements.details.checked = show;
-    this._elements.sync.collapsed = !show;
-    this._elements.device.collapsed = !show;
-    this._elements.disconnect.collapsed = !show;
-  },
-
   tryConnect: function login() {
     // If Sync is not configured, simply show the setup dialog
     if (this._loginError || Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED) {
       this.open();
       return;
     }
 
     // No setup data, do nothing
@@ -404,17 +395,17 @@ let WeaveGlue = {
 
     // Get all the setting nodes from the add-ons display
     let elements = {};
     let setupids = ["account", "password", "synckey", "usecustomserver", "customserver"];
     setupids.forEach(function(id) {
       elements[id] = document.getElementById("syncsetup-" + id);
     });
 
-    let settingids = ["device", "connect", "connected", "disconnect", "sync", "details", "pairdevice"];
+    let settingids = ["device", "connect", "connected", "disconnect", "sync", "pairdevice"];
     settingids.forEach(function(id) {
       elements[id] = document.getElementById("sync-" + id);
     });
 
     // Replace the getter with the collection of settings
     delete this._elements;
     return this._elements = elements;
   },
@@ -431,17 +422,16 @@ let WeaveGlue = {
 
     // Can't do anything before settings are loaded
     if (this._elements == null)
       return;
 
     // Make some aliases
     let connect = this._elements.connect;
     let connected = this._elements.connected;
-    let details = this._elements.details;
     let device = this._elements.device;
     let disconnect = this._elements.disconnect;
     let sync = this._elements.sync;
     let pairdevice = this._elements.pairdevice;
 
     // Show what went wrong with login if necessary
     if (aTopic == "weave:ui:login:error") {
       this._loginError = true;
@@ -460,22 +450,22 @@ let WeaveGlue = {
     let isConfigured = (!this._loginError && Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED);
 
     connect.collapsed = isConfigured;
     connected.collapsed = !isConfigured;
 
     if (!isConfigured) {
       connect.setAttribute("title", this._bundle.GetStringFromName("notconnected.label"));
       connect.firstChild.disabled = false;
-      details.checked = false;
-      sync.collapsed = true;
-      device.collapsed = true;
-      disconnect.collapsed = true;
     }
 
+    sync.collapsed = !isConfigured;
+    device.collapsed = !isConfigured;
+    disconnect.collapsed = !isConfigured;
+
     // Check the lock on a timeout because it's set just after notifying
     setTimeout(function(self) {
       // Prevent certain actions when the service is locked
       if (Weave.Service.locked) {
         connect.firstChild.disabled = true;
         sync.firstChild.disabled = true;
 
         if (aTopic == "weave:service:login:start")
--- a/browser/metro/locales/en-US/chrome/browser.properties
+++ b/browser/metro/locales/en-US/chrome/browser.properties
@@ -11,16 +11,17 @@ browser.search.order.2=Google
 browser.search.order.3=Yahoo
 
 # l10n: search context menu item text will be: |Search (browser.search.defaultenginename) for ".."
 browser.search.contextTextSearchLabel=Search %S for ".."
 
 # Settings Charms
 aboutCharm1=About
 optionsCharm=Options
+syncCharm=Sync
 helpOnlineCharm=Help (online)
 
 # General
 browserForSaveLocation=Save Location
 
 # Download Manager
 downloadsUnknownSize=Unknown size
 
--- a/browser/metro/locales/en-US/chrome/preferences.dtd
+++ b/browser/metro/locales/en-US/chrome/preferences.dtd
@@ -20,10 +20,11 @@
 <!ENTITY optionsHeader.char.options.label                        "Show encoding options on the App Bar">
 <!ENTITY optionsHeader.privacy.clearPrivateData.title            "Clear Private Data">
 <!ENTITY optionsHeader.privacy.clearPrivateData.label            "Clear your browsing history, passwords, cookies, and form data on this device">
 <!ENTITY optionsHeader.privacy.clearPrivateData.button           "Clear">
 <!ENTITY optionsHeader.privacy.passwords.label                   "Remember Passwords">
 <!ENTITY optionsHeader.privacy.doNotTrack.title                  "Tracking">
 <!ENTITY optionsHeader.privacy.doNotTrack.label                  "Tell websites not to track me">
 <!ENTITY optionsHeader.privacy.masterPassword.label              "Use Master Password">
+
+<!-- ## Sync Flyout Panel ## -->
 <!-- see sync.dtd -->
-
--- a/browser/metro/locales/en-US/chrome/sync.dtd
+++ b/browser/metro/locales/en-US/chrome/sync.dtd
@@ -1,17 +1,17 @@
 <!-- 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/. -->
 
-<!ENTITY sync.title                 "Sync">
+
+<!ENTITY syncHeader.title           "Sync">
 <!ENTITY sync.notconnected          "Not connected">
 <!ENTITY sync.connect               "Connect">
 <!ENTITY sync.connected             "Connected">
-<!ENTITY sync.details               "Details">
 <!ENTITY sync.deviceName            "This device">
 <!ENTITY sync.disconnect            "Disconnect">
 <!ENTITY sync.syncNow               "Sync Now">
 
 <!ENTITY sync.setup.title           "Connect to Sync">
 <!ENTITY sync.setup.pair            "To activate, select &#x0022;Pair a Device&#x0022; on your other device.">
 <!ENTITY sync.fallback              "I'm not near my computer…">
 <!ENTITY sync.setup.manual          "Enter your Sync account information">
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -623,16 +623,22 @@ appbar toolbarbutton[disabled="true"] {
 
 /* Flyouts ---------------------------------------------------------------- */
 
 /* don't add a margin to the very top settings entry in flyouts */
 flyoutpanel > settings:first-child {
   margin-top: 0px;
 }
 
+/* Sync flyout pane */
+
+#sync-flyoutpanel {
+  width: 400px;
+}
+
 /* About flyout pane */
 
 #about-flyoutpanel {
   width: 350px;
   background-image:url('chrome://browser/skin/images/about-footer.png');
   background-repeat: no-repeat;
   background-attachment: fixed;
   background-position: right bottom;
@@ -682,20 +688,22 @@ settings {
   margin-top: 32px;
 }
 
 .settings-title {
   font-weight: bold;
 }
 
 /* <setting> elements that are not in a <settings> group get special treatment */
-#prefs-flyoutpanel > setting {
+#prefs-flyoutpanel > setting,
+#sync-flyoutpanel > setting {
   margin-top: 16px;
 }
-#prefs-flyoutpanel > setting .preferences-title {
+
+#prefs-flyoutpanel > setting .preferences-title,
   font-weight: bold
 }
 
 setting[type="integer"] > .preferences-alignment,
 setting[type="string"] > .preferences-alignment {
   -moz-box-flex: 3;
 }
 
--- a/configure.in
+++ b/configure.in
@@ -9018,19 +9018,19 @@ if test "${OS_TARGET}" = "WINNT"; then
    else
       OS_BITS=32
    fi
    EXTRA_GYP_DEFINES="-D MSVS_VERSION=${_MSVS_VERSION} -D MSVS_OS_BITS=${OS_BITS}"
 
 elif test "${OS_TARGET}" = "Android"; then
    EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
    if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then
-      EXTRA_GYP_DEFINES+=" -D armv7=0 "
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
    else
-      EXTRA_GYP_DEFINES+=" -D armv7=1 "
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 "
    fi
 fi
 
 # Don't try to compile sse4.1 code if toolchain doesn't support
 if test -z "$HAVE_TOOLCHAIN_SUPPORT_MSSE4_1"; then
   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D yuv_disable_asm=1"
 fi
 
--- a/content/base/test/test_child_process_shutdown_message.html
+++ b/content/base/test/test_child_process_shutdown_message.html
@@ -29,17 +29,17 @@ let ppmm = SpecialPowers.Cc["@mozilla.or
  * @param isApp
  *        If true, the example.org site will be loaded as an app.
  */
 function loadBrowser(isApp, callback) {
   let iframe = document.createElement("iframe");
   if (isApp) {
     iframe.setAttribute("mozapp", APP_MANIFEST);
   }
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.src = APP_URL;
   document.getElementById("content").appendChild(iframe);
 
   iframe.addEventListener("mozbrowserloadend", function onloadend() {
     iframe.removeEventListener("mozbrowserloadend", onloadend);
     callback(iframe);
   });
 }
--- a/content/base/test/test_ipc_messagemanager_blob.html
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -38,17 +38,17 @@
         reader.readAsText(message.json);
       });
     }
 
     function runTests() {
       ok("Browser prefs set.");
 
       let iframe = document.createElement("iframe");
-      iframe.mozbrowser = true;
+      SpecialPowers.wrap(iframe).mozbrowser = true;
       iframe.id = "iframe";
       iframe.src = childFrameURL;
 
       iframe.addEventListener("mozbrowserloadend", function() {
         ok(true, "Got iframe load event.");
 
         const messages = [
           "hi!",
--- a/content/base/test/test_messagemanager_assertpermission.html
+++ b/content/base/test/test_messagemanager_assertpermission.html
@@ -40,17 +40,17 @@ function setUp() {
 }
 
 /**
  * Load the example.org app in an <iframe mozbrowser mozapp>
  */
 function loadApp(callback) {
   let iframe = document.createElement("iframe");
   iframe.setAttribute("mozapp", APP_MANIFEST);
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.src = APP_URL;
   document.getElementById("content").appendChild(iframe);
 
   iframe.addEventListener("mozbrowserloadend", function onloadend() {
     iframe.removeEventListener("mozbrowserloadend", onloadend);
     callback(iframe);
   });
 }
--- a/content/canvas/crashtests/crashtests.list
+++ b/content/canvas/crashtests/crashtests.list
@@ -9,11 +9,11 @@ load texImage2D.html
 load 729116.html
 load 745699-1.html
 load 746813-1.html
 # this test crashes in a bunch places still
 #load 745818-large-source.html
 load 743499-negative-size.html
 skip-if(Android) load 767337-1.html
 skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
-skip-if(B2G) load 789933-1.html # bug 833371
+skip-if(Android||B2G) load 789933-1.html # bug 833371
 load 794463-1.html
 load 802926-1.html
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -1894,29 +1894,16 @@ nsTextEditorState::SetValue(const nsAStr
         }
 
         plaintextEditor->SetMaxTextLength(savedMaxLength);
         mEditor->SetFlags(savedFlags);
         if (selPriv)
           selPriv->EndBatchChanges();
       }
     }
-
-    // This second check _shouldn't_ be necessary, but let's be safe.
-    if (!weakFrame.IsAlive()) {
-      return;
-    }
-    nsIScrollableFrame* scrollableFrame = do_QueryFrame(mBoundFrame->GetFirstPrincipalChild());
-    if (scrollableFrame)
-    {
-      // Scroll the upper left corner of the text control's
-      // content area back into view.
-      scrollableFrame->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
-    }
-
   } else {
     if (!mValue) {
       mValue = new nsCString;
     }
     nsString value(aValue);
     nsContentUtils::PlatformToDOMLineBreaks(value);
     CopyUTF16toUTF8(value, *mValue);
 
--- a/content/html/content/test/forms/Makefile.in
+++ b/content/html/content/test/forms/Makefile.in
@@ -47,12 +47,13 @@ MOCHITEST_FILES = \
 		test_step_attribute.html \
 		test_stepup_stepdown.html \
 		test_valueasnumber_attribute.html \
 		test_experimental_forms_pref.html \
 		test_input_typing_sanitization.html \
 		test_input_sanitization.html \
 		test_valueasdate_attribute.html \
 		test_input_file_b2g_disabled.html \
+		test_input_textarea_set_value_no_scroll.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/forms/test_input_textarea_set_value_no_scroll.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=829606
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 829606</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript;version=1.7">
+
+  /** Test for Bug 829606 **/
+  /*
+   * This test checks that setting .value on an text field (input or textarea)
+   * doesn't scroll the field to its beginning.
+   */
+
+  SimpleTest.waitForExplicitFinish();
+
+  var gTestRunner = null;
+
+  function test(aElementName)
+  {
+    var element = document.getElementsByTagName(aElementName)[0];
+    element.focus();
+
+    var baseSnapshot = snapshotWindow(window);
+
+    // This is a sanity check.
+    var s2 = snapshotWindow(window);
+    var results = compareSnapshots(baseSnapshot, snapshotWindow(window), true);
+    ok(results[0], "sanity check: screenshots should be the same");
+
+    element.selectionStart = element.selectionEnd = element.value.length;
+
+    setTimeout(function() {
+      synthesizeKey('f', {});
+
+      var selectionAtTheEndSnapshot = snapshotWindow(window);
+      results = compareSnapshots(baseSnapshot, selectionAtTheEndSnapshot, false);
+      ok(results[0], "after appending a character, string should have changed");
+
+      element.value = element.value;
+      var tmpSnapshot = snapshotWindow(window);
+
+      results = compareSnapshots(baseSnapshot, tmpSnapshot, false);
+      ok(results[0], "re-settig the value should change nothing");
+
+      results = compareSnapshots(selectionAtTheEndSnapshot, tmpSnapshot, true);
+      ok(results[0], "re-settig the value should change nothing");
+
+      element.selectionStart = element.selectionEnd = 0;
+      element.blur();
+
+      gTestRunner.next();
+    }, 0);
+  }
+
+  // This test checks that when a textarea has a long list of values and the
+  // textarea's value is then changed, the values are shown correctly.
+  function testCorrectUpdateOnScroll()
+  {
+    var textarea = document.createElement('textarea');
+    textarea.rows = 5;
+    textarea.cols = 10;
+    textarea.value = 'a\nb\nc\nd';
+    document.getElementById('content').appendChild(textarea);
+
+    var baseSnapshot = snapshotWindow(window);
+
+    textarea.value = '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n';
+    textarea.selectionStart = textarea.selectionEnd = textarea.value.length;
+
+    var fullSnapshot = snapshotWindow(window);
+    var results = compareSnapshots(baseSnapshot, fullSnapshot, false);
+    ok(results[0], "sanity check: screenshots should not be the same");
+
+    textarea.value = 'a\nb\nc\nd';
+
+    var tmpSnapshot = snapshotWindow(window);
+    results = compareSnapshots(baseSnapshot, tmpSnapshot, true);
+    ok(results[0], "textarea view should look like the beginning");
+
+    setTimeout(function() {
+      gTestRunner.next();
+    }, 0);
+  }
+
+  function runTest()
+  {
+    test('input');
+    yield;
+    test('textarea');
+    yield;
+    testCorrectUpdateOnScroll();
+    yield;
+    SimpleTest.finish();
+    yield;
+  }
+
+  gTestRunner = runTest();
+
+  SimpleTest.waitForFocus(function() {
+    gTestRunner.next();
+  });;
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=829606">Mozilla Bug 829606</a>
+<p id="display"></p>
+<div id="content">
+  <textarea rows='1' cols='5'>this is a \n long text</textarea>
+  <input size='5' value="this is a very long text">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/html/content/test/test_fullscreen-api.html
+++ b/content/html/content/test/test_fullscreen-api.html
@@ -16,17 +16,21 @@
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=545812">Mozilla Bug 545812</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-SimpleTest.expectAssertions(1); // bug 845552
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 1); // bug 845552
+} else {
+  SimpleTest.expectAssertions(1); // bug 845552
+}
 
 /** Tests for Bug 545812 **/
 
 // Ensure the full-screen api is enabled, and will be disabled on test exit.
 SpecialPowers.setBoolPref("full-screen-api.enabled", true);
 
 // Disable the requirement for trusted contexts only, so the tests are easier
 // to write.
--- a/content/media/MediaSegment.h
+++ b/content/media/MediaSegment.h
@@ -95,16 +95,20 @@ public:
   /**
    * Insert aDuration of null data at the start of the segment.
    */
   virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0;
   /**
    * Insert aDuration of null data at the end of the segment.
    */
   virtual void AppendNullData(TrackTicks aDuration) = 0;
+  /**
+   * Remove all contents, setting duration to 0.
+   */
+  virtual void Clear() = 0;
 
 protected:
   MediaSegment(Type aType) : mDuration(0), mType(aType)
   {
     MOZ_COUNT_CTOR(MediaSegment);
   }
 
   TrackTicks mDuration; // total of mDurations of all chunks
@@ -181,16 +185,21 @@ public:
     }
     if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
       mChunks[mChunks.Length() - 1].mDuration += aDuration;
     } else {
       mChunks.AppendElement()->SetNull(aDuration);
     }
     mDuration += aDuration;
   }
+  virtual void Clear()
+  {
+    mDuration = 0;
+    mChunks.Clear();
+  }
 
   class ChunkIterator {
   public:
     ChunkIterator(MediaSegmentBase<C, Chunk>& aSegment)
       : mSegment(aSegment), mIndex(0) {}
     bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
     void Next() { ++mIndex; }
     Chunk& operator*() { return mSegment.mChunks[mIndex]; }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -113,16 +113,19 @@ MediaStreamGraphImpl::ExtractPendingInpu
     if (aStream->mPullEnabled && !aStream->mFinished &&
         !aStream->mListeners.IsEmpty()) {
       // Compute how much stream time we'll need assuming we don't block
       // the stream at all between mBlockingDecisionsMadeUntilTime and
       // aDesiredUpToTime.
       StreamTime t =
         GraphTimeToStreamTime(aStream, mStateComputedTime) +
         (aDesiredUpToTime - mStateComputedTime);
+      LOG(PR_LOG_DEBUG, ("Calling NotifyPull aStream=%p t=%f current end=%f", aStream,
+                         MediaTimeToSeconds(t),
+                         MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
       if (t > aStream->mBuffer.GetEnd()) {
         *aEnsureNextIteration = true;
         for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
           MediaStreamListener* l = aStream->mListeners[j];
           {
             MutexAutoUnlock unlock(aStream->mMutex);
             l->NotifyPull(this, t);
           }
@@ -1632,54 +1635,56 @@ SourceMediaStream::AddTrack(TrackID aID,
   data->mCommands = TRACK_CREATE;
   data->mData = aSegment;
   data->mHaveEnough = false;
   if (!mDestroyed) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
-void
+bool
 SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment)
 {
   MutexAutoLock lock(mMutex);
   // ::EndAllTrackAndFinished() can end these before the sources notice
+  bool appended = false;
   if (!mFinished) {
     TrackData *track = FindDataForTrack(aID);
     if (track) {
       track->mData->AppendFrom(aSegment);
+      appended = true;
     } else {
-      NS_ERROR("Append to non-existent track!");
+      aSegment->Clear();
     }
   }
   if (!mDestroyed) {
     GraphImpl()->EnsureNextIteration();
   }
+  return appended;
 }
 
 bool
 SourceMediaStream::HaveEnoughBuffered(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
     return track->mHaveEnough;
   }
-  NS_ERROR("No track in HaveEnoughBuffered!");
-  return true;
+  return false;
 }
 
 void
 SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
     nsIThread* aSignalThread, nsIRunnable* aSignalRunnable)
 {
   MutexAutoLock lock(mMutex);
   TrackData* data = FindDataForTrack(aID);
   if (!data) {
-    NS_ERROR("No track in DispatchWhenNotEnoughBuffered");
+    aSignalThread->Dispatch(aSignalRunnable, 0);
     return;
   }
 
   if (data->mHaveEnough) {
     data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalThread, aSignalRunnable);
   } else {
     aSignalThread->Dispatch(aSignalRunnable, 0);
   }
@@ -1689,18 +1694,16 @@ void
 SourceMediaStream::EndTrack(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   // ::EndAllTrackAndFinished() can end these before the sources call this
   if (!mFinished) {
     TrackData *track = FindDataForTrack(aID);
     if (track) {
       track->mCommands |= TRACK_END;
-    } else {
-      NS_ERROR("End of non-existant track");
     }
   }
   if (!mDestroyed) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
 void
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -562,32 +562,37 @@ public:
    * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should
    * contain data starting after aStart.
    */
   void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
                 MediaSegment* aSegment);
   /**
    * Append media data to a track. Ownership of aSegment remains with the caller,
    * but aSegment is emptied.
+   * Returns false if the data was not appended because no such track exists
+   * or the stream was already finished.
    */
-  void AppendToTrack(TrackID aID, MediaSegment* aSegment);
+  bool AppendToTrack(TrackID aID, MediaSegment* aSegment);
   /**
    * Returns true if the buffer currently has enough data.
+   * Returns false if there isn't enough data or if no such track exists.
    */
   bool HaveEnoughBuffered(TrackID aID);
   /**
    * Ensures that aSignalRunnable will be dispatched to aSignalThread
    * when we don't have enough buffered data in the track (which could be
-   * immediately).
+   * immediately). Will dispatch the runnable immediately if the track
+   * does not exist.
    */
   void DispatchWhenNotEnoughBuffered(TrackID aID,
       nsIThread* aSignalThread, nsIRunnable* aSignalRunnable);
   /**
    * Indicate that a track has ended. Do not do any more API calls
    * affecting this track.
+   * Ignored if the track does not exist.
    */
   void EndTrack(TrackID aID);
   /**
    * Indicate that no tracks will be added starting before time aKnownTime.
    * aKnownTime must be >= its value at the last call to AdvanceKnownTracksTime.
    */
   void AdvanceKnownTracksTime(StreamTime aKnownTime);
   /**
@@ -648,17 +653,16 @@ public:
 protected:
   TrackData* FindDataForTrack(TrackID aID)
   {
     for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
       if (mUpdateTracks[i].mID == aID) {
         return &mUpdateTracks[i];
       }
     }
-    NS_ERROR("Bad track ID!");
     return nullptr;
   }
 
   // Media stream graph thread only
   MediaStreamListener::Consumption mLastConsumptionState;
 
   // This must be acquired *before* MediaStreamGraphImpl's lock, if they are
   // held together.
--- a/content/media/test/test_a4_tone.html
+++ b/content/media/test/test_a4_tone.html
@@ -16,16 +16,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
 
 <!-- mute audio, since there is no need to hear the sound for these tests -->
 <audio id='a1' controls></audio>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 2); // bug 845676
+}
+
 /**
 * FFT is a class for calculating the Discrete Fourier Transform of a signal
 * with the Fast Fourier Transform algorithm.
 *
 * Source: github.com/corbanbrook/dsp.js; License: MIT; Copyright: Corban Brook
 *
 * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
--- a/content/media/test/test_bug493187.html
+++ b/content/media/test/test_bug493187.html
@@ -11,16 +11,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="use_large_cache.js"></script>
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=493187">Mozilla Bug 493187</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.expectAssertions(0, 1);
+
 var manager = new MediaTestManager;
 
 function start(e) {
   e.target.currentTime = e.target.duration / 4;
 }
 
 function startSeeking(e) {
   e.target._seeked = true;
--- a/content/media/test/test_framebuffer.html
+++ b/content/media/test/test_framebuffer.html
@@ -12,16 +12,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
 
 <!-- mute audio, since there is no need to hear the sound for these tests -->
 <audio id='a1' controls></audio>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
+
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 2); // bug 845676
+}
+
 var testFile = "bug495794.ogg";
 var testFileDuration = 0.30;
 var testFileChannelCount = 6;
 var testFileSampleRate = 48000;
 var testFileFrameBufferLength = 6 * 1024;
 
 var undef;
 
--- a/content/media/test/test_invalid_reject.html
+++ b/content/media/test/test_invalid_reject.html
@@ -6,16 +6,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 20); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   var v = document.createElement('video');
   manager.started(token);
 
   // Set up event handlers. Seeing any of these is a failure.
   function badEvent(type) { return function(e) {
--- a/content/media/test/test_loop.html
+++ b/content/media/test/test_loop.html
@@ -4,16 +4,21 @@
   <title>Test looping support</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 16); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   manager.started(token);
   var v = document.createElement('video');
   v.token = token;
   v.src = test.name;
   v.name = test.name;
--- a/content/media/test/test_paused_after_ended.html
+++ b/content/media/test/test_paused_after_ended.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 20); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function ended(evt) {
   var v = evt.target;
   is(v.gotPause, true, "We should have received a \"pause\" event.")
   is(v.paused, true, v._name + " must be paused after end");
   manager.finished(v.token);
 }
--- a/content/media/test/test_play_events.html
+++ b/content/media/test/test_play_events.html
@@ -6,16 +6,20 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 
 <script>
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 16); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 var tokens = {
   0:                ["play"],
   "play":           ["canplay"],
   "canplay":        ["playing"],
   "playing":        ["canplay", "canplaythrough"],
   "canplaythrough": ["canplay", "canplaythrough"]
--- a/content/media/test/test_play_events_2.html
+++ b/content/media/test/test_play_events_2.html
@@ -5,16 +5,21 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 
 <script>
+
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 16); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 var tokens = {
   0:                ["play"],
   "play":           ["canplay"],
   "canplay":        ["playing"],
   "playing":        ["canplay", "canplaythrough"],
   "canplaythrough": ["canplay", "canplaythrough"]
--- a/content/media/test/test_play_twice.html
+++ b/content/media/test/test_play_twice.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 18); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   var v = document.createElement('video');
   v.token = token;
   manager.started(token);
   v.src = test.name;
   v.name = test.name;
--- a/content/media/test/test_playback.html
+++ b/content/media/test/test_playback.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 84); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   var v = document.createElement('video');
   v.token = token;
   manager.started(token);
 
   v.src = test.name;
--- a/content/media/test/test_playback_errors.html
+++ b/content/media/test/test_playback_errors.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 16); // bug 845676
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   var v = document.createElement('video');
   manager.started(token);
   v._errorCount = 0;
   v._ignore = false;
   function endedTest(v) {
--- a/content/media/test/test_playback_rate.html
+++ b/content/media/test/test_playback_rate.html
@@ -7,16 +7,18 @@
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type='application/javascript;version=1.8'>
 
 if (navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(0, 1);
+} else if (navigator.platform.startsWith("Mac")) {
+  SimpleTest.expectAssertions(0, 2);
 }
 
 let manager = new MediaTestManager;
 
 function rangeCheck(lhs, rhs, threshold) {
   var diff = Math.abs(lhs - rhs);
   if (diff < threshold) {
     return true;
--- a/content/media/test/test_preload_actions.html
+++ b/content/media/test/test_preload_actions.html
@@ -16,16 +16,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <!-- <button onClick="SimpleTest.finish();">Finish</button> -->
 <div>Tests complete: <span id="log" style="font-size: small;"></span></div>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 8); // bug 845676
+}
+
 /** Test for Bug 548523 **/
 
 var manager = new MediaTestManager;
 
 manager.onFinished = function() {
   is(gotLoadEvent, true, "Should not have delayed the load event indefinitely");
 };
 
--- a/content/media/test/test_seek.html
+++ b/content/media/test/test_seek.html
@@ -18,17 +18,17 @@
   <script type="text/javascript" src="seek11.js"></script>
   <script type="text/javascript" src="seek12.js"></script>
   <script type="text/javascript" src="seek13.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.expectAssertions(120, 400);
+SimpleTest.expectAssertions(80, 400);
 
 var manager = new MediaTestManager;
 
 const NUM_SEEK_TESTS = 13;
 
 function createTestArray() {
   var tests = [];
   var tmpVid = document.createElement("video");
--- a/content/media/test/test_too_many_elements.html
+++ b/content/media/test/test_too_many_elements.html
@@ -15,16 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <p>Received loadeddata event for <span id="result">0</span> <span id="expected"></span>audio elements.</p>
 <pre id="test">
 <script type="application/javascript">
 
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 2); // bug 845676
+}
+
 /** Test for Bug 713381 **/
 
 const num = 500;
 var loadeddata = 0;
 
 var result = document.getElementById("result");
 document.getElementById("expected").innerHTML = " of " + num + " ";
 
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -119,18 +119,21 @@ MediaEngineWebRTCVideoSource::NotifyPull
   TrackTicks delta = target - aLastEndTime;
   LOGFRAME(("NotifyPull, desired = %ld, target = %ld, delta = %ld %s", (int64_t) aDesiredTime, 
             (int64_t) target, (int64_t) delta, image ? "" : "<null>"));
   // Don't append if we've already provided a frame that supposedly goes past the current aDesiredTime
   // Doing so means a negative delta and thus messes up handling of the graph
   if (delta > 0) {
     // NULL images are allowed
     segment.AppendFrame(image ? image.forget() : nullptr, delta, gfxIntSize(mWidth, mHeight));
-    aSource->AppendToTrack(aID, &(segment));
-    aLastEndTime = target;
+    // This can fail if either a) we haven't added the track yet, or b)
+    // we've removed or finished the track.
+    if (aSource->AppendToTrack(aID, &(segment))) {
+      aLastEndTime = target;
+    }
   }
 }
 
 void
 MediaEngineWebRTCVideoSource::ChooseCapability(uint32_t aWidth, uint32_t aHeight, uint32_t aMinFPS)
 {
   int num = mViECapture->NumberOfCapabilities(mUniqueId, KMaxUniqueIdLength);
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -867,19 +867,19 @@ nsDocShell::Init()
                                      nsIWebProgress::NOTIFY_STATE_NETWORK);
     
 }
 
 void
 nsDocShell::DestroyChildren()
 {
     nsCOMPtr<nsIDocShellTreeItem> shell;
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; i++) {
-        shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        shell = do_QueryObject(iter.GetNext());
         NS_ASSERTION(shell, "docshell has null child");
 
         if (shell) {
             shell->SetTreeOwner(nullptr);
         }
     }
 
     nsDocLoader::DestroyChildren();
@@ -2075,19 +2075,19 @@ nsDocShell::SetPrivateBrowsing(bool aUse
             if (aUsePrivateBrowsing) {
                 IncreasePrivateDocShellCount();
             } else {
                 DecreasePrivateDocShellCount();
             }
         }
     }
 
-    uint32_t count = mChildList.Length();
-    for (uint32_t i = 0; i < count; ++i) {
-        nsCOMPtr<nsILoadContext> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
         if (shell) {
             shell->SetPrivateBrowsing(aUsePrivateBrowsing);
         }
     }
 
     if (changed) {
         nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
         while (iter.HasMore()) {
@@ -2111,19 +2111,19 @@ nsDocShell::SetAffectPrivateSessionLifet
         if (aAffectLifetime) {
             IncreasePrivateDocShellCount();
         } else {
             DecreasePrivateDocShellCount();
         }
     }
     mAffectPrivateSessionLifetime = aAffectLifetime;
 
-    uint32_t count = mChildList.Length();
-    for (uint32_t i = 0; i < count; ++i) {
-        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
         if (shell) {
             shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
         }
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2483,19 +2483,19 @@ nsDocShell::HistoryPurged(int32_t aNumEn
     // These indices are used for fastback cache eviction, to determine
     // which session history entries are candidates for content viewer
     // eviction.  We need to adjust by the number of entries that we
     // just purged from history, so that we look at the right session history
     // entries during eviction.
     mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
     mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
 
-    uint32_t count = mChildList.Length();
-    for (uint32_t i = 0; i < count; ++i) {
-        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
         if (shell) {
             shell->HistoryPurged(aNumEntries);
         }
     }
 
     return NS_OK;
 }
 
@@ -2513,19 +2513,19 @@ nsDocShell::HistoryTransactionRemoved(in
         --mPreviousTransIndex;
     }
     if (mLoadedTransIndex == aIndex) {
         mLoadedTransIndex = 0;
     } else if (aIndex < mLoadedTransIndex) {
         --mLoadedTransIndex;
     }
                             
-    uint32_t count = mChildList.Length();
-    for (uint32_t i = 0; i < count; ++i) {
-        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
         if (shell) {
             static_cast<nsDocShell*>(shell.get())->
                 HistoryTransactionRemoved(aIndex);
         }
     }
 
     return NS_OK;
 }
@@ -3398,19 +3398,19 @@ nsDocShell::SetTreeOwner(nsIDocShellTree
                 webProgress->AddProgressListener(newListener,
                                                  nsIWebProgress::NOTIFY_ALL);
             }
         }
     }
 
     mTreeOwner = aTreeOwner;    // Weak reference per API
 
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; i++) {
-        nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
         NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
         int32_t childType = ~mItemType; // Set it to not us in case the get fails
         child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
         if (childType == mItemType)
             child->SetTreeOwner(aTreeOwner);
     }
 
     return NS_OK;
@@ -3597,17 +3597,17 @@ nsDocShell::GetChildAt(int32_t aIndex, n
 #ifdef DEBUG
     if (aIndex < 0) {
       NS_WARNING("Negative index passed to GetChildAt");
     } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
       NS_WARNING("Too large an index passed to GetChildAt");
     }
 #endif
 
-    nsIDocumentLoader* child = SafeChildAt(aIndex);
+    nsIDocumentLoader* child = ChildAt(aIndex);
     NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
     
     return CallQueryInterface(child, aChild);
 }
 
 NS_IMETHODIMP
 nsDocShell::FindChildWithName(const PRUnichar * aName,
                               bool aRecurse, bool aSameType,
@@ -3619,19 +3619,19 @@ nsDocShell::FindChildWithName(const PRUn
     NS_ENSURE_ARG_POINTER(_retval);
 
     *_retval = nullptr;          // if we don't find one, we return NS_OK and a null result 
 
     if (!*aName)
         return NS_OK;
 
     nsXPIDLString childName;
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; i++) {
-        nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
         NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
         int32_t childType;
         child->GetItemType(&childType);
 
         if (aSameType && (childType != mItemType))
             continue;
 
         bool childNameEquals = false;
@@ -4671,19 +4671,19 @@ nsDocShell::Stop(uint32_t aStopFlags)
         }
 
         // XXXbz We could also pass |this| to nsIURILoader::Stop.  That will
         // just call Stop() on us as an nsIDocumentLoader... We need fewer
         // redundant apis!
         Stop();
     }
 
-    uint32_t count = mChildList.Length();
-    for (uint32_t n = 0; n < count; n++) {
-        nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(ChildAt(n)));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
         if (shellAsNav)
             shellAsNav->Stop(aStopFlags);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -5285,19 +5285,19 @@ nsDocShell::SetIsActive(bool aIsActive)
       nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
       if (doc) {
           doc->PostVisibilityUpdateEvent();
       }
   }
 
   // Recursively tell all of our children, but don't tell <iframe mozbrowser>
   // children; they handle their state separately.
-  uint32_t n = mChildList.Length();
-  for (uint32_t i = 0; i < n; ++i) {
-      nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(ChildAt(i));
+  nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+  while (iter.HasMore()) {
+      nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
       if (!docshell) {
           continue;
       }
 
       if (!docshell->GetIsBrowserOrApp()) {
           docshell->SetIsActive(aIsActive);
       }
   }
@@ -6208,35 +6208,35 @@ nsDocShell::SuspendRefreshURIs()
             nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
             NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
 
             mRefreshURIList->ReplaceElementAt(rt, i);
         }
     }
 
     // Suspend refresh URIs for our child shells as well.
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; ++i) {
-        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
         if (shell)
             shell->SuspendRefreshURIs();
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::ResumeRefreshURIs()
 {
     RefreshURIFromQueue();
 
     // Resume refresh URIs for our child shells as well.
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; ++i) {
-        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
         if (shell)
             shell->ResumeRefreshURIs();
     }
 
     return NS_OK;
 }
 
 nsresult
@@ -7271,36 +7271,36 @@ nsDocShell::BeginRestore(nsIContentViewe
     }
 
     return NS_OK;
 }
 
 nsresult
 nsDocShell::BeginRestoreChildren()
 {
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; ++i) {
-        nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
         if (child) {
             nsresult rv = child->BeginRestore(nullptr, false);
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::FinishRestore()
 {
     // First we call finishRestore() on our children.  In the simulated load,
     // all of the child frames finish loading before the main document.
 
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; ++i) {
-        nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
         if (child) {
             child->FinishRestore();
         }
     }
 
     if (mOSHE && mOSHE->HasDetachedEditor()) {
       ReattachEditorToWindow(mOSHE);
     }
@@ -7793,19 +7793,19 @@ nsDocShell::RestoreFromHistory()
     privWin->ResumeTimeouts();
 
     // Restore the refresh URI list.  The refresh timers will be restarted
     // when EndPageLoad() is called.
     mRefreshURIList = refreshURIList;
 
     // Meta-refresh timers have been restarted for this shell, but not
     // for our children.  Walk the child shells and restart their timers.
-    uint32_t n = mChildList.Length();
-    for (uint32_t i = 0; i < n; ++i) {
-        nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
+    nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+    while (iter.HasMore()) {
+        nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
         if (child)
             child->ResumeRefreshURIs();
     }
 
     // Make sure this presentation is the same size as the previous
     // presentation.  If this is not the same size we showed it at last time,
     // then we need to resize the widget.
 
@@ -10806,20 +10806,19 @@ nsDocShell::WalkHistoryEntries(nsISHEntr
             continue;
         }
 
         nsDocShell *childShell = nullptr;
         if (aRootShell) {
             // Walk the children of aRootShell and see if one of them
             // has srcChild as a SHEntry.
 
-            uint32_t childCount = aRootShell->mChildList.Length();
-            for (uint32_t j = 0; j < childCount; ++j) {
-                nsDocShell *child =
-                    static_cast<nsDocShell*>(aRootShell->ChildAt(j));
+            nsTObserverArray<nsDocLoader*>::ForwardIterator iter(aRootShell->mChildList);
+            while (iter.HasMore()) {
+                nsDocShell *child = static_cast<nsDocShell*>(iter.GetNext());
 
                 if (child->HasHistoryEntry(childEntry)) {
                     childShell = child;
                     break;
                 }
             }
         }
         nsresult rv = aCallback(childEntry, childShell, i, aData);
--- a/docshell/test/navigation/test_child.html
+++ b/docshell/test/navigation/test_child.html
@@ -4,17 +4,17 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
-if (navigator.platform.startsWith("Mac")) {
+if (!navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(0, 1);
 }
 
 window.onload = function() {
   navigateByLocation(frames[0]);
   navigateByOpen("child1");
   navigateByForm("child2");
   navigateByHyperlink("child3");
--- a/docshell/test/navigation/test_grandchild.html
+++ b/docshell/test/navigation/test_grandchild.html
@@ -4,17 +4,17 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 200px; }
     </style>
 <script>
-if (navigator.platform.startsWith("Mac")) {
+if (!navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(0, 1);
 }
 
 window.onload = function () {
   navigateByLocation(frames[0].frames[0]);
   navigateByOpen("child1_child0");
   navigateByForm("child2_child0");
   navigateByHyperlink("child3_child0");
--- a/docshell/test/navigation/test_not-opener.html
+++ b/docshell/test/navigation/test_not-opener.html
@@ -4,16 +4,20 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+if (navigator.platform.startsWith("Linux")) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 window.onload = function () {
   //navigateByLocation(window0);  // Don't have a handle to the window.
   navigateByOpen("window1");
   navigateByForm("window2");
   navigateByHyperlink("window3");
 
   xpcWaitForFinishedFrames(function() {
     is(xpcGetFramesByName("window1").length, 2, "Should not be able to navigate popup's popup by calling window.open.");
--- a/docshell/test/navigation/test_opener.html
+++ b/docshell/test/navigation/test_opener.html
@@ -4,16 +4,20 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+if (navigator.platform.startsWith("Linux")) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 window.onload = function () {
   navigateByLocation(window0);
   navigateByOpen("window1");
   navigateByForm("window2");
   navigateByHyperlink("window3");
 
   xpcWaitForFinishedFrames(function() {
     isNavigated(window0, "Should be able to navigate popup by setting location.");
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -1,18 +1,24 @@
 /* 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 "Crypto.h"
 #include "nsIDOMClassInfo.h"
 #include "DOMError.h"
 #include "nsString.h"
-#include "nsIRandomGenerator.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+#include "nsIRandomGenerator.h"
+
+#include "mozilla/dom/ContentChild.h"
+
+using mozilla::dom::ContentChild;
 
 using namespace js::ArrayBufferView;
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN(Crypto)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
@@ -31,16 +37,18 @@ Crypto::Crypto()
 Crypto::~Crypto()
 {
   MOZ_COUNT_DTOR(Crypto);
 }
 
 NS_IMETHODIMP
 Crypto::GetRandomValues(const jsval& aData, JSContext *cx, jsval* _retval)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Called on the wrong thread");
+
   // Make sure this is a JavaScript object
   if (!aData.isObject()) {
     return NS_ERROR_DOM_NOT_OBJECT_ERR;
   }
 
   JSObject* view = &aData.toObject();
 
   // Make sure this object is an ArrayBufferView
@@ -67,37 +75,41 @@ Crypto::GetRandomValues(const jsval& aDa
 
   if (dataLen == 0) {
     NS_WARNING("ArrayBufferView length is 0, cannot continue");
     return NS_OK;
   } else if (dataLen > 65536) {
     return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
   }
 
-  nsCOMPtr<nsIRandomGenerator> randomGenerator;
-  nsresult rv;
-  randomGenerator =
-    do_GetService("@mozilla.org/security/random-generator;1", &rv);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("unable to continue without random number generator");
-    return rv;
-  }
-
   void *dataptr = JS_GetArrayBufferViewData(view);
   NS_ENSURE_TRUE(dataptr, NS_ERROR_FAILURE);
-
   unsigned char* data =
     static_cast<unsigned char*>(dataptr);
 
-  uint8_t *buf;
-  rv = randomGenerator->GenerateRandomBytes(dataLen, &buf);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    InfallibleTArray<uint8_t> randomValues;
+    // Tell the parent process to generate random values via PContent
+    ContentChild* cc = ContentChild::GetSingleton();
+    if (!cc->SendGetRandomValues(dataLen, &randomValues)) {
+      return NS_ERROR_FAILURE;
+    }
+    NS_ASSERTION(dataLen == randomValues.Length(),
+                 "Invalid length returned from parent process!");
+    memcpy(data, randomValues.Elements(), dataLen);
+  } else {
+    uint8_t *buf = GetRandomValues(dataLen);
 
-  memcpy(data, buf, dataLen);
-  NS_Free(buf);
+    if (!buf) {
+      return NS_ERROR_FAILURE;
+    }
+
+    memcpy(data, buf, dataLen);
+    NS_Free(buf);
+  }
 
   *_retval = OBJECT_TO_JSVAL(view);
 
   return NS_OK;
 }
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
 // Stub out the legacy nsIDOMCrypto methods. The actual
@@ -163,10 +175,27 @@ Crypto::Logout()
 
 NS_IMETHODIMP
 Crypto::DisableRightClick()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 #endif
 
+uint8_t*
+Crypto::GetRandomValues(uint32_t aLength)
+{
+  nsCOMPtr<nsIRandomGenerator> randomGenerator;
+  nsresult rv;
+  randomGenerator =
+    do_GetService("@mozilla.org/security/random-generator;1");
+  NS_ENSURE_TRUE(randomGenerator, nullptr);
+
+  uint8_t* buf;
+  rv = randomGenerator->GenerateRandomBytes(aLength, &buf);
+
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return buf;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Crypto.h
+++ b/dom/base/Crypto.h
@@ -20,14 +20,17 @@ namespace dom {
 class Crypto : public nsIDOMCrypto
 {
 public:
   Crypto();
   virtual ~Crypto();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMCRYPTO
+
+  static uint8_t*
+  GetRandomValues(uint32_t aLength);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Crypto_h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3006,18 +3006,19 @@ nsDOMWindowUtils::SelectAtPoint(float aX
 
   // The root frame for this content window
   nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
   if (!rootFrame) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // Get the target frame at the client coordinates passed to us
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  nsIntPoint pt(aX, aY);
+  nsPoint offset;
+  nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
+  nsIntPoint pt = ToWidgetPoint(aX, aY, offset, GetPresContext());
   nsPoint ptInRoot =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, pt, rootFrame);
   nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
   // This can happen if the page hasn't loaded yet or if the point
   // is outside the frame.
   if (!targetFrame) {
     return NS_ERROR_INVALID_ARG;
   }
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1018,17 +1018,75 @@ class IDLDictionary(IDLObjectWithScope):
         for inheritedMember in inheritedMembers:
             for member in self.members:
                 if member.identifier.name == inheritedMember.identifier.name:
                     raise WebIDLError("Dictionary %s has two members with name %s" %
                                       (self.identifier.name, member.identifier.name),
                                       [member.location, inheritedMember.location])
 
     def validate(self):
-        pass
+        def typeContainsDictionary(memberType, dictionary):
+            """
+            Returns a tuple whose:
+
+                - First element is a Boolean value indicating whether
+                  memberType contains dictionary.
+
+                - Second element is:
+                    A list of locations that leads from the type that was passed in
+                    the memberType argument, to the dictionary being validated,
+                    if the boolean value in the first element is True.
+
+                    None, if the boolean value in the first element is False.
+            """
+
+            if memberType.nullable() or \
+               memberType.isArray() or \
+               memberType.isSequence():
+                return typeContainsDictionary(memberType.inner, dictionary)
+
+            if memberType.isDictionary():
+                if memberType.inner == dictionary:
+                    return (True, [memberType.location])
+
+                (contains, locations) = dictionaryContainsDictionary(memberType.inner, \
+                                                                     dictionary)
+                if contains:
+                    return (True, [memberType.location] + locations)
+
+            if memberType.isUnion():
+                for member in memberType.flatMemberTypes:
+                    (contains, locations) = typeContainsDictionary(member, dictionary)
+                    if contains:
+                        return (True, locations)
+
+            return (False, None)
+
+        def dictionaryContainsDictionary(dictMember, dictionary):
+            for member in dictMember.members:
+                (contains, locations) = typeContainsDictionary(member.type, dictionary)
+                if contains:
+                    return (True, [member.location] + locations)
+
+            if dictMember.parent:
+                if dictMember.parent == dictionary:
+                    return (True, [dictMember.location])
+                else:
+                    (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+                    if contains:
+                        return (True, [dictMember.location] + locations)
+
+            return (False, None)
+
+        for member in self.members:
+            (contains, locations) = typeContainsDictionary(member.type, self)
+            if contains:
+                raise WebIDLError("Dictionary %s has member with itself as type." %
+                                  self.identifier.name,
+                                  [member.location] + locations)
 
     def addExtendedAttributes(self, attrs):
         assert len(attrs) == 0
 
     def _getDependentObjects(self):
         deps = set(self.members)
         if (self.parent):
             deps.add(self.parent)
--- a/dom/bindings/parser/tests/test_dictionary.py
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -291,8 +291,137 @@ def WebIDLTest(parser, harness):
         dictionary A {
         };
         interface X {
           void doFoo(optional (A or DOMString) arg);
         };
     """)
     results = parser.finish()
     harness.ok(True, "Union arg containing a dictionary should actually parse")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              Foo foo;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo3 : Foo {
+              short d;
+            };
+
+            dictionary Foo2 : Foo3 {
+              boolean c;
+            };
+
+            dictionary Foo1 : Foo2 {
+              long a;
+            };
+
+            dictionary Foo {
+              Foo1 b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Dictionary that "
+                      "inherits from its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (Foo or DOMString)[]? b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Nullable type "
+                      "whose inner type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (DOMString or Foo) b;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Union type, one of "
+                      "whose member types includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              sequence<sequence<sequence<Foo>>> c;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Sequence type "
+                      "whose element type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              (DOMString or Foo)[] d;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be an Array type "
+                      "whose element type includes its Dictionary.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary Foo {
+              Foo1 b;
+            };
+
+            dictionary Foo3 {
+              Foo d;
+            };
+
+            dictionary Foo2 : Foo3 {
+              short c;
+            };
+
+            dictionary Foo1 : Foo2 {
+              long a;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Member type must not be a Dictionary, one of whose "
+                      "members or inherited members has a type that includes "
+                      "its Dictionary.")
--- a/dom/browser-element/mochitest/browserElement_Alert.js
+++ b/dom/browser-element/mochitest/browserElement_Alert.js
@@ -10,17 +10,17 @@ var numPendingChildTests = 0;
 var iframe;
 var mm;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
   mm.addMessageListener('test-success', function(msg) {
     numPendingChildTests--;
     ok(true, SpecialPowers.wrap(msg).json);
   });
   mm.addMessageListener('test-fail', function(msg) {
--- a/dom/browser-element/mochitest/browserElement_AlertInFrame.js
+++ b/dom/browser-element/mochitest/browserElement_AlertInFrame.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     is(e.detail.message, 'Hello');
     SimpleTest.finish();
   });
 
   iframe.src = 'file_browserElement_AlertInFrame.html';
   document.body.appendChild(iframe);
--- a/dom/browser-element/mochitest/browserElement_AppFramePermission.js
+++ b/dom/browser-element/mochitest/browserElement_AppFramePermission.js
@@ -21,17 +21,17 @@ function makeAllAppsLaunchable() {
       appRegistry.allAppsLaunchable = originalValue;
     }
   }, false);
 }
 makeAllAppsLaunchable();
 
 function testAppElement(expectAnApp, callback) {
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     is(e.detail.message == 'app', expectAnApp, e.detail.message);
     SimpleTest.executeSoon(callback);
   });
   document.body.appendChild(iframe);
   iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AppFramePermission.html';
 }
--- a/dom/browser-element/mochitest/browserElement_AppWindowNamespace.js
+++ b/dom/browser-element/mochitest/browserElement_AppWindowNamespace.js
@@ -8,30 +8,30 @@ SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
   // Permission to embed an app.
   SpecialPowers.addPermission("embed-apps", true, document);
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   iframe1.setAttribute('mozapp', 'http://example.org/manifest.webapp');
 
   // Two mozapp frames for different apps with the same code both do the same
   // window.open("foo", "bar") call.  We should get two mozbrowseropenwindow
   // events.
 
   iframe1.addEventListener('mozbrowseropenwindow', function(e) {
     ok(true, "Got first mozbrowseropenwindow event.");
     document.body.appendChild(e.detail.frameElement);
 
     SimpleTest.executeSoon(function() {
       var iframe2 = document.createElement('iframe');
-      iframe2.mozbrowser = true;
+      SpecialPowers.wrap(iframe2).mozbrowser = true;
       iframe2.setAttribute('mozapp', 'http://example.com/manifest.webapp');
 
       iframe2.addEventListener('mozbrowseropenwindow', function(e) {
         ok(true, "Got second mozbrowseropenwindow event.");
         SpecialPowers.removePermission("embed-apps", document);
         SimpleTest.finish();
       });
 
--- a/dom/browser-element/mochitest/browserElement_Auth.js
+++ b/dom/browser-element/mochitest/browserElement_Auth.js
@@ -12,17 +12,17 @@ function testFail(msg) {
 
 var iframe;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   // Wait for the initial load to finish, then navigate the page, then start test
   // by loading SJS with http 401 response.
   iframe.addEventListener('mozbrowserloadend', function loadend() {
     iframe.removeEventListener('mozbrowserloadend', loadend);
     iframe.addEventListener('mozbrowserusernameandpasswordrequired', testHttpAuthCancel);
     SimpleTest.executeSoon(function() {
--- a/dom/browser-element/mochitest/browserElement_BackForward.js
+++ b/dom/browser-element/mochitest/browserElement_BackForward.js
@@ -17,17 +17,17 @@ function addOneShotIframeEventListener(e
   iframe.addEventListener(event, wrapper);
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   addOneShotIframeEventListener('mozbrowserloadend', function() {
     SimpleTest.executeSoon(test2);
   });
 
   iframe.src = browserElementTestHelpers.emptyPage1;
   document.body.appendChild(iframe);
 }
--- a/dom/browser-element/mochitest/browserElement_BadScreenshot.js
+++ b/dom/browser-element/mochitest/browserElement_BadScreenshot.js
@@ -42,17 +42,17 @@ function checkScreenshotResult(expectSuc
   };
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
   iframe.src = 'data:text/html,<html>' +
     '<body style="background:green">hello</body></html>';
 
   iframe.addEventListener('mozbrowserfirstpaint', function() {
     // This one should succeed.
     checkScreenshotResult(true, [100, 100]);
 
--- a/dom/browser-element/mochitest/browserElement_BrowserWindowNamespace.js
+++ b/dom/browser-element/mochitest/browserElement_BrowserWindowNamespace.js
@@ -9,17 +9,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
 
   // Two mozbrowser frames with the same code both do the same
   // window.open("foo", "bar") call.  We should only get one
   // mozbrowseropenwindow event.
 
   iframe1.addEventListener('mozbrowseropenwindow', function(e) {
     ok(true, "Got first mozbrowseropenwindow event.");
     document.body.appendChild(e.detail.frameElement);
@@ -31,17 +31,17 @@ function runTest() {
       }
       else {
         ok(true, "Got locationchange to " + e.detail);
       }
     });
 
     SimpleTest.executeSoon(function() {
       var iframe2 = document.createElement('iframe');
-      iframe2.mozbrowser = true;
+      SpecialPowers.wrap(iframe2).mozbrowser = true;
 
       iframe2.addEventListener('mozbrowseropenwindow', function(e) {
         ok(false, "Got second mozbrowseropenwindow event.");
       });
 
       document.body.appendChild(iframe2);
       iframe2.src = 'file_browserElement_BrowserWindowNamespace.html#2';
     });
--- a/dom/browser-element/mochitest/browserElement_Close.js
+++ b/dom/browser-element/mochitest/browserElement_Close.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener("mozbrowserclose", function(e) {
     ok(true, "got mozbrowserclose event.");
     SimpleTest.finish();
   });
 
   iframe.src = "data:text/html,<html><body><script>window.close()</scr"+"ipt></body></html>";
--- a/dom/browser-element/mochitest/browserElement_CloseApp.js
+++ b/dom/browser-element/mochitest/browserElement_CloseApp.js
@@ -17,21 +17,21 @@ function runTest() {
   });
 
   // Our app frame and browser frame load the same content.  That content calls
   // window.close() and then alert().  We should get a mozbrowserclose event on
   // the app frame before the mozbrowsershowmodalprompt, but not on the browser
   // frame.
 
   var appFrame = document.createElement('iframe');
-  appFrame.mozbrowser = true;
+  SpecialPowers.wrap(appFrame).mozbrowser = true;
   appFrame.setAttribute('mozapp', 'http://example.org/manifest.webapp');
 
   var browserFrame = document.createElement('iframe');
-  browserFrame.mozbrowser = true;
+  SpecialPowers.wrap(browserFrame).mozbrowser = true;
 
   var gotAppFrameClose = false;
   appFrame.addEventListener('mozbrowserclose', function() {
     ok(true, "Got close from app frame.");
     gotAppFrameClose = true;
   });
 
   var gotAppFrameAlert = false;
--- a/dom/browser-element/mochitest/browserElement_CloseFromOpener.js
+++ b/dom/browser-element/mochitest/browserElement_CloseFromOpener.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     ok(true, "got openwindow event.");
     document.body.appendChild(e.detail.frameElement);
 
     e.detail.frameElement.addEventListener("mozbrowserclose", function(e) {
       ok(true, "got mozbrowserclose event.");
       SimpleTest.finish();
--- a/dom/browser-element/mochitest/browserElement_ContextmenuEvents.js
+++ b/dom/browser-element/mochitest/browserElement_ContextmenuEvents.js
@@ -29,17 +29,17 @@ var trigger1 = function() {
 };
 
 function runTest() {
 
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(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">' +
--- a/dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
+++ b/dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
@@ -9,17 +9,17 @@ SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   const innerPage = 'http://example.com/tests/dom/browser-element/mochitest/file_browserElement_CookiesNotThirdParty.html';
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     if (e.detail.message == 'next') {
       iframe.src = innerPage + '?step=2';
       return;
     }
 
     if (e.detail.message.startsWith('success:')) {
--- a/dom/browser-element/mochitest/browserElement_DOMRequestError.js
+++ b/dom/browser-element/mochitest/browserElement_DOMRequestError.js
@@ -7,17 +7,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   iframe1.src = 'data:text/html,<html>' +
     '<body style="background:green">hello</body></html>';
   document.body.appendChild(iframe1);
 
   function testIframe(beforeRun, isErrorExpected, nextTest) {
     return function() {
       var error = false;
       if (beforeRun)
--- a/dom/browser-element/mochitest/browserElement_DataURI.js
+++ b/dom/browser-element/mochitest/browserElement_DataURI.js
@@ -6,17 +6,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   iframe1.id = 'iframe1';
   iframe1.addEventListener('mozbrowserloadend', function if1_loadend() {
     iframe1.removeEventListener('mozbrowserloadend', if1_loadend);
     ok(true, 'Got first loadend event.');
     SimpleTest.executeSoon(runTest2);
   });
   iframe1.src = browserElementTestHelpers.emptyPage1;
   document.body.appendChild(iframe1);
--- a/dom/browser-element/mochitest/browserElement_DocumentFirstPaint.js
+++ b/dom/browser-element/mochitest/browserElement_DocumentFirstPaint.js
@@ -57,14 +57,14 @@ function testFirstLoad() {
   iframe.src = browserElementTestHelpers.emptyPage1;
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   runTestQueue([testFirstLoad, testReload, testChangeLocation]);
 }
 
 runTest();
--- a/dom/browser-element/mochitest/browserElement_ErrorSecurity.js
+++ b/dom/browser-element/mochitest/browserElement_ErrorSecurity.js
@@ -7,17 +7,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener("mozbrowsererror", function(e) {
     ok(true, "Got mozbrowsererror event.");
     ok(e.detail.type, "Event's detail has a |type| param.");
     SimpleTest.finish();
   });
 
   iframe.src = "https://expired.example.com";
--- a/dom/browser-element/mochitest/browserElement_ExposableURI.js
+++ b/dom/browser-element/mochitest/browserElement_ExposableURI.js
@@ -44,14 +44,14 @@ function testWyciwyg() {
   iframe.addEventListener('mozbrowserlocationchange', locationchange);
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
   testWyciwyg();
 }
 
 addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
--- a/dom/browser-element/mochitest/browserElement_FirstPaint.js
+++ b/dom/browser-element/mochitest/browserElement_FirstPaint.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var gotFirstPaint = false;
   var gotFirstLocationChange = false;
   iframe.addEventListener('mozbrowserfirstpaint', function(e) {
     ok(!gotFirstPaint, "Got only one first paint.");
     gotFirstPaint = true;
 
     if (gotFirstLocationChange) {
--- a/dom/browser-element/mochitest/browserElement_ForwardName.js
+++ b/dom/browser-element/mochitest/browserElement_ForwardName.js
@@ -8,17 +8,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.setAttribute('name', 'foo');
 
   iframe.addEventListener("mozbrowseropenwindow", function(e) {
     ok(false, 'Got mozbrowseropenwindow, but should not have.');
   });
 
   iframe.addEventListener('mozbrowserlocationchange', function(e) {
     ok(true, "Got locationchange to " + e.detail);
--- a/dom/browser-element/mochitest/browserElement_FrameWrongURI.js
+++ b/dom/browser-element/mochitest/browserElement_FrameWrongURI.js
@@ -6,31 +6,31 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframeJS = document.createElement('iframe');
-  iframeJS.mozbrowser = true;
+  SpecialPowers.wrap(iframeJS).mozbrowser = true;
 
   iframeJS.addEventListener('mozbrowserloadstart', function(e) {
     ok(false, "This should not happen!");
   });
 
   iframeJS.addEventListener('mozbrowserloadend', function(e) {
     ok(false, "This should not happen!");
   });
 
   iframeJS.src = 'javascript:alert("Foo");';
   document.body.appendChild(iframeJS);
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var gotPopup = false;
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     is(gotPopup, false, 'Should get just one popup.');
     gotPopup = true;
 
     document.body.appendChild(e.detail.frameElement);
   });
--- a/dom/browser-element/mochitest/browserElement_GetScreenshot.js
+++ b/dom/browser-element/mochitest/browserElement_GetScreenshot.js
@@ -7,17 +7,17 @@
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
 
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   document.body.appendChild(iframe1);
   iframe1.src = 'data:text/html,<html>' +
     '<body style="background:green">hello</body></html>';
 
   var screenshotArrayBuffers = [];
   var numLoaded = 0;
 
   function screenshotTaken(screenshotArrayBuffer) {
--- a/dom/browser-element/mochitest/browserElement_Iconchange.js
+++ b/dom/browser-element/mochitest/browserElement_Iconchange.js
@@ -14,24 +14,24 @@ function createLink(name) {
   return '<link rel="icon" type="image/png" href="http://example.com/' + name + '.png">';
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   document.body.appendChild(iframe1);
 
   // iframe2 is a red herring; we modify its favicon but don't listen for
   // iconchanges; we want to make sure that its iconchange events aren't
   // picked up by the listener on iframe1.
   var iframe2 = document.createElement('iframe');
-  iframe2.mozbrowser = true;
+  SpecialPowers.wrap(iframe2).mozbrowser = true;
   document.body.appendChild(iframe2);
 
   // iframe3 is another red herring.  It's not a mozbrowser, so we shouldn't
   // get any iconchange events on it.
   var iframe3 = document.createElement('iframe');
   document.body.appendChild(iframe3);
 
   var numIconChanges = 0;
--- a/dom/browser-element/mochitest/browserElement_KeyEvents.js
+++ b/dom/browser-element/mochitest/browserElement_KeyEvents.js
@@ -18,17 +18,17 @@ let whitelistedEvents = [
 
 SimpleTest.waitForExplicitFinish();
 
 browserElementTestHelpers.setEnabledPref(true);
 browserElementTestHelpers.addPermission();
 browserElementTestHelpers.setOOPDisabledPref(true); // this is breaking the autofocus.
 
 var iframe = document.createElement('iframe');
-iframe.mozbrowser = true;
+SpecialPowers.wrap(iframe).mozbrowser = true;
 iframe.src = browserElementTestHelpers.focusPage;
 document.body.appendChild(iframe);
 
 // Number of expected events at which point we will consider the test as done.
 var nbEvents = whitelistedEvents.length * 3;
 
 function eventHandler(e) {
   ok(((e.type == 'keydown' || e.type == 'keypress' || e.type == 'keyup') &&
--- a/dom/browser-element/mochitest/browserElement_LoadEvents.js
+++ b/dom/browser-element/mochitest/browserElement_LoadEvents.js
@@ -16,17 +16,17 @@ function runTest() {
   //
   // This should trigger loadstart, locationchange, and loadend events.
 
   var seenLoadEnd = false;
   var seenLoadStart = false;
   var seenLocationChange = false;
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.id = 'iframe';
   iframe.src = browserElementTestHelpers.emptyPage1;
 
   function loadstart(e) {
     ok(e.isTrusted, 'Event should be trusted.');
     ok(!seenLoadEnd, 'loadstart before loadend.');
     ok(!seenLoadStart, 'Just one loadstart event.');
     ok(!seenLocationChange, 'loadstart before locationchange.');
--- a/dom/browser-element/mochitest/browserElement_NextPaint.js
+++ b/dom/browser-element/mochitest/browserElement_NextPaint.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   // Add a first listener that we'll remove shortly after.
   iframe.addNextPaintListener(wrongListener);
 
   var gotFirstNextPaintEvent = false;
   iframe.addNextPaintListener(function () {
     ok(!gotFirstNextPaintEvent, 'got the first nextpaint event');
--- a/dom/browser-element/mochitest/browserElement_OpenMixedProcess.js
+++ b/dom/browser-element/mochitest/browserElement_OpenMixedProcess.js
@@ -24,17 +24,17 @@ function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   // We're going to open a remote frame if OOP off by default.  If OOP is on by
   // default, we're going to open an in-process frame.
   var remote = !browserElementTestHelpers.getOOPByDefaultPref();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.setAttribute('remote', remote);
 
   // The page we load does window.open, then checks some things and reports
   // back using alert().  Finally, it calls alert('finish').
   //
   // Bug 776129 in particular manifests itself such that the popup frame loads
   // and the tests in file_browserElement_OpenMixedProcess pass, but the
   // content of the frame is invisible.  To catch this case, we take a
--- a/dom/browser-element/mochitest/browserElement_OpenNamed.js
+++ b/dom/browser-element/mochitest/browserElement_OpenNamed.js
@@ -9,17 +9,17 @@ SimpleTest.waitForExplicitFinish();
 
 var iframe;
 var popupFrame;
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var gotPopup = false;
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     is(gotPopup, false, 'Should get just one popup.');
     gotPopup = true;
     popupFrame = e.detail.frameElement;
     is(popupFrame.getAttribute('name'), 'OpenNamed');
 
--- a/dom/browser-element/mochitest/browserElement_OpenWindow.js
+++ b/dom/browser-element/mochitest/browserElement_OpenWindow.js
@@ -6,17 +6,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var gotPopup = false;
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     is(gotPopup, false, 'Should get just one popup.');
     gotPopup = true;
 
     document.body.appendChild(e.detail.frameElement);
 
--- a/dom/browser-element/mochitest/browserElement_OpenWindowDifferentOrigin.js
+++ b/dom/browser-element/mochitest/browserElement_OpenWindowDifferentOrigin.js
@@ -6,17 +6,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     ok(true, 'Got first window.open call');
 
     e.detail.frameElement.addEventListener('mozbrowseropenwindow', function(e) {
       ok(true, 'Got second window.open call');
       document.body.appendChild(e.detail.frameElement);
     });
--- a/dom/browser-element/mochitest/browserElement_OpenWindowInFrame.js
+++ b/dom/browser-element/mochitest/browserElement_OpenWindowInFrame.js
@@ -12,17 +12,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var gotPopup = false;
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     is(gotPopup, false, 'Should get just one popup.');
     gotPopup = true;
 
     document.body.appendChild(e.detail.frameElement);
 
--- a/dom/browser-element/mochitest/browserElement_OpenWindowRejected.js
+++ b/dom/browser-element/mochitest/browserElement_OpenWindowRejected.js
@@ -7,17 +7,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     ok(e.detail.url.indexOf('does_not_exist.html') != -1,
        'Opened URL; got ' + e.detail.url);
     is(e.detail.name, '');
     is(e.detail.features, '');
 
     // Don't add e.detail.frameElement to the DOM, so the window.open is
--- a/dom/browser-element/mochitest/browserElement_OpenWindowRejected2.js
+++ b/dom/browser-element/mochitest/browserElement_OpenWindowRejected2.js
@@ -11,17 +11,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     ok(e.detail.url.indexOf('does_not_exist.html') != -1,
        'Opened URL; got ' + e.detail.url);
     is(e.detail.name, '');
     is(e.detail.features, '');
 
     // Call preventDefault, but don't add the iframe to the DOM.  This still
--- a/dom/browser-element/mochitest/browserElement_PromptCheck.js
+++ b/dom/browser-element/mochitest/browserElement_PromptCheck.js
@@ -21,17 +21,17 @@ var oldDialogTimeLimitPref;
 try {
   oldDialogTimeLimitPref = SpecialPowers.getIntPref(dialogTimeLimitPrefName);
 }
 catch(e) {}
 
 SpecialPowers.setIntPref(dialogTimeLimitPrefName, 10);
 
 var iframe = document.createElement('iframe');
-iframe.mozbrowser = true;
+SpecialPowers.wrap(iframe).mozbrowser = true;
 document.body.appendChild(iframe);
 
 var numPrompts = 0;
 iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
   is(e.detail.message, numPrompts, "prompt message");
   if (numPrompts / 10 < 1) {
     is(e.detail.promptType, 'alert');
   }
--- a/dom/browser-element/mochitest/browserElement_PromptConfirm.js
+++ b/dom/browser-element/mochitest/browserElement_PromptConfirm.js
@@ -10,17 +10,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   var prompts = [
     {msg: 1, type: 'alert', rv: 42, expected: 'undefined'},
     {msg: 2, type: 'confirm', rv: true, expected: 'true'},
     {msg: 3, type: 'confirm', rv: false, expected: 'false'},
 
     // rv == 42 should be coerced to 'true' for confirm.
--- a/dom/browser-element/mochitest/browserElement_PurgeHistory.js
+++ b/dom/browser-element/mochitest/browserElement_PurgeHistory.js
@@ -17,17 +17,17 @@ function addOneShotIframeEventListener(e
   iframe.addEventListener(event, wrapper);
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   addOneShotIframeEventListener('mozbrowserloadend', function() {
     SimpleTest.executeSoon(test2);
   });
 
   iframe.src = browserElementTestHelpers.emptyPage1;
   document.body.appendChild(iframe);
 }
--- a/dom/browser-element/mochitest/browserElement_Reload.js
+++ b/dom/browser-element/mochitest/browserElement_Reload.js
@@ -17,17 +17,17 @@ var iframe;
 var loadedEvents = 0;
 var countAcc;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowserloadend', mozbrowserLoaded);
 
   iframe.src = 'file_bug741717.sjs';
   document.body.appendChild(iframe);
 }
 
 function iframeBodyRecv(data) {
--- a/dom/browser-element/mochitest/browserElement_ReloadPostRequest.js
+++ b/dom/browser-element/mochitest/browserElement_ReloadPostRequest.js
@@ -71,17 +71,17 @@ function pageLoadDone() {
   iframe.reload();
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   isPostRequestSubmitted = false;
   iframe.src = 'file_post_request.html';
   document.body.appendChild(iframe);
 
   iframe.addEventListener('mozbrowserloadend', pageLoadDone);
 
   let expectedMessage = getExpectedStrings();
--- a/dom/browser-element/mochitest/browserElement_RemoveBrowserElement.js
+++ b/dom/browser-element/mochitest/browserElement_RemoveBrowserElement.js
@@ -8,17 +8,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener("mozbrowsershowmodalprompt", function(e) {
     document.body.removeChild(iframe);
     SimpleTest.executeSoon(function() {
       ok(true);
       SimpleTest.finish();
     });
--- a/dom/browser-element/mochitest/browserElement_ScrollEvent.js
+++ b/dom/browser-element/mochitest/browserElement_ScrollEvent.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener("mozbrowserscroll", function(e) {
     ok(true, "got mozbrowserscroll event.");
     ok(e.detail, "event.detail is not null.");
     ok(e.detail.top === 4000, "top position is correct.");
     ok(e.detail.left === 4000, "left position is correct.");
     SimpleTest.finish();
--- a/dom/browser-element/mochitest/browserElement_SecurityChange.js
+++ b/dom/browser-element/mochitest/browserElement_SecurityChange.js
@@ -7,17 +7,17 @@
 "use strict";
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   var lastSecurityState;
   iframe.addEventListener('mozbrowsersecuritychange', function(e) {
     lastSecurityState = e.detail;
   });
 
   var filepath = 'tests/dom/browser-element/mochitest/file_browserElement_SecurityChange.html';
 
--- a/dom/browser-element/mochitest/browserElement_SendEvent.js
+++ b/dom/browser-element/mochitest/browserElement_SendEvent.js
@@ -6,17 +6,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement("iframe");
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener("mozbrowserloadend", function onloadend(e) {
     iframe.sendMouseEvent("mousedown", 10, 10, 0, 1, 0);
   });
 
   iframe.addEventListener("mozbrowserlocationchange", function onlocchange(e) {
     var a = document.createElement("a");
--- a/dom/browser-element/mochitest/browserElement_SetVisible.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisible.js
@@ -17,17 +17,17 @@ var iframeScript = function() {
 function runTest() {
 
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var mm;
   var numEvents = 0;
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   iframe1.src = 'data:text/html,1';
 
   document.body.appendChild(iframe1);
 
   function recvVisibilityChanged(msg) {
     msg = SpecialPowers.wrap(msg);
     numEvents++;
     if (numEvents === 1) {
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
@@ -19,17 +19,17 @@ function runTest() {
   browserElementTestHelpers.addPermission();
 
   var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
   SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
                                                  appId: principal.appId,
                                                  isInBrowserElement: true });
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   // Our test involves three <iframe mozbrowser>'s, parent, child1, and child2.
   // child1 and child2 are contained inside parent.  child1 is visibile, and
   // child2 is not.
   //
   // For the purposes of this test, we want there to be a process barrier
   // between child{1,2} and parent.  Therefore parent must be a non-remote
   // <iframe mozbrowser>, until bug 761935 is resolved and we can have nested
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
@@ -13,17 +13,17 @@ function runTest() {
   browserElementTestHelpers.addPermission();
 
   var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
   SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
                                                  appId: principal.appId,
                                                  isInBrowserElement: true });
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   // We need remote = false here until bug 761935 is fixed; see
   // SetVisibleFrames.js for an explanation.
   iframe.remote = false;
 
   iframe.addEventListener('mozbrowserloadend', function loadEnd(e) {
     iframe.removeEventListener('mozbrowserloadend', loadEnd);
     iframe.setVisible(false);
--- a/dom/browser-element/mochitest/browserElement_Stop.js
+++ b/dom/browser-element/mochitest/browserElement_Stop.js
@@ -15,17 +15,17 @@ var iframe;
 var stopped = false;
 var imgSrc = 'http://test/tests/dom/browser-element/mochitest/file_bug709759.sjs';
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowserloadend', loadend);
   iframe.src = 'data:text/html,<html>' +
     '<body><img src="' + imgSrc + '" /></body></html>';
 
   document.body.appendChild(iframe);
 
   setTimeout(function() {
--- a/dom/browser-element/mochitest/browserElement_TargetBlank.js
+++ b/dom/browser-element/mochitest/browserElement_TargetBlank.js
@@ -7,17 +7,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     is(e.detail.url, 'http://example.com/');
     SimpleTest.finish();
   });
 
   iframe.src = "file_browserElement_TargetBlank.html";
   document.body.appendChild(iframe);
--- a/dom/browser-element/mochitest/browserElement_TargetTop.js
+++ b/dom/browser-element/mochitest/browserElement_TargetTop.js
@@ -7,17 +7,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   iframe.addEventListener('mozbrowseropenwindow', function(e) {
     ok(false, 'Not expecting an openwindow event.');
   });
 
   iframe.addEventListener('mozbrowserlocationchange', function(e) {
     if (/file_browserElement_TargetTop.html\?2$/.test(e.detail)) {
       ok(true, 'Got the locationchange we were looking for.');
--- a/dom/browser-element/mochitest/browserElement_Titlechange.js
+++ b/dom/browser-element/mochitest/browserElement_Titlechange.js
@@ -6,24 +6,24 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe1 = document.createElement('iframe');
-  iframe1.mozbrowser = true;
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
   document.body.appendChild(iframe1);
 
   // iframe2 is a red herring; we modify its title but don't listen for
   // titlechanges; we want to make sure that its titlechange events aren't
   // picked up by the listener on iframe1.
   var iframe2 = document.createElement('iframe');
-  iframe2.mozbrowser = true;
+  SpecialPowers.wrap(iframe2).mozbrowser = true;
   document.body.appendChild(iframe2);
 
   // iframe3 is another red herring.  It's not a mozbrowser, so we shouldn't
   // get any titlechange events on it.
   var iframe3 = document.createElement('iframe');
   document.body.appendChild(iframe3);
 
   var numTitleChanges = 0;
--- a/dom/browser-element/mochitest/browserElement_TopBarrier.js
+++ b/dom/browser-element/mochitest/browserElement_TopBarrier.js
@@ -12,17 +12,17 @@ function runTest() {
   iframe = document.createElement('iframe');
   iframe.addEventListener('mozbrowserloadend', function() {
     try {
       outerIframeLoaded();
     } catch(e) {
       dump("Got error: " + e + '\n');
     }
   });
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.src = 'data:text/html,Outer iframe <iframe id="inner-iframe"></iframe>';
   // For kicks, this test uses a display:none iframe.  This shouldn't make a
   // difference in anything.
   iframe.style.display = 'none';
   document.body.appendChild(iframe);
 
   SimpleTest.waitForExplicitFinish();
 }
--- a/dom/browser-element/mochitest/browserElement_XFrameOptions.js
+++ b/dom/browser-element/mochitest/browserElement_XFrameOptions.js
@@ -7,17 +7,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   // The page we load will fire an alert when it successfully loads.
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     ok(true, "Got alert");
     SimpleTest.finish();
   });
 
   document.body.appendChild(iframe);
--- a/dom/browser-element/mochitest/browserElement_XFrameOptionsAllowFrom.js
+++ b/dom/browser-element/mochitest/browserElement_XFrameOptionsAllowFrom.js
@@ -25,17 +25,17 @@ function arrayBuffersEqual(a, b) {
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
   var count = 0;
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   iframe.height = '1000px';
 
   // The innermost page we load will fire an alert when it successfully loads.
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     switch (e.detail.message) {
     case 'step 1':
       // Make the page wait for us to unblock it (which we do after we finish
       // taking the screenshot).
--- a/dom/browser-element/mochitest/browserElement_XFrameOptionsDeny.js
+++ b/dom/browser-element/mochitest/browserElement_XFrameOptionsDeny.js
@@ -25,17 +25,17 @@ function arrayBuffersEqual(a, b) {
   return true;
 }
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   // Our child will create two iframes, so make sure this iframe is big enough
   // to show both of them without scrolling, so taking a screenshot gets both
   // frames.
   iframe.height = '1000px';
 
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     switch (e.detail.message) {
--- a/dom/browser-element/mochitest/browserElement_XFrameOptionsSameOrigin.js
+++ b/dom/browser-element/mochitest/browserElement_XFrameOptionsSameOrigin.js
@@ -9,17 +9,17 @@
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
 
   // The innermost page we load will fire an alert when it successfully loads.
   iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
     ok(true, "Got alert");
     SimpleTest.finish();
   });
 
   document.body.appendChild(iframe);
--- a/dom/browser-element/mochitest/createNewTest.py
+++ b/dom/browser-element/mochitest/createNewTest.py
@@ -36,17 +36,17 @@ js_template = textwrap.dedent("""\
 
     SimpleTest.waitForExplicitFinish();
 
     function runTest() {{
       browserElementTestHelpers.setEnabledPref(true);
       browserElementTestHelpers.addPermission();
 
       var iframe = document.createElement('iframe');
-      iframe.mozbrowser = true;
+      SpecialPowers.wrap(iframe).mozbrowser = true;
 
       // FILL IN TEST
 
       document.body.appendChild(iframe);
     }}
 
     runTest();""")
 
--- a/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames2_Outer.html
+++ b/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames2_Outer.html
@@ -1,14 +1,14 @@
 <html>
 <body>
 <script>
 
 var iframe = document.createElement('iframe');
-iframe.mozbrowser = true;
+iframe.setAttribute("mozbrowser", "true");
 
 iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
   if (e.detail.message == 'child:ready') {
     setTimeout(function() {
       iframe.setVisible(false);
       iframe.setVisible(true);
       setTimeout(function() {
         alert('parent:finish');
--- a/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html
+++ b/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html
@@ -22,21 +22,21 @@ function handlePrompt(e) {
     alert('parent:ready');
   }
   else if (numPrompts == 4 || numPrompts == 5) {
     alert(e.detail.message);
   }
 }
 
 var iframe1 = document.createElement('iframe');
-iframe1.mozbrowser = true;
+iframe1.setAttribute("mozbrowser", "true");
 iframe1.addEventListener('mozbrowsershowmodalprompt', handlePrompt);
 
 var iframe2 = document.createElement('iframe');
-iframe2.mozbrowser = true;
+iframe2.setAttribute("mozbrowser", "true");
 iframe2.addEventListener('mozbrowsershowmodalprompt', handlePrompt);
 
 iframe1.src = 'file_browserElement_SetVisibleFrames_Inner.html?child1';
 iframe2.src = 'file_browserElement_SetVisibleFrames_Inner.html?child2';
 document.body.appendChild(iframe1);
 document.body.appendChild(iframe2);
 
 </script>
--- a/dom/browser-element/mochitest/test_browserElement_NoPref.html
+++ b/dom/browser-element/mochitest/test_browserElement_NoPref.html
@@ -21,17 +21,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(false);
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener('mozbrowserloadstart', function() {
     ok(false, 'Should not send mozbrowserloadstart event.');
   });
 
   iframe.addEventListener('load', function() {
     ok(true, 'Got iframe load event.');
--- a/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html
+++ b/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html
@@ -22,17 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermissionForUrl('http://foobar.com');
 
   var iframe = document.createElement('iframe');
-  iframe.mozbrowser = true;
+  SpecialPowers.wrap(iframe).mozbrowser = true;
   document.body.appendChild(iframe);
 
   iframe.addEventListener('mozbrowserloadstart', function() {
     ok(false, 'Should not send mozbrowserloadstart event.');
   });
 
   iframe.addEventListener('load', function() {
     ok(true, 'Got iframe load event.');
--- a/dom/devicestorage/ipc/test_ipc.html
+++ b/dom/devicestorage/ipc/test_ipc.html
@@ -84,17 +84,17 @@
       mm.removeMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage);
       mm.removeMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete);
 
       SimpleTest.executeSoon(function () { SimpleTest.finish(); });
     }
 
     function runTests() {
       let iframe = document.createElement("iframe");
-      iframe.mozbrowser = true;
+      SpecialPowers.wrap(iframe).mozbrowser = true;
       iframe.id = "iframe";
       iframe.style.width = "100%";
       iframe.style.height = "1000px";
 
       function iframeLoadSecond() {
         ok(true, "Got second iframe load event.");
         iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
         let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/encoding/labelsencodings.properties
+++ b/dom/encoding/labelsencodings.properties
@@ -184,17 +184,17 @@ gb2312=gbk
 gb_2312=gbk
 gb_2312-80=gbk
 gbk=gbk
 iso-ir-58=gbk
 x-gbk=gbk
 gb18030=gb18030
 hz-gb-2312=HZ-GB-2312
 big5=Big5
-big5-hkscs=Big5
+big5-hkscs=Big5-HKSCS
 cn-big5=Big5
 csbig5=Big5
 x-x-big5=Big5
 cseucpkdfmtjapanese=EUC-JP
 euc-jp=EUC-JP
 x-euc-jp=EUC-JP
 csiso2022jp=ISO-2022-JP
 iso-2022-jp=ISO-2022-JP
--- a/dom/encoding/test/test_TextDecoder.js
+++ b/dom/encoding/test/test_TextDecoder.js
@@ -337,17 +337,18 @@ function testDecoderGetEncoding()
     {encoding: "windows-1255", labels: ["cp1255", "windows-1255", "x-cp1255"]},
     {encoding: "windows-1256", labels: ["cp1256", "windows-1256", "x-cp1256"]},
     {encoding: "windows-1257", labels: ["cp1257", "windows-1257", "x-cp1257"]},
     {encoding: "windows-1258", labels: ["cp1258", "windows-1258", "x-cp1258"]},
     {encoding: "x-mac-cyrillic", labels: ["x-mac-cyrillic", "x-mac-ukrainian"]},
     {encoding: "gbk", labels: ["chinese", "csgb2312", "csiso58gb231280", "gb2312", "gb_2312", "gb_2312-80", "gbk", "iso-ir-58", "x-gbk"]},
     {encoding: "gb18030", labels: ["gb18030"]},
     {encoding: "hz-gb-2312", labels: ["hz-gb-2312"]},
-    {encoding: "big5", labels: ["big5", "big5-hkscs", "cn-big5", "csbig5", "x-x-big5"]},
+    {encoding: "big5", labels: ["big5", "cn-big5", "csbig5", "x-x-big5"]},
+    {encoding: "big5-hkscs", labels: ["big5-hkscs"]},
     {encoding: "euc-jp", labels: ["cseucpkdfmtjapanese", "euc-jp", "x-euc-jp"]},
     {encoding: "iso-2022-jp", labels: ["csiso2022jp", "iso-2022-jp"]},
     {encoding: "shift_jis", labels: ["csshiftjis", "ms_kanji", "shift-jis", "shift_jis", "sjis", "windows-31j", "x-sjis"]},
     {encoding: "euc-kr", labels: ["cseuckr", "csksc56011987", "euc-kr", "iso-ir-149", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601", "ksc_5601", "windows-949"]},
     {encoding: "iso-2022-kr", labels: ["csiso2022kr", "iso-2022-kr"]},
     {encoding: "utf-16le", labels: ["utf-16", "utf-16le"]},
     {encoding: "utf-16be", labels: ["utf-16be"]},
     {encoding: "x-user-defined", labels: ["x-user-defined"]},
--- a/dom/indexedDB/ipc/test_ipc.html
+++ b/dom/indexedDB/ipc/test_ipc.html
@@ -103,17 +103,17 @@
       is(usingChildProcess, true, "Expecting to run in child process");
       is(seenTestMessage, true, "Expecting to receive messages from child");
       SpecialPowers.removePermission("browser", window.location.href);
       SimpleTest.executeSoon(function () { SimpleTest.finish(); });
     }
 
     function runTests() {
       let iframe = document.createElement("iframe");
-      iframe.mozbrowser = true;
+      SpecialPowers.wrap(iframe).mozbrowser = true;
       iframe.id = "iframe";
       iframe.style.width = "100%";
       iframe.style.height = "1000px";
 
       function iframeLoadSecond() {
         ok(true, "Got second iframe load event.");
         iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
         let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -109,16 +109,18 @@
 using namespace mozilla::system;
 #endif
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothParent.h"
 #include "BluetoothService.h"
 #endif
 
+#include "Crypto.h"
+
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
@@ -2163,16 +2165,32 @@ ContentParent::RecvShowFilePicker(const 
         file->GetPath(filePath);
         files->AppendElement(filePath);
     }
 
     return true;
 }
 
 bool
+ContentParent::RecvGetRandomValues(const uint32_t& length,
+                                   InfallibleTArray<uint8_t>* randomValues)
+{
+    uint8_t* buf = Crypto::GetRandomValues(length);
+
+    randomValues->SetCapacity(length);
+    randomValues->SetLength(length);
+
+    memcpy(randomValues->Elements(), buf, length);
+
+    NS_Free(buf);
+
+    return true;
+}
+
+bool
 ContentParent::RecvLoadURIExternal(const URIParams& uri)
 {
     nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
     if (!extProtService) {
         return true;
     }
     nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
     if (!ourURI) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -245,16 +245,19 @@ private:
 
     virtual PCrashReporterParent* AllocPCrashReporter(const NativeThreadId& tid,
                                                       const uint32_t& processType);
     virtual bool DeallocPCrashReporter(PCrashReporterParent* crashreporter);
     virtual bool RecvPCrashReporterConstructor(PCrashReporterParent* actor,
                                                const NativeThreadId& tid,
                                                const uint32_t& processType);
 
+    virtual bool RecvGetRandomValues(const uint32_t& length,
+                                     InfallibleTArray<uint8_t>* randomValues);
+
     virtual PHalParent* AllocPHal() MOZ_OVERRIDE;
     virtual bool DeallocPHal(PHalParent*) MOZ_OVERRIDE;
 
     virtual PIndexedDBParent* AllocPIndexedDB();
 
     virtual bool DeallocPIndexedDB(PIndexedDBParent* aActor);
 
     virtual bool
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -380,16 +380,19 @@ parent:
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     sync GetXPCOMProcessAttributes()
         returns (bool isOffline);
 
     PDeviceStorageRequest(DeviceStorageParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
+    sync GetRandomValues(uint32_t length)
+        returns (uint8_t[] randomValues);
+
     PHal();
 
     PIndexedDB();
 
     PNecko();
 
     PSms();
     
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -90,27 +90,16 @@ GetContentChildID()
   if (!contentChild) {
     return 0;
   }
 
   return contentChild->GetID();
 }
 
 /**
- * Determine if the priority is a backround priority.
- */
-bool
-IsBackgroundPriority(ProcessPriority aPriority)
-{
-  return (aPriority == PROCESS_PRIORITY_BACKGROUND ||
-          aPriority == PROCESS_PRIORITY_BACKGROUND_HOMESCREEN ||
-          aPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE);
-}
-
-/**
  * This class listens to various Gecko events and asks the hal back-end to
  * change this process's priority when it transitions between various states of
  * "importance".
  *
  * The process's priority determines its CPU priority and also how likely it is
  * to be killed when the system is running out of memory.
  *
  * The most basic dichotomy in the ProcessPriorityManager is between
@@ -156,31 +145,22 @@ public:
    * ProcessPriorityManager.h::TemporarilyLockProcessPriority().
    */
   void TemporarilyLockProcessPriority();
 
   /**
    * Recompute this process's priority and apply it, potentially after a brief
    * delay.
    *
-   * If we are transitioning to a priority that is "lower" than the current
-   * priority (as defined below), that transition happens after a grace period.
-   * Otherwise the transition happens immediately.
-   *
-   * For the purposes of deciding whether to apply a grace period, the
-   * hierarchy of priorities is
+   * If we are transitioning to a priority that is lower than the current
+   * priority, that transition happens after a grace period.  Otherwise the
+   * transition happens immediately.
    *
-   *  - UNKNOWN
-   *  - FOREGROUND_HIGH
-   *  - FOREGROUND
-   *  - BACKGROUND*
-   *
-   * So for example, a transition between any two BACKGROUND* priorites happens
-   * immediately, but a transition from UNKNOWN to FOREGROUND_HIGH happens
-   * after a grace period.
+   * Note that PROCESS_PRIORITY_UNKNOWN is considered the highest priority.
+   * Going from UNKNOWN to any other priority requires a grace period.
    */
   void ResetPriority();
 
   /**
    * Recompute this process's priority and apply it immediately.
    */
   void ResetPriorityNow();
 
@@ -189,57 +169,37 @@ private:
 
   /**
    * Is this process a "critical" process that's holding the "CPU" or
    * "high-priority" wake lock?
    */
   bool IsCriticalProcessWithWakeLock();
 
   /**
-   * If this process were in the foreground, what priority would it have?
-   */
-  ProcessPriority GetForegroundPriority();
-
-  /**
-   * If this process were in the foreground, what priority would it have?
-   */
-  ProcessPriority GetBackgroundPriority();
-
-  /**
    * Compute whether this process is in the foreground and return the result.
    */
   bool ComputeIsInForeground();
 
-
   /**
-   * Set this process's priority to the appropriate FOREGROUND* priority
-   * immediately if we're upgrading its priority, and after a grace period if
-   * we're downgrading it or if the current priority is unknown.
+   * Compute the priority this process ought to have, based on what we know at
+   * the moment.
    */
-  void SetIsForeground();
+  ProcessPriority ComputePriority();
 
   /**
-   * Set this process's priority to the appropriate FOREGROUND* priority
-   * immediately.
+   * Immediately set this process's priority to the given priority.
    */
-  void SetIsForegroundNow();
-
-  /**
-   * Set this process's priority to the appropriate BACKGROUND* priority
-   * immediately.
-   */
-  void SetIsBackgroundNow();
+  void SetPriorityNow(ProcessPriority aPriority);
 
   /**
    * If mResetPriorityTimer is null (i.e., not running), create a timer and set
    * it to invoke ResetPriorityNow() after
    * dom.ipc.processPriorityManager.aTimeoutPref ms.
    */
-  void
-  ScheduleResetPriority(const char* aTimeoutPref);
+  void ScheduleResetPriority(const char* aTimeoutPref);
 
   // Tracks whether this process holds the "cpu" lock.
   bool mHoldsCPUWakeLock;
 
   // Tracks whether this process holds the "high-priority" lock.
   bool mHoldsHighPriorityWakeLock;
 
   // mProcessPriority tracks the priority we've given this process in hal.
@@ -425,90 +385,45 @@ ProcessPriorityManager::IsCriticalProces
     if (appType.EqualsLiteral("critical")) {
       return true;
     }
   }
 
   return false;
 }
 
-ProcessPriority
-ProcessPriorityManager::GetForegroundPriority()
-{
-  return IsCriticalProcessWithWakeLock() ? PROCESS_PRIORITY_FOREGROUND_HIGH :
-                                           PROCESS_PRIORITY_FOREGROUND;
-}
-
-/**
- * Get the appropriate backround priority for this process.
- */
-ProcessPriority
-ProcessPriorityManager::GetBackgroundPriority()
-{
-  AudioChannelService* service = AudioChannelService::GetAudioChannelService();
-  if (service->ContentOrNormalChannelIsActive()) {
-    return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
-  }
-
-  bool isHomescreen = false;
-
-  ContentChild* contentChild = ContentChild::GetSingleton();
-  if (contentChild) {
-    const InfallibleTArray<PBrowserChild*>& browsers =
-      contentChild->ManagedPBrowserChild();
-    for (uint32_t i = 0; i < browsers.Length(); i++) {
-      nsAutoString appType;
-      static_cast<TabChild*>(browsers[i])->GetAppType(appType);
-      if (appType.EqualsLiteral("homescreen")) {
-        isHomescreen = true;
-        break;
-      }
-    }
-  }
-
-  return isHomescreen ?
-         PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
-         PROCESS_PRIORITY_BACKGROUND;
-}
-
-
 void
 ProcessPriorityManager::ResetPriority()
 {
   if (!mObservedTabChildCreated) {
     LOG("ResetPriority bailing because we haven't observed "
         "a tab-child-created event.");
     return;
   }
 
-  if (ComputeIsInForeground()) {
-    SetIsForeground();
-  } else if (IsBackgroundPriority(mProcessPriority)) {
-    // If we're already in the background, recompute our background priority
-    // and set it immediately.
-    SetIsBackgroundNow();
-  } else {
+  ProcessPriority processPriority = ComputePriority();
+  if (mProcessPriority == PROCESS_PRIORITY_UNKNOWN ||
+      mProcessPriority > processPriority) {
     ScheduleResetPriority("backgroundGracePeriodMS");
+    return;
   }
+
+  SetPriorityNow(processPriority);
 }
 
 void
 ProcessPriorityManager::ResetPriorityNow()
 {
   if (!mObservedTabChildCreated) {
     LOG("ResetPriorityNow bailing because we haven't observed "
         "a tab-child-created event.");
     return;
   }
 
-  if (ComputeIsInForeground()) {
-    SetIsForegroundNow();
-  } else {
-    SetIsBackgroundNow();
-  }
+  SetPriorityNow(ComputePriority());
 }
 
 bool
 ProcessPriorityManager::ComputeIsInForeground()
 {
   // Critical processes holding the CPU/high-priority wake lock are always
   // considered to be in the foreground.
   if (IsCriticalProcessWithWakeLock()) {
@@ -554,79 +469,94 @@ ProcessPriorityManager::ComputeIsInForeg
     // We could break out early from this loop if
     //   isActive && mProcessPriority == BACKGROUND,
     // but then we might not clean up all the weak refs.
   }
 
   return !allHidden;
 }
 
-void
-ProcessPriorityManager::SetIsForeground()
+ProcessPriority
+ProcessPriorityManager::ComputePriority()
 {
-  ProcessPriority foregroundPriority = GetForegroundPriority();
+  if (ComputeIsInForeground()) {
+    if (IsCriticalProcessWithWakeLock()) {
+      return PROCESS_PRIORITY_FOREGROUND_HIGH;
+    }
+    return PROCESS_PRIORITY_FOREGROUND;
+  }
+
+  AudioChannelService* service = AudioChannelService::GetAudioChannelService();
+  if (service->ContentOrNormalChannelIsActive()) {
+    return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
+  }
+
+  bool isHomescreen = false;
 
-  if (mProcessPriority == PROCESS_PRIORITY_UNKNOWN ||
-      foregroundPriority < mProcessPriority) {
-    LOG("Giving grace period to %s -> %s transition.",
-        ProcessPriorityToString(mProcessPriority),
-        ProcessPriorityToString(foregroundPriority));
-    ScheduleResetPriority("backgroundGracePeriodMS");
-  } else {
-    SetIsForegroundNow();
+  ContentChild* contentChild = ContentChild::GetSingleton();
+  if (contentChild) {
+    const InfallibleTArray<PBrowserChild*>& browsers =
+      contentChild->ManagedPBrowserChild();
+    for (uint32_t i = 0; i < browsers.Length(); i++) {
+      nsAutoString appType;
+      static_cast<TabChild*>(browsers[i])->GetAppType(appType);
+      if (appType.EqualsLiteral("homescreen")) {
+        isHomescreen = true;
+        break;
+      }
+    }
   }
+
+  return isHomescreen ?
+         PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
+         PROCESS_PRIORITY_BACKGROUND;
 }
 
 void
-ProcessPriorityManager::SetIsForegroundNow()
+ProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
 {
-  ProcessPriority foregroundPriority = GetForegroundPriority();
-  if (foregroundPriority == mProcessPriority) {
+  if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
+    MOZ_ASSERT(false);
     return;
   }
 
-  // Cancel the memory minimization procedure we might have started.
-  nsCOMPtr<nsICancelableRunnable> runnable =
-    do_QueryReferent(mMemoryMinimizerRunnable);
-  if (runnable) {
-    runnable->Cancel();
-  }
-
-  mProcessPriority = foregroundPriority;
-  LOG("Setting priority to %s.", ProcessPriorityToString(mProcessPriority));
-  hal::SetProcessPriority(getpid(), mProcessPriority);
-}
-
-void
-ProcessPriorityManager::SetIsBackgroundNow()
-{
-  ProcessPriority backgroundPriority = GetBackgroundPriority();
-  if (mProcessPriority == backgroundPriority) {
+  if (mProcessPriority == aPriority) {
     return;
   }
 
-  mProcessPriority = backgroundPriority;
-  LOG("Setting priority to %s", ProcessPriorityToString(mProcessPriority));
+  LOG("Changing priority from %s to %s.",
+      ProcessPriorityToString(mProcessPriority),
+      ProcessPriorityToString(aPriority));
+  mProcessPriority = aPriority;
   hal::SetProcessPriority(getpid(), mProcessPriority);
 
-  // We're in the background; dump as much memory as we can.
-  nsCOMPtr<nsIMemoryReporterManager> mgr =
-    do_GetService("@mozilla.org/memory-reporter-manager;1");
-  if (mgr) {
+  if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
+    // Cancel the memory minimization procedure we might have started.
     nsCOMPtr<nsICancelableRunnable> runnable =
       do_QueryReferent(mMemoryMinimizerRunnable);
-
-    // Cancel the previous task if it's still pending
     if (runnable) {
       runnable->Cancel();
     }
+  } else {
+    // We're in the background; dump as much memory as we can.
+    nsCOMPtr<nsIMemoryReporterManager> mgr =
+      do_GetService("@mozilla.org/memory-reporter-manager;1");
+    if (mgr) {
+      nsCOMPtr<nsICancelableRunnable> runnable =
+        do_QueryReferent(mMemoryMinimizerRunnable);
 
-    mgr->MinimizeMemoryUsage(/* callback = */ nullptr,
-                             getter_AddRefs(runnable));
-    mMemoryMinimizerRunnable = do_GetWeakReference(runnable);
+      // Cancel the previous task if it's still pending
+      if (runnable) {
+        runnable->Cancel();
+      }
+
+      mgr->MinimizeMemoryUsage(/* callback = */ nullptr,
+                               getter_AddRefs(runnable));
+      mMemoryMinimizerRunnable = do_GetWeakReference(runnable);
+    }
   }
 }
 
 void
 ProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref)
 {
   if (mResetPriorityTimer) {
     LOG("ScheduleResetPriority bailing; the timer is already running.");
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -8,16 +8,17 @@
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsIDocShell.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
@@ -1294,16 +1295,91 @@ MediaManager::GetActiveMediaCaptureWindo
     return rv;
 
   mActiveWindows.EnumerateRead(WindowsHashToArrayFunc, array);
 
   *aArray = array;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
+                                      bool* aAudio)
+{
+  *aVideo = false;
+  *aAudio = false;
+
+  nsresult rv = MediaCaptureWindowStateInternal(aWindow, aVideo, aAudio);
+#ifdef DEBUG
+  nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
+  LOG(("%s: window %lld capturing %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
+       *aVideo ? "video" : "", *aAudio ? "audio" : ""));
+#endif
+  return rv;
+}
+
+nsresult
+MediaManager::MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
+                                              bool* aAudio)
+{
+  // We need to return the union of all streams in all innerwindows that
+  // correspond to that outerwindow.
+
+  // Iterate the docshell tree to find all the child windows, find
+  // all the listeners for each one, get the booleans, and merge the
+  // results.
+  nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
+  if (piWin) {
+    if (piWin->GetCurrentInnerWindow() || piWin->IsInnerWindow()) {
+      uint64_t windowID;
+      if (piWin->GetCurrentInnerWindow()) {
+        windowID = piWin->GetCurrentInnerWindow()->WindowID();
+      } else {
+        windowID = piWin->WindowID();
+      }
+      StreamListeners* listeners = GetActiveWindows()->Get(windowID);
+      if (listeners) {
+        uint32_t length = listeners->Length();
+        for (uint32_t i = 0; i < length; ++i) {
+          nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
+            listeners->ElementAt(i);
+          if (listener->CapturingVideo()) {
+            *aVideo = true;
+          }
+          if (listener->CapturingAudio()) {
+            *aAudio = true;
+          }
+          if (*aAudio && *aVideo) {
+            return NS_OK; // no need to continue iterating
+          }
+        }
+      }
+    }
+
+    // iterate any children of *this* window (iframes, etc)
+    nsCOMPtr<nsIDocShellTreeNode> node =
+      do_QueryInterface(piWin->GetDocShell());
+    if (node) {
+      int32_t i, count;
+      node->GetChildCount(&count);
+      for (i = 0; i < count; ++i) {
+        nsCOMPtr<nsIDocShellTreeItem> item;
+        node->GetChildAt(i, getter_AddRefs(item));
+        nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(item);
+
+        MediaCaptureWindowStateInternal(win, aVideo, aAudio);
+        if (*aAudio && *aVideo) {
+          return NS_OK; // no need to continue iterating
+        }
+      }
+    }
+  }
+  return NS_OK;
+}
+
 // Can be invoked from EITHER MainThread or MSG thread
 void
 GetUserMediaCallbackMediaStreamListener::Invalidate()
 {
 
   nsRefPtr<MediaOperationRunnable> runnable;
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -123,16 +123,25 @@ public:
   {
     NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
     if (!mStream) {
       return nullptr;
     }
     return mStream->AsSourceStream();
   }
 
+  bool CapturingVideo()
+  {
+    return mVideoSource && mLastEndTimeVideo > 0 && !mFinished;
+  }
+  bool CapturingAudio()
+  {
+    return mAudioSource && mLastEndTimeAudio > 0 && !mFinished;
+  }
+
   // implement in .cpp to avoid circular dependency with MediaOperationRunnable
   // Can be invoked from EITHER MainThread or MSG thread
   void Invalidate();
 
   void
   AudioConfig(bool aEchoOn, uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise)
@@ -420,16 +429,19 @@ private:
     mActiveWindows.Init();
     mActiveCallbacks.Init();
   }
 
   ~MediaManager() {
     delete mBackend;
   }
 
+  nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
+                                           bool* aAudio);
+
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsRefPtrHashtable<nsStringHashKey, nsRunnable> mActiveCallbacks;
   // Always exists
   nsCOMPtr<nsIThread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -1,18 +1,23 @@
 /* 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 "nsISupports.idl"
 
 interface nsISupportsArray;
+interface nsIDOMWindow;
 
 %{C++
 #define NS_MEDIAMANAGERSERVICE_CID {0xabc622ea, 0x9655, 0x4123, {0x80, 0xd9, 0x22, 0x62, 0x1b, 0xdd, 0x54, 0x65}}
 #define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
 %}
 
-[scriptable, builtinclass, uuid(afe82ff1-2caa-4304-85da-0158a5dee56b)]
+[scriptable, builtinclass, uuid(2efff6ab-0e3e-4cc4-8f9b-4aaca59a1140)]
 interface nsIMediaManagerService : nsISupports
 {
+  /* return a array of inner windows that have active captures */
   readonly attribute nsISupportsArray activeMediaCaptureWindows;
+
+  /* Get the capture state for the given window and all descendant windows (iframes, etc) */
+  void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio);
 };
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
@@ -21,16 +21,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 if (navigator.platform.startsWith("Win")) {
   SimpleTest.expectAssertions(0, 9);
+} else if (navigator.platform.startsWith("Mac")) {
+  SimpleTest.expectAssertions(0, 2);
 } else {
   SimpleTest.expectAssertions(0, 1);
 }
 
   var audioLocal;
   var audioPCLocal;
   var audioPCRemote;
 
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
@@ -19,19 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <video id="videoPCLocal" width="160" height="120" controls></video>
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-if (!navigator.platform.startsWith("Linux")) {
   SimpleTest.expectAssertions(0, 1);
-}
 
   var audioLocal;
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
   var pcLocal;
   var pcRemote;
--- a/dom/permission/tests/test_permission_basics.html
+++ b/dom/permission/tests/test_permission_basics.html
@@ -16,16 +16,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 "use strict";
 
+if (!navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 1);
+}
+
 var testPrivApp = {
   'manifestURL' : 'https://aprivileged.com/manifest.webapp'
 };
 
 var testCertApp = {
   'manifestURL' : 'https://acertified.com/manifest.webapp'
 };
 
--- a/dom/plugins/test/mochitest/test_GCrace.html
+++ b/dom/plugins/test/mochitest/test_GCrace.html
@@ -16,17 +16,17 @@
     function start() {
       if (!SimpleTest.testPluginIsOOP()) {
         ok(true, "Skipping this test when test plugin is not OOP.");
         SimpleTest.finish();
         return;
       }
       else {
         if (navigator.platform.startsWith("Win")) {
-          SimpleTest.expectAssertions(0, 66);
+          SimpleTest.expectAssertions(0, 250);
         } else {
           SimpleTest.expectAssertions(0, 1);
         }
 
         setTimeout(checkGCRace, 1000);
       }
     }
 
--- a/dom/plugins/test/mochitest/test_pluginstream_seek_close.html
+++ b/dom/plugins/test/mochitest/test_pluginstream_seek_close.html
@@ -1,19 +1,17 @@
 <body>
 <head>
   <title>NPAPI Seekable NPStream Test</title>
   <script type="text/javascript" 
           src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" 
         href="/tests/SimpleTest/test.css" />
 <script>
-  if (navigator.platform.startsWith("Linux")) {
-    SimpleTest.expectAssertions(0, 1);
-  }
+  SimpleTest.expectAssertions(0, 1);
 
   SimpleTest.waitForExplicitFinish();
 
   function frameLoaded() {
     var testframe = document.getElementById('testframe');
     var content = testframe.contentDocument.body.innerHTML;
     if (!content.length)
       return;
--- a/dom/plugins/test/mochitest/test_twostreams.html
+++ b/dom/plugins/test/mochitest/test_twostreams.html
@@ -5,21 +5,17 @@
           src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" 
         href="/tests/SimpleTest/test.css" />
 </head>
 <body>
   <p id="display"></p>
 
   <script type="text/javascript">
-  if (navigator.platform.startsWith("Win")) {
-    SimpleTest.expectAssertions(1, 2);
-  } else {
-    SimpleTest.expectAssertions(1);
-  }
+  SimpleTest.expectAssertions(1, 2);
 
   SimpleTest.waitForExplicitFinish();
 
   var framesToLoad = 2;
   function frameLoaded(id) {
     var frame = document.getElementById('testframe' + id);
     if (!frame.contentDocument.body.innerHTML.length)
       return;
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2428,10 +2428,74 @@ this.MMI_SC_BAOC = "33";
 this.MMI_SC_BAOIC = "331";
 this.MMI_SC_BAOICxH = "332";
 this.MMI_SC_BAIC = "35";
 this.MMI_SC_BAICr = "351";
 this.MMI_SC_BA_ALL = "330";
 this.MMI_SC_BA_MO = "333";
 this.MMI_SC_BA_MT = "353";
 
+/**
+ * CDMA PDU constants
+ */
+
+// SMS Message Type, as defined in 3GPP2 C.S0015-A v2.0, Table 3.4-1
+this.PDU_CDMA_MSG_TYPE_P2P = 0x00;        // Point-to-Point
+this.PDU_CDMA_MSG_TYPE_BROADCAST = 0x01;  // Broadcast
+this.PDU_CDMA_MSG_TYPE_ACK = 0x02;        // Acknowledge
+
+// SMS Teleservice Identitifier, as defined in 3GPP2 N.S0005, Table 175
+this.PDU_CDMA_MSG_TELESERIVCIE_ID_SMS = 0x1002;   // SMS
+this.PDU_CDMA_MSG_TELESERIVCIE_ID_WEMT = 0x1005;  // Wireless Enhanced Messaging Teleservice
+                                                  // required for fragmented SMS
+
+// SMS Service Category, as defined in 3GPP2 C.R1001-D, Table 9.3.1-1
+this.PDU_CDMA_MSG_CATEGORY_UNSPEC = 0x00; // Unknown/Unspecified
+
+// Address Information, Digit Mode, as defined in 3GPP2 C.S0015-A v2.0, sec 3.4.3.3
+this.PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF = 0x00;      // Digit Mode : DTMF
+this.PDU_CDMA_MSG_ADDR_DIGIT_MODE_ASCII = 0x01;     // Digit Mode : 8-bit ASCII with MSB = 0
+
+// Address Information, Number Mode, as defined in 3GPP2 C.S0015-A v2.0, sec 3.4.3.3
+this.PDU_CDMA_MSG_ADDR_NUMBER_MODE_ANSI = 0x00;     // Number Mode : ANSI T1.607-2000(R2004)
+this.PDU_CDMA_MSG_ADDR_NUMBER_MODE_ASCII = 0x01;    // Number Mode : Data network address format
+
+// Address Information, Number Type, as defined in 3GPP2 C.S0015-A v2.0, Table 3.4.3.3-1
+this.PDU_CDMA_MSG_ADDR_NUMBER_TYPE_UNKNOWN = 0x00;        // Number Type : Unknown
+this.PDU_CDMA_MSG_ADDR_NUMBER_TYPE_INTERNATIONAL = 0x01;  // Number Type : Internaltional number(+XXXXX)
+this.PDU_CDMA_MSG_ADDR_NUMBER_TYPE_NATIONAL = 0x02;       // Number Type : National number
+
+// Address Information, Number Plan, as defined in 3GPP2 C.S0005-D v2.0, Table 2.7.1.3.2.4-3
+this.PDU_CDMA_MSG_ADDR_NUMBER_PLAN_UNKNOWN = 0x00;  // Number Plan : Unknown
+this.PDU_CDMA_MSG_ADDR_NUMBER_PLAN_ISDN = 0x01;     // Number Plan : ISDN/Telephony numbering plan
+
+// SMS Encoding, as defined in 3GPP2 C.R1001-D, Table 9.1-1
+this.PDU_CDMA_MSG_CODING_OCTET = 0x00;        // octet(8-bit), Not tested
+this.PDU_CDMA_MSG_CODING_IS_91 = 0x01;        // IS-91 Extended Protocol Message(variable), Not tested
+this.PDU_CDMA_MSG_CODING_7BITS_ASCII = 0x02;  // 7-bit ASCII(7-bit)
+this.PDU_CDMA_MSG_CODING_IA5 = 0x03;          // IA5(7-bit), Not tested
+this.PDU_CDMA_MSG_CODING_UNICODE = 0x04;      // Unicode(16-bit)
+this.PDU_CDMA_MSG_CODING_SHIFT_JIS = 0x05;    // Shift-6 JIS(8/16-bit variable), Not supported
+this.PDU_CDMA_MSG_CODING_KOREAN = 0x06;       // Korean(8/16-bit variable), Not supported
+this.PDU_CDMA_MSG_CODING_LATIN_HEBREW = 0x07; // Latin/ Hebrew(8-bit), ISO/IEC 8859-8, Not supported
+this.PDU_CDMA_MSG_CODING_LATIN = 0x08;        // Latin(8-bit), ISO/IEC 8859-1, Not tested
+this.PDU_CDMA_MSG_CODING_7BITS_GSM = 0x09;    // GSM 7-bit default alphabet(7-bit), Not tested
+this.PDU_CDMA_MSG_CODING_GSM_DCS = 0x0A;      // GSM Data-Coding-Scheme, Not supported
+
+// SMS Message Type, as defined in 3GPP2 C.S0015-A v2.0, Table 4.5.1-1
+this.PDU_CDMA_MSG_TYPE_DELIVER = 0x01;        // Receive
+this.PDU_CDMA_MSG_TYPE_SUBMIT = 0x02;         // Send
+
+// SMS User Data Subparameters, as defined in 3GPP2 C.S0015-A v2.0, Table 4.5-1
+this.PDU_CDMA_MSG_USERDATA_MSG_ID = 0x00;           // Message Identifier
+this.PDU_CDMA_MSG_USERDATA_BODY = 0x01;             // User Data Body
+this.PDU_CDMA_MSG_USERDATA_TIMESTAMP = 0x03;        // Message Center Time Stamp
+this.PDU_CDMA_REPLY_OPTION = 0x0A;                  // Reply Option
+this.PDU_CDMA_MSG_USERDATA_CALLBACK_NUMBER = 0x0E;  // Callback Number
+
+// IS-91 Message Type, as defined in TIA/EIA/IS-91-A, Table 9
+this.PDU_CDMA_MSG_CODING_IS_91_TYPE_VOICEMAIL_STATUS = 0x82;
+this.PDU_CDMA_MSG_CODING_IS_91_TYPE_SMS_FULL = 0x83;
+this.PDU_CDMA_MSG_CODING_IS_91_TYPE_CLI = 0x84;
+this.PDU_CDMA_MSG_CODING_IS_91_TYPE_SMS = 0x85;
+
 // Allow this file to be imported via Components.utils.import().
 this.EXPORTED_SYMBOLS = Object.keys(this);
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -771,17 +771,17 @@ let RIL = {
      * ICC status. Keeps a reference of the data response to the
      * getICCStatus request.
      */
     this.iccStatus = null;
 
     /**
      * Card state
      */
-    this.cardState = null;
+    this.cardState = GECKO_CARDSTATE_UNKNOWN;
 
     /**
      * Strings
      */
     this.IMEI = null;
     this.IMEISV = null;
     this.ESN = null;
     this.MEID = null;
@@ -1758,32 +1758,37 @@ let RIL = {
 
     if (!options.segmentSeq) {
       // Fist segment to send
       options.segmentSeq = 1;
       options.body = options.segments[0].body;
       options.encodedBodyLength = options.segments[0].encodedBodyLength;
     }
 
-    Buf.newParcel(REQUEST_SEND_SMS, options);
-    Buf.writeUint32(2);
-    Buf.writeString(options.SMSC);
-    GsmPDUHelper.writeMessage(options);
+    if (this._isCdma) {
+      Buf.newParcel(REQUEST_CDMA_SEND_SMS, options);
+      CdmaPDUHelper.writeMessage(options);
+    } else {
+      Buf.newParcel(REQUEST_SEND_SMS, options);
+      Buf.writeUint32(2);
+      Buf.writeString(options.SMSC);
+      GsmPDUHelper.writeMessage(options);
+    }
     Buf.sendParcel();
   },
 
   /**
    * Acknowledge the receipt and handling of an SMS.
    *
    * @param success
    *        Boolean indicating whether the message was successfuly handled.
    * @param cause
    *        SMS_* constant indicating the reason for unsuccessful handling.
    */
-  acknowledgeSMS: function acknowledgeSMS(success, cause) {
+  acknowledgeGsmSms: function acknowledgeGsmSms(success, cause) {
     let token = Buf.newParcel(REQUEST_SMS_ACKNOWLEDGE);
     Buf.writeUint32(2);
     Buf.writeUint32(success ? 1 : 0);
     Buf.writeUint32(cause);
     Buf.sendParcel();
   },
 
   /**
@@ -1791,17 +1796,36 @@ let RIL = {
    *
    * @param success
    *        Boolean indicating whether the message was successfuly handled.
    */
   ackSMS: function ackSMS(options) {
     if (options.result == PDU_FCS_RESERVED) {
       return;
     }
-    this.acknowledgeSMS(options.result == PDU_FCS_OK, options.result);
+    if (this._isCdma) {
+      this.acknowledgeCdmaSms(options.result == PDU_FCS_OK, options.result);
+    } else {
+      this.acknowledgeGsmSms(options.result == PDU_FCS_OK, options.result);
+    }
+  },
+
+  /**
+   * Acknowledge the receipt and handling of a CDMA SMS.
+   *
+   * @param success
+   *        Boolean indicating whether the message was successfuly handled.
+   * @param cause
+   *        SMS_* constant indicating the reason for unsuccessful handling.
+   */
+  acknowledgeCdmaSms: function acknowledgeCdmaSms(success, cause) {
+    let token = Buf.newParcel(REQUEST_CDMA_SMS_ACKNOWLEDGE);
+    Buf.writeUint32(success ? 0 : 1);
+    Buf.writeUint32(cause);
+    Buf.sendParcel();
   },
 
   setCellBroadcastSearchList: function setCellBroadcastSearchList(options) {
     try {
       let str = options.searchListStr;
       this.cellBroadcastConfigs.MMI = this._convertCellBroadcastSearchList(str);
     } catch (e) {
       if (DEBUG) {
@@ -2765,69 +2789,77 @@ let RIL = {
     Buf.simpleRequest(REQUEST_REPORT_STK_SERVICE_IS_RUNNING);
   },
 
   /**
    * Process ICC status.
    */
   _processICCStatus: function _processICCStatus(iccStatus) {
     this.iccStatus = iccStatus;
+    let newCardState;
 
     if ((!iccStatus) || (iccStatus.cardState == CARD_STATE_ABSENT)) {
-      if (DEBUG) debug("ICC absent");
-      if (this.cardState == GECKO_CARDSTATE_ABSENT) {
-        this.operator = null;
+      switch (this.radioState) {
+        case GECKO_RADIOSTATE_UNAVAILABLE:
+          newCardState = GECKO_CARDSTATE_UNKNOWN;
+          break;
+        case GECKO_RADIOSTATE_OFF:
+          newCardState = GECKO_CARDSTATE_NOT_READY;
+          break;
+        case GECKO_RADIOSTATE_READY:
+          if (DEBUG) {
+            debug("ICC absent");
+          }
+          newCardState = GECKO_CARDSTATE_ABSENT;
+          break;
+      }
+      if (newCardState == this.cardState) {
         return;
       }
-      this.cardState = GECKO_CARDSTATE_ABSENT;
+      this.cardState = newCardState;
       this.sendDOMMessage({rilMessageType: "cardstatechange",
                            cardState: this.cardState});
       return;
     }
 
     let index = this._isCdma ? iccStatus.cdmaSubscriptionAppIndex :
                                iccStatus.gsmUmtsSubscriptionAppIndex;
     let app = iccStatus.apps[index];
-    if (!app) {
-      if (DEBUG) {
-        debug("Subscription application is not present in iccStatus.");
-      }
-      if (this.cardState == GECKO_CARDSTATE_ABSENT) {
+    if (iccStatus.cardState == CARD_STATE_ERROR || !app) {
+      if (this.cardState == GECKO_CARDSTATE_UNKNOWN) {
+        this.operator = null;
         return;
       }
-      this.cardState = GECKO_CARDSTATE_ABSENT;
       this.operator = null;
+      this.cardState = GECKO_CARDSTATE_UNKNOWN;
       this.sendDOMMessage({rilMessageType: "cardstatechange",
                            cardState: this.cardState});
       return;
     }
     // fetchICCRecords will need to read aid, so read aid here.
     this.aid = app.aid;
     this.appType = app.app_type;
 
-    let newCardState;
     switch (app.app_state) {
       case CARD_APPSTATE_PIN:
         newCardState = GECKO_CARDSTATE_PIN_REQUIRED;
         break;
       case CARD_APPSTATE_PUK:
         newCardState = GECKO_CARDSTATE_PUK_REQUIRED;
         break;
       case CARD_APPSTATE_SUBSCRIPTION_PERSO:
         newCardState = GECKO_CARDSTATE_NETWORK_LOCKED;
         break;
       case CARD_APPSTATE_READY:
         newCardState = GECKO_CARDSTATE_READY;
         break;
       case CARD_APPSTATE_UNKNOWN:
-        newCardState = GECKO_CARDSTATE_UNKNOWN;
-        break;
       case CARD_APPSTATE_DETECTED:
       default:
-        newCardState = GECKO_CARDSTATE_NOT_READY;
+        newCardState = GECKO_CARDSTATE_UNKNOWN;
     }
 
     if (this.cardState == newCardState) {
       return;
     }
 
     // This was moved down from CARD_APPSTATE_READY
     this.requestNetworkInfo();
@@ -3577,105 +3609,24 @@ let RIL = {
     }
     // Write 2 string delimitors for the total string length must be even.
     Buf.writeStringDelimiter(0);
 
     Buf.sendParcel();
   },
 
   /**
-   * Helper for processing received SMS parcel data.
-   *
-   * @param length
-   *        Length of SMS string in the incoming parcel.
-   *
-   * @return Message parsed or null for invalid message.
-   */
-  _processReceivedSms: function _processReceivedSms(length) {
-    if (!length) {
-      if (DEBUG) debug("Received empty SMS!");
-      return null;
-    }
-
-    // An SMS is a string, but we won't read it as such, so let's read the
-    // string length and then defer to PDU parsing helper.
-    let messageStringLength = Buf.readUint32();
-    if (DEBUG) debug("Got new SMS, length " + messageStringLength);
-    let message = GsmPDUHelper.readMessage();
-    if (DEBUG) debug("Got new SMS: " + JSON.stringify(message));
-
-    // Read string delimiters. See Buf.readString().
-    Buf.readStringDelimiter(length);
-
-    return message;
-  },
-
-  /**
-   * Helper for processing SMS-DELIVER PDUs.
-   *
-   * @param length
-   *        Length of SMS string in the incoming parcel.
+   * Helper for processing multipart SMS.
+   *
+   * @param message
+   *        Received sms message.
    *
    * @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
    */
-  _processSmsDeliver: function _processSmsDeliver(length) {
-    let message = this._processReceivedSms(length);
-    if (!message) {
-      return PDU_FCS_UNSPECIFIED;
-    }
-
-    if (message.epid == PDU_PID_SHORT_MESSAGE_TYPE_0) {
-      // `A short message type 0 indicates that the ME must acknowledge receipt
-      // of the short message but shall discard its contents.` ~ 3GPP TS 23.040
-      // 9.2.3.9
-      return PDU_FCS_OK;
-    }
-
-    if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
-      switch (message.epid) {
-        case PDU_PID_ANSI_136_R_DATA:
-        case PDU_PID_USIM_DATA_DOWNLOAD:
-          if (ICCUtilsHelper.isICCServiceAvailable("DATA_DOWNLOAD_SMS_PP")) {
-            // `If the service "data download via SMS Point-to-Point" is
-            // allocated and activated in the (U)SIM Service Table, ... then the
-            // ME shall pass the message transparently to the UICC using the
-            // ENVELOPE (SMS-PP DOWNLOAD).` ~ 3GPP TS 31.111 7.1.1.1
-            this.dataDownloadViaSMSPP(message);
-
-            // `the ME shall not display the message, or alert the user of a
-            // short message waiting.` ~ 3GPP TS 31.111 7.1.1.1
-            return PDU_FCS_RESERVED;
-          }
-          // Fall through!
-
-          // If the service "data download via SMS-PP" is not available in the
-          // (U)SIM Service Table, ..., then the ME shall store the message in
-          // EFsms in accordance with TS 31.102` ~ 3GPP TS 31.111 7.1.1.1
-        default:
-          this.writeSmsToSIM(message);
-          break;
-      }
-    }
-
-    // TODO: Bug 739143: B2G SMS: Support SMS Storage Full event
-    if ((message.messageClass != GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0]) && !true) {
-      // `When a mobile terminated message is class 0..., the MS shall display
-      // the message immediately and send a ACK to the SC ..., irrespective of
-      // whether there is memory available in the (U)SIM or ME.` ~ 3GPP 23.038
-      // clause 4.
-
-      if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
-        // `If all the short message storage at the MS is already in use, the
-        // MS shall return "memory capacity exceeded".` ~ 3GPP 23.038 clause 4.
-        return PDU_FCS_MEMORY_CAPACITY_EXCEEDED;
-      }
-
-      return PDU_FCS_UNSPECIFIED;
-    }
-
+  _processSmsMultipart: function _processSmsMultipart(message) {
     if (message.header && (message.header.segmentMaxSeq > 1)) {
       message = this._processReceivedSmsSegment(message);
     } else {
       if (message.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
         message.fullData = message.data;
         delete message.data;
       } else {
         message.fullBody = message.body;
@@ -3705,17 +3656,17 @@ let RIL = {
    * Helper for processing SMS-STATUS-REPORT PDUs.
    *
    * @param length
    *        Length of SMS string in the incoming parcel.
    *
    * @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
    */
   _processSmsStatusReport: function _processSmsStatusReport(length) {
-    let message = this._processReceivedSms(length);
+    let [message, result] = GsmPDUHelper.processReceivedSms(length);
     if (!message) {
       if (DEBUG) debug("invalid SMS-STATUS-REPORT");
       return PDU_FCS_UNSPECIFIED;
     }
 
     let options = this._pendingSentSmsMap[message.messageRef];
     if (!options) {
       if (DEBUG) debug("no pending SMS-SUBMIT message");
@@ -3840,16 +3791,70 @@ let RIL = {
     let next = options.segmentSeq;
     options.body = options.segments[next].body;
     options.encodedBodyLength = options.segments[next].encodedBodyLength;
     options.segmentSeq = next + 1;
 
     this.sendSMS(options);
   },
 
+  /**
+   * Helper for processing result of send SMS.
+   *
+   * @param length
+   *        Length of SMS string in the incoming parcel.
+   * @param options
+   *        Sms information.
+   */
+  _processSmsSendResult: function _processSmsSendResult(length, options) {
+    if (options.rilRequestError) {
+      if (DEBUG) debug("_processSmsSendResult: rilRequestError = " + options.rilRequestError);
+      switch (options.rilRequestError) {
+        case ERROR_SMS_SEND_FAIL_RETRY:
+          if (options.retryCount < SMS_RETRY_MAX) {
+            options.retryCount++;
+            // TODO: bug 736702 TP-MR, retry interval, retry timeout
+            this.sendSMS(options);
+            break;
+          }
+
+          // Fallback to default error handling if it meets max retry count.
+        default:
+          this.sendDOMMessage({
+            rilMessageType: "sms-send-failed",
+            envelopeId: options.envelopeId,
+            error: options.rilRequestError,
+          });
+          break;
+      }
+      return;
+    }
+
+    options.messageRef = Buf.readUint32();
+    options.ackPDU = Buf.readString();
+    options.errorCode = Buf.readUint32();
+
+    if ((options.segmentMaxSeq > 1)
+        && (options.segmentSeq < options.segmentMaxSeq)) {
+      // Not last segment
+      this._processSentSmsSegment(options);
+    } else {
+      // Last segment sent with success.
+      if (options.requestStatusReport) {
+        if (DEBUG) debug("waiting SMS-STATUS-REPORT for messageRef " + options.messageRef);
+        this._pendingSentSmsMap[options.messageRef] = options;
+      }
+
+      this.sendDOMMessage({
+        rilMessageType: "sms-sent",
+        envelopeId: options.envelopeId,
+      });
+    }
+  },
+
   _processReceivedSmsCbPage: function _processReceivedSmsCbPage(original) {
     if (original.numPages <= 1) {
       if (original.body) {
         original.fullBody = original.body;
         delete original.body;
       } else if (original.data) {
         original.fullData = original.data;
         delete original.data;
@@ -4527,59 +4532,17 @@ RIL[REQUEST_OPERATOR] = function REQUEST
 
   let operatorData = Buf.readStringList();
   if (DEBUG) debug("Operator: " + operatorData);
   this._processOperator(operatorData);
 };
 RIL[REQUEST_RADIO_POWER] = null;
 RIL[REQUEST_DTMF] = null;
 RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) {
-  if (options.rilRequestError) {
-    if (DEBUG) debug("REQUEST_SEND_SMS: rilRequestError = " + options.rilRequestError);
-    switch (options.rilRequestError) {
-      case ERROR_SMS_SEND_FAIL_RETRY:
-        if (options.retryCount < SMS_RETRY_MAX) {
-          options.retryCount++;
-          // TODO: bug 736702 TP-MR, retry interval, retry timeout
-          this.sendSMS(options);
-          break;
-        }
-
-        // Fallback to default error handling if it meets max retry count.
-      default:
-        this.sendDOMMessage({
-          rilMessageType: "sms-send-failed",
-          envelopeId: options.envelopeId,
-          error: options.rilRequestError,
-        });
-        break;
-    }
-    return;
-  }
-
-  options.messageRef = Buf.readUint32();
-  options.ackPDU = Buf.readString();
-  options.errorCode = Buf.readUint32();
-
-  if ((options.segmentMaxSeq > 1)
-      && (options.segmentSeq < options.segmentMaxSeq)) {
-    // Not last segment
-    this._processSentSmsSegment(options);
-  } else {
-    // Last segment sent with success.
-    if (options.requestStatusReport) {
-      if (DEBUG) debug("waiting SMS-STATUS-REPORT for messageRef " + options.messageRef);
-      this._pendingSentSmsMap[options.messageRef] = options;
-    }
-
-    this.sendDOMMessage({
-      rilMessageType: "sms-sent",
-      envelopeId: options.envelopeId,
-    });
-  }
+  this._processSmsSendResult(length, options);
 };
 RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null;
 
 RIL.readSetupDataCall_v5 = function readSetupDataCall_v5(options) {
   if (!options) {
     options = {};
   }
   let [cid, ifname, ipaddr, dns, gw] = Buf.readStringList();
@@ -4951,19 +4914,19 @@ RIL[REQUEST_OEM_HOOK_STRINGS] = null;
 RIL[REQUEST_SCREEN_STATE] = null;
 RIL[REQUEST_SET_SUPP_SVC_NOTIFICATION] = null;
 RIL[REQUEST_WRITE_SMS_TO_SIM] = function REQUEST_WRITE_SMS_TO_SIM(length, options) {
   if (options.rilRequestError) {
     // `The MS shall return a "protocol error, unspecified" error message if
     // the short message cannot be stored in the (U)SIM, and there is other
     // message storage available at the MS` ~ 3GPP TS 23.038 section 4. Here
     // we assume we always have indexed db as another storage.
-    this.acknowledgeSMS(false, PDU_FCS_PROTOCOL_ERROR);
+    this.acknowledgeGsmSms(false, PDU_FCS_PROTOCOL_ERROR);
   } else {
-    this.acknowledgeSMS(true, PDU_FCS_OK);
+    this.acknowledgeGsmSms(true, PDU_FCS_OK);
   }
 };
 RIL[REQUEST_DELETE_SMS_ON_SIM] = null;
 RIL[REQUEST_SET_BAND_MODE] = null;
 RIL[REQUEST_QUERY_AVAILABLE_BAND_MODE] = null;
 RIL[REQUEST_STK_GET_PROFILE] = null;
 RIL[REQUEST_STK_SET_PROFILE] = null;
 RIL[REQUEST_STK_SEND_ENVELOPE_COMMAND] = null;
@@ -5005,17 +4968,19 @@ RIL[REQUEST_CDMA_SET_ROAMING_PREFERENCE]
 RIL[REQUEST_CDMA_QUERY_ROAMING_PREFERENCE] = null;
 RIL[REQUEST_SET_TTY_MODE] = null;
 RIL[REQUEST_QUERY_TTY_MODE] = null;
 RIL[REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE] = null;
 RIL[REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE] = null;
 RIL[REQUEST_CDMA_FLASH] = null;
 RIL[REQUEST_CDMA_BURST_DTMF] = null;
 RIL[REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY] = null;
-RIL[REQUEST_CDMA_SEND_SMS] = null;
+RIL[REQUEST_CDMA_SEND_SMS] = function REQUEST_CDMA_SEND_SMS(length, options) {
+  this._processSmsSendResult(length, options);
+};
 RIL[REQUEST_CDMA_SMS_ACKNOWLEDGE] = null;
 RIL[REQUEST_GSM_GET_BROADCAST_SMS_CONFIG] = null;
 RIL[REQUEST_GSM_SET_BROADCAST_SMS_CONFIG] = function REQUEST_GSM_SET_BROADCAST_SMS_CONFIG(length, options) {
   if (options.rilRequestError == ERROR_SUCCESS) {
     this.setGsmSmsBroadcastActivation(true);
   }
 };
 RIL[REQUEST_GSM_SMS_BROADCAST_ACTIVATION] = null;
@@ -5047,34 +5012,34 @@ RIL[REQUEST_GET_SMSC_ADDRESS] = function
   this.SMSC = Buf.readString();
 };
 RIL[REQUEST_SET_SMSC_ADDRESS] = null;
 RIL[REQUEST_REPORT_SMS_MEMORY_STATUS] = null;
 RIL[REQUEST_REPORT_STK_SERVICE_IS_RUNNING] = null;
 RIL[REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU] = null;
 RIL[REQUEST_STK_SEND_ENVELOPE_WITH_STATUS] = function REQUEST_STK_SEND_ENVELOPE_WITH_STATUS(length, options) {
   if (options.rilRequestError) {
-    this.acknowledgeSMS(false, PDU_FCS_UNSPECIFIED);
+    this.acknowledgeGsmSms(false, PDU_FCS_UNSPECIFIED);
     return;
   }
 
   let sw1 = Buf.readUint32();
   let sw2 = Buf.readUint32();
   if ((sw1 == ICC_STATUS_SAT_BUSY) && (sw2 == 0x00)) {
-    this.acknowledgeSMS(false, PDU_FCS_USAT_BUSY);
+    this.acknowledgeGsmSms(false, PDU_FCS_USAT_BUSY);
     return;
   }
 
   let success = ((sw1 == ICC_STATUS_NORMAL_ENDING) && (sw2 == 0x00))
                 || (sw1 == ICC_STATUS_NORMAL_ENDING_WITH_EXTRA);
 
   let messageStringLength = Buf.readUint32(); // In semi-octets
   let responsePduLen = messageStringLength / 2; // In octets
   if (!responsePduLen) {
-    this.acknowledgeSMS(success, success ? PDU_FCS_OK
+    this.acknowledgeGsmSms(success, success ? PDU_FCS_OK
                                          : PDU_FCS_USIM_DATA_DOWNLOAD_ERROR);
     return;
   }
 
   this.acknowledgeIncomingGsmSmsWithPDU(success, responsePduLen, options);
 };
 RIL[REQUEST_VOICE_RADIO_TECH] = function REQUEST_VOICE_RADIO_TECH(length, options) {
   if (options.rilRequestError) {
@@ -5175,26 +5140,32 @@ RIL[UNSOLICITED_RESPONSE_CALL_STATE_CHAN
   this.getCurrentCalls();
 };
 RIL[UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED] = function UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED() {
   if (DEBUG) debug("Network state changed, re-requesting phone state and ICC status");
   this.getICCStatus();
   this.requestNetworkInfo();
 };
 RIL[UNSOLICITED_RESPONSE_NEW_SMS] = function UNSOLICITED_RESPONSE_NEW_SMS(length) {
-  let result = this._processSmsDeliver(length);
+  let [message, result] = GsmPDUHelper.processReceivedSms(length);
+
+  if (message) {
+    result = this._processSmsMultipart(message);
+  }
+
   if (result == PDU_FCS_RESERVED || result == MOZ_FCS_WAIT_FOR_EXPLICIT_ACK) {
     return;
   }
+
   // Not reserved FCS values, send ACK now.
-  this.acknowledgeSMS(result == PDU_FCS_OK, result);
+  this.acknowledgeGsmSms(result == PDU_FCS_OK, result);
 };
 RIL[UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT] = function UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT(length) {
   let result = this._processSmsStatusReport(length);
-  this.acknowledgeSMS(result == PDU_FCS_OK, result);
+  this.acknowledgeGsmSms(result == PDU_FCS_OK, result);
 };
 RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM] = function UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM(length) {
   let info = Buf.readUint32List();
   //TODO
 };
 RIL[UNSOLICITED_ON_USSD] = function UNSOLICITED_ON_USSD() {
   let [typeCode, message] = Buf.readStringList();
   if (DEBUG) {
@@ -5285,17 +5256,30 @@ RIL[UNSOLICITED_CALL_RING] = function UN
   // call, but that's enough to bring up the Phone app already. We'll know
   // details once we get a call state changed notification and can then
   // dispatch DOM events etc.
   this.sendDOMMessage(info);
 };
 RIL[UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED] = function UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED() {
   this.getICCStatus();
 };
-RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = null;
+RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = function UNSOLICITED_RESPONSE_CDMA_NEW_SMS(length) {
+  let [message, result] = CdmaPDUHelper.processReceivedSms(length);
+
+  if (message) {
+    result = this._processSmsMultipart(message);
+  }
+
+  if (result == PDU_FCS_RESERVED || result == MOZ_FCS_WAIT_FOR_EXPLICIT_ACK) {
+    return;
+  }
+
+  // Not reserved FCS values, send ACK now.
+  this.acknowledgeCdmaSms(result == PDU_FCS_OK, result);
+};
 RIL[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = function UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS(length) {
   let message;
   try {
     message = GsmPDUHelper.readCbMessage(Buf.readUint32());
   } catch (e) {
     if (DEBUG) {
       debug("Failed to parse Cell Broadcast message: " + JSON.stringify(e));
     }
@@ -6728,16 +6712,97 @@ let GsmPDUHelper = {
       case PDU_MTI_SMS_STATUS_REPORT:
         return this.readStatusReportMessage(msg);
       default:
         return null;
     }
   },
 
   /**
+   * Helper for processing received SMS parcel data.
+   *
+   * @param length
+   *        Length of SMS string in the incoming parcel.
+   *
+   * @return Message parsed or null for invalid message.
+   */
+  processReceivedSms: function processReceivedSms(length) {
+    if (!length) {
+      if (DEBUG) debug("Received empty SMS!");
+      return [null, PDU_FCS_UNSPECIFIED];
+    }
+
+    // An SMS is a string, but we won't read it as such, so let's read the
+    // string length and then defer to PDU parsing helper.
+    let messageStringLength = Buf.readUint32();
+    if (DEBUG) debug("Got new SMS, length " + messageStringLength);
+    let message = this.readMessage();
+    if (DEBUG) debug("Got new SMS: " + JSON.stringify(message));
+
+    // Read string delimiters. See Buf.readString().
+    Buf.readStringDelimiter(length);
+
+    // Determine result
+    if (!message) {
+      return [null, PDU_FCS_UNSPECIFIED];
+    }
+
+    if (message.epid == PDU_PID_SHORT_MESSAGE_TYPE_0) {
+      // `A short message type 0 indicates that the ME must acknowledge receipt
+      // of the short message but shall discard its contents.` ~ 3GPP TS 23.040
+      // 9.2.3.9
+      return [null, PDU_FCS_OK];
+    }
+
+    if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
+      switch (message.epid) {
+        case PDU_PID_ANSI_136_R_DATA:
+        case PDU_PID_USIM_DATA_DOWNLOAD:
+          if (ICCUtilsHelper.isICCServiceAvailable("DATA_DOWNLOAD_SMS_PP")) {
+            // `If the service "data download via SMS Point-to-Point" is
+            // allocated and activated in the (U)SIM Service Table, ... then the
+            // ME shall pass the message transparently to the UICC using the
+            // ENVELOPE (SMS-PP DOWNLOAD).` ~ 3GPP TS 31.111 7.1.1.1
+            RIL.dataDownloadViaSMSPP(message);
+
+            // `the ME shall not display the message, or alert the user of a
+            // short message waiting.` ~ 3GPP TS 31.111 7.1.1.1
+            return [null, PDU_FCS_RESERVED];
+          }
+          // Fall through!
+
+          // If the service "data download via SMS-PP" is not available in the
+          // (U)SIM Service Table, ..., then the ME shall store the message in
+          // EFsms in accordance with TS 31.102` ~ 3GPP TS 31.111 7.1.1.1
+        default:
+          RIL.writeSmsToSIM(message);
+          break;
+      }
+    }
+
+    // TODO: Bug 739143: B2G SMS: Support SMS Storage Full event
+    if ((message.messageClass != GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0]) && !true) {
+      // `When a mobile terminated message is class 0..., the MS shall display
+      // the message immediately and send a ACK to the SC ..., irrespective of
+      // whether there is memory available in the (U)SIM or ME.` ~ 3GPP 23.038
+      // clause 4.
+
+      if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
+        // `If all the short message storage at the MS is already in use, the
+        // MS shall return "memory capacity exceeded".` ~ 3GPP 23.038 clause 4.
+        return [null, PDU_FCS_MEMORY_CAPACITY_EXCEEDED];
+      }
+
+      return [null, PDU_FCS_UNSPECIFIED];
+    }
+
+    return [message, PDU_FCS_OK];
+  },
+
+  /**
    * Read and decode a SMS-DELIVER PDU.
    *
    * @param msg
    *        message object for output.
    */
   readDeliverMessage: function readDeliverMessage(msg) {
     // - Sender Address info -
     let senderAddressLength = this.readHexOctet();
@@ -7308,16 +7373,1020 @@ let GsmPDUHelper = {
     }
 
     // TODO - Bug 820286: According to shouldIncludeCountryInitials, add
     // country initials to the resulting string.
     return resultString;
   }
 };
 
+/**
+ * Provide buffer with bitwise read/write function so make encoding/decoding easier.
+ */
+let BitBufferHelper = {
+  readCache: 0,
+  readCacheSize: 0,
+  readBuffer: [],
+  readIndex: 0,
+  writeCache: 0,
+  writeCacheSize: 0,
+  writeBuffer: [],
+
+  // Max length is 32 because we use integer as read/write cache.
+  // All read/write functions are implemented based on bitwise operation.
+  readBits: function readBits(length) {
+    if (length <= 0 || length > 32) {
+      return null;
+    }
+
+    if (length > this.readCacheSize) {
+      let bytesToRead = Math.ceil((length - this.readCacheSize) / 8);
+      for(let i = 0; i < bytesToRead; i++) {
+        this.readCache = (this.readCache << 8) | (this.readBuffer[this.readIndex++] & 0xFF);
+        this.readCacheSize += 8;
+      }
+    }
+
+    let bitOffset = (this.readCacheSize - length),
+        resultMask = (1 << length) - 1,
+        result = 0;
+
+    result = (this.readCache >> bitOffset) & resultMask;
+    this.readCacheSize -= length;
+
+    return result;
+  },
+
+  writeBits: function writeBits(value, length) {
+    if (length <= 0 || length > 32) {
+      return;
+    }
+
+    let totalLength = length + this.writeCacheSize;
+
+    // 8-byte cache not full
+    if (totalLength < 8) {
+      let valueMask = (1 << length) - 1;
+      this.writeCache = (this.writeCache << length) | (value & valueMask);
+      this.writeCacheSize += length;
+      return;
+    }
+
+    // Deal with unaligned part
+    if (this.writeCacheSize) {
+      let mergeLength = 8 - this.writeCacheSize,
+          valueMask = (1 << mergeLength) - 1;
+
+      this.writeCache = (this.writeCache << mergeLength) | ((value >> (length - mergeLength)) & valueMask);
+      this.writeBuffer.push(this.writeCache & 0xFF);
+      length -= mergeLength;
+    }
+
+    // Aligned part, just copy
+    while (length >= 8) {
+      length -= 8;
+      this.writeBuffer.push((value >> length) & 0xFF);
+    }
+
+    // Rest part is saved into cache
+    this.writeCacheSize = length;
+    this.writeCache = value & ((1 << length) - 1);
+
+    return;
+  },
+
+  // Drop what still in read cache and goto next 8-byte alignment.
+  // There might be a better naming.
+  nextOctetAlign: function nextOctetAlign() {
+    this.readCache = 0;
+    this.readCacheSize = 0;
+  },
+
+  // Flush current write cache to Buf with padding 0s.
+  // There might be a better naming.
+  flushWithPadding: function flushWithPadding() {
+    if (this.writeCacheSize) {
+      this.writeBuffer.push(this.writeCache << (8 - this.writeCacheSize));
+    }
+    this.writeCache = 0;
+    this.writeCacheSize = 0;
+  },
+
+  startWrite: function startWrite(dataBuffer) {
+    this.writeBuffer = dataBuffer;
+    this.writeCache = 0;
+    this.writeCacheSize = 0;
+  },
+
+  startRead: function startRead(dataBuffer) {
+    this.readBuffer = dataBuffer;
+    this.readCache = 0;
+    this.readCacheSize = 0;
+    this.readIndex = 0;
+  },
+
+  getWriteBufferSize: function getWriteBufferSize() {
+    return this.writeBuffer.length;
+  },
+
+  overwriteWriteBuffer: function overwriteWriteBuffer(position, data) {
+    let writeLength = data.length;
+    if (writeLength + position >= this.writeBuffer.length) {
+      writeLength = this.writeBuffer.length - position;
+    }
+    for (let i = 0; i < writeLength; i++) {
+      this.writeBuffer[i + position] = data[i];
+    }
+  }
+};
+
+/**
+ * Helper for CDMA PDU
+ *
+ * Currently, some function are shared with GsmPDUHelper, they should be
+ * moved from GsmPDUHelper to a common object shared among GsmPDUHelper and
+ * CdmaPDUHelper.
+ */
+let CdmaPDUHelper = {
+  //       1..........C
+  // Only "1234567890*#" is defined in C.S0005-D v2.0
+  dtmfChars: ".1234567890*#...",
+
+  /**
+   * Entry point for SMS encoding, the options object is made compatible
+   * with existing writeMessage() of GsmPDUHelper, but less key is used.
+   *
+   * Current used key in options:
+   * @param number
+   *        String containing the address (number) of the SMS receiver
+   * @param body
+   *        String containing the message to be sent, segmented part
+   * @param dcs
+   *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
+   *        constants.
+   * @param encodedBodyLength
+   *        Length of the user data when encoded with the given DCS. For UCS2,
+   *        in bytes; for 7-bit, in septets.
+   * @param requestStatusReport
+   *        Request status report.
+   * @param segmentRef
+   *        Reference number of concatenated SMS message
+   * @param segmentMaxSeq
+   *        Total number of concatenated SMS message
+   * @param segmentSeq
+   *        Sequence number of concatenated SMS message
+   */
+  writeMessage: function cdma_writeMessage(options) {
+    if (DEBUG) {
+      debug("cdma_writeMessage: " + JSON.stringify(options));
+    }
+
+    // Get encoding
+    options.encoding = this.gsmDcsToCdmaEncoding(options.dcs);
+
+    // Common Header
+    if (options.segmentMaxSeq > 1) {
+      this.writeInt(PDU_CDMA_MSG_TELESERIVCIE_ID_WEMT);
+    } else {
+      this.writeInt(PDU_CDMA_MSG_TELESERIVCIE_ID_SMS);
+    }
+
+    this.writeInt(0);
+    this.writeInt(PDU_CDMA_MSG_CATEGORY_UNSPEC);
+
+    // Just fill out address info in byte, rild will encap them for us
+    let addrInfo = this.encodeAddr(options.number);
+    this.writeByte(addrInfo.digitMode);
+    this.writeByte(addrInfo.numberMode);
+    this.writeByte(addrInfo.numberType);
+    this.writeByte(addrInfo.numberPlan);
+    this.writeByte(addrInfo.address.length);
+    for (let i = 0; i < addrInfo.address.length; i++) {
+      this.writeByte(addrInfo.address[i]);
+    }
+
+    // Subaddress, not supported
+    this.writeByte(0);  // Subaddress : Type
+    this.writeByte(0);  // Subaddress : Odd
+    this.writeByte(0);  // Subaddress : length
+
+    // User Data
+    let encodeResult = this.encodeUserData(options);
+    this.writeByte(encodeResult.length);
+    for (let i = 0; i < encodeResult.length; i++) {
+      this.writeByte(encodeResult[i]);
+    }
+
+    encodeResult = null;
+  },
+
+  /**
+   * Data writters
+   */
+  writeInt: function writeInt(value) {
+    Buf.writeUint32(value);
+  },
+
+  writeByte: function writeByte(value) {
+    Buf.writeUint32(value & 0xFF);
+  },
+
+  /**
+   * Transform GSM DCS to CDMA encoding.
+   */
+  gsmDcsToCdmaEncoding: function gsmDcsToCdmaEncoding(encoding) {
+    switch (encoding) {
+      case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
+        return PDU_CDMA_MSG_CODING_7BITS_ASCII;
+      case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
+        return PDU_CDMA_MSG_CODING_OCTET;
+      case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
+        return PDU_CDMA_MSG_CODING_UNICODE;
+    }
+  },
+
+  /**
+   * Encode address into CDMA address format, as a byte array.
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 3.4.3.3 Address Parameters
+   *
+   * @param address
+   *        String of address to be encoded
+   */
+  encodeAddr: function cdma_encodeAddr(address) {
+    let result = {};
+
+    result.numberType = PDU_CDMA_MSG_ADDR_NUMBER_TYPE_UNKNOWN;
+    result.numberPlan = PDU_CDMA_MSG_ADDR_NUMBER_TYPE_UNKNOWN;
+
+    if (address[0] === '+') {
+      address = address.substring(1);
+    }
+
+    // Try encode with DTMF first
+    result.digitMode = PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF;
+    result.numberMode = PDU_CDMA_MSG_ADDR_NUMBER_MODE_ANSI;
+
+    result.address = [];
+    for (let i = 0; i < address.length; i++) {
+      let addrDigit = this.dtmfChars.indexOf(address.charAt(i));
+      if (addrDigit < 0) {
+        result.digitMode = PDU_CDMA_MSG_ADDR_DIGIT_MODE_ASCII;
+        result.numberMode = PDU_CDMA_MSG_ADDR_NUMBER_MODE_ASCII;
+        result.address = [];
+        break;
+      }
+      result.address.push(addrDigit)
+    }
+
+    // Address can't be encoded with DTMF, then use 7-bit ASCII
+    if (result.digitMode !== PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF) {
+      if (address.indexOf("@") !== -1) {
+        result.numberType = PDU_CDMA_MSG_ADDR_NUMBER_TYPE_NATIONAL;
+      }
+
+      for (let i = 0; i < address.length; i++) {
+        result.address.push(address.charCodeAt(i) & 0x7F);
+      }
+    }
+
+    return result;
+  },
+
+  /**
+   * Encode SMS contents in options into CDMA userData field.
+   * Corresponding and required subparameters will be added automatically.
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 3.4.3.7 Bearer Data
+   *                           4.5 Bearer Data Parameters
+   *
+   * Current used key in options:
+   * @param body
+   *        String containing the message to be sent, segmented part
+   * @param encoding
+   *        Encoding method of CDMA, can be transformed from GSM DCS by function
+   *        cdmaPduHelp.gsmDcsToCdmaEncoding()
+   * @param encodedBodyLength
+   *        Length of the user data when encoded with the given DCS. For UCS2,
+   *        in bytes; for 7-bit, in septets.
+   * @param requestStatusReport
+   *        Request status report.
+   * @param segmentRef
+   *        Reference number of concatenated SMS message
+   * @param segmentMaxSeq
+   *        Total number of concatenated SMS message
+   * @param segmentSeq
+   *        Sequence number of concatenated SMS message
+   */
+  encodeUserData: function cdma_encodeUserData(options) {
+    let userDataBuffer = [];
+    BitBufferHelper.startWrite(userDataBuffer);
+
+    // Message Identifier
+    this.encodeUserDataMsgId(options);
+
+    // User Data
+    this.encodeUserDataMsg(options);
+
+    return userDataBuffer;
+  },
+
+  /**
+   * User data subparameter encoder : Message Identifier
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.1 Message Identifier
+   */
+  encodeUserDataMsgId: function cdma_encodeUserDataMsgId(options) {
+    BitBufferHelper.writeBits(PDU_CDMA_MSG_USERDATA_MSG_ID, 8);
+    BitBufferHelper.writeBits(3, 8);
+    BitBufferHelper.writeBits(PDU_CDMA_MSG_TYPE_SUBMIT, 4);
+    BitBufferHelper.writeBits(1, 16); // TODO: How to get message ID?
+    if (options.segmentMaxSeq > 1) {
+      BitBufferHelper.writeBits(1, 1);
+    } else {
+      BitBufferHelper.writeBits(0, 1);
+    }
+
+    BitBufferHelper.flushWithPadding();
+  },
+
+  /**
+   * User data subparameter encoder : User Data
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.2 User Data
+   */
+  encodeUserDataMsg: function cdma_encodeUserDataMsg(options) {
+    BitBufferHelper.writeBits(PDU_CDMA_MSG_USERDATA_BODY, 8);
+    // Reserve space for length
+    BitBufferHelper.writeBits(0, 8);
+    let lengthPosition = BitBufferHelper.getWriteBufferSize();
+
+    BitBufferHelper.writeBits(options.encoding, 5);
+
+    // Add user data header for message segement
+    let msgBody = options.body,
+        msgBodySize = (options.encoding === PDU_CDMA_MSG_CODING_7BITS_ASCII ?
+                       options.encodedBodyLength :
+                       msgBody.length);
+    if (options.segmentMaxSeq > 1) {
+      if (options.encoding === PDU_CDMA_MSG_CODING_7BITS_ASCII) {
+          BitBufferHelper.writeBits(msgBodySize + 7, 8); // Required length for user data header, in septet(7-bit)
+
+          BitBufferHelper.writeBits(5, 8);  // total header length 5 bytes
+          BitBufferHelper.writeBits(PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT, 8);  // header id 0
+          BitBufferHelper.writeBits(3, 8);  // length of element for id 0 is 3
+          BitBufferHelper.writeBits(options.segmentRef & 0xFF, 8);      // Segement reference
+          BitBufferHelper.writeBits(options.segmentMaxSeq & 0xFF, 8);   // Max segment
+          BitBufferHelper.writeBits(options.segmentSeq & 0xFF, 8);      // Current segment
+          BitBufferHelper.writeBits(0, 1);  // Padding to make header data septet(7-bit) aligned
+        } else {
+          if (options.encoding === PDU_CDMA_MSG_CODING_UNICODE) {
+            BitBufferHelper.writeBits(msgBodySize + 3, 8); // Required length for user data header, in 16-bit
+          } else {
+            BitBufferHelper.writeBits(msgBodySize + 6, 8); // Required length for user data header, in octet(8-bit)
+          }
+
+          BitBufferHelper.writeBits(5, 8);  // total header length 5 bytes
+          BitBufferHelper.writeBits(PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT, 8);  // header id 0
+          BitBufferHelper.writeBits(3, 8);  // length of element for id 0 is 3
+          BitBufferHelper.writeBits(options.segmentRef & 0xFF, 8);      // Segement reference
+          BitBufferHelper.writeBits(options.segmentMaxSeq & 0xFF, 8);   // Max segment
+          BitBufferHelper.writeBits(options.segmentSeq & 0xFF, 8);      // Current segment
+        }
+    } else {
+      BitBufferHelper.writeBits(msgBodySize, 8);
+    }
+
+    // Encode message based on encoding method
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    for (let i = 0; i < msgBody.length; i++) {
+      switch (options.encoding) {
+        case PDU_CDMA_MSG_CODING_OCTET: {
+          let msgDigit = msgBody.charCodeAt(i);
+          BitBufferHelper.writeBits(msgDigit, 8);
+          break;
+        }
+        case PDU_CDMA_MSG_CODING_7BITS_ASCII: {
+          let msgDigit = msgBody.charCodeAt(i),
+              msgDigitChar = msgBody.charAt(i);
+
+          if (msgDigit >= 32) {
+            BitBufferHelper.writeBits(msgDigit, 7);
+          } else {
+            msgDigit = langTable.indexOf(msgDigitChar);
+
+            if (msgDigit === PDU_NL_EXTENDED_ESCAPE) {
+              break;
+            }
+            if (msgDigit >= 0) {
+              BitBufferHelper.writeBits(msgDigit, 7);
+            } else {
+              msgDigit = langShiftTable.indexOf(msgDigitChar);
+              if (msgDigit == -1) {
+                throw new Error("'" + msgDigitChar + "' is not in 7 bit alphabet "
+                                + langIndex + ":" + langShiftIndex + "!");
+                break;
+              }
+
+              if (msgDigit === PDU_NL_RESERVED_CONTROL) {
+                break;
+              }
+
+              BitBufferHelper.writeBits(PDU_NL_EXTENDED_ESCAPE, 7);
+              BitBufferHelper.writeBits(msgDigit, 7);
+            }
+          }
+          break;
+        }
+        case PDU_CDMA_MSG_CODING_UNICODE: {
+          let msgDigit = msgBody.charCodeAt(i);
+          BitBufferHelper.writeBits(msgDigit, 16);
+          break;
+        }
+      }
+    }
+    BitBufferHelper.flushWithPadding();
+
+    // Fill length
+    let currentPosition = BitBufferHelper.getWriteBufferSize();
+    BitBufferHelper.overwriteWriteBuffer(lengthPosition - 1, [currentPosition - lengthPosition]);
+  },
+
+  /**
+   * Entry point for SMS decoding, the returned object is made compatible
+   * with existing readMessage() of GsmPDUHelper
+   */
+  readMessage: function cdma_readMessage() {
+    let message = {};
+
+    // Teleservice Identifier
+    message.teleservice = this.readInt();
+
+    // Message Type
+    let isServicePresent = this.readByte();
+    if (isServicePresent) {
+      message.messageType = PDU_CDMA_MSG_TYPE_BROADCAST;
+    } else {
+      if (message.teleservice) {
+        message.messageType = PDU_CDMA_MSG_TYPE_P2P;
+      } else {
+        message.messageType = PDU_CDMA_MSG_TYPE_ACK;
+      }
+    }
+
+    // Service Category
+    message.service = this.readInt();
+
+    // Originated Address
+    let addrInfo = {};
+    addrInfo.digitMode = (this.readInt() & 0x01);
+    addrInfo.numberMode = (this.readInt() & 0x01);
+    addrInfo.numberType = (this.readInt() & 0x01);
+    addrInfo.numberPlan = (this.readInt() & 0x01);
+    addrInfo.addrLength = this.readByte();
+    addrInfo.address = [];
+    for (let i = 0; i < addrInfo.addrLength; i++) {
+      addrInfo.address.push(this.readByte());
+    }
+    message.sender = this.decodeAddr(addrInfo);
+
+    // Originated Subaddress
+    addrInfo.Type = (this.readInt() & 0x07);
+    addrInfo.Odd = (this.readByte() & 0x01);
+    addrInfo.addrLength = this.readByte();
+    for (let i = 0; i < addrInfo.addrLength; i++) {
+      let addrDigit = this.readByte();
+      message.sender += String.fromCharCode(addrDigit);
+    }
+
+    // User Data
+    this.decodeUserData(message);
+
+    // Transform message to GSM msg
+    let msg = {
+      SMSC:           "",
+      mti:            0,
+      udhi:           0,
+      sender:         message.sender,
+      recipient:      null,
+      pid:            PDU_PID_DEFAULT,
+      epid:           PDU_PID_DEFAULT,
+      dcs:            0,
+      mwi:            null, //message[PDU_CDMA_MSG_USERDATA_BODY].header ? message[PDU_CDMA_MSG_USERDATA_BODY].header.mwi : null,
+      replace:        false,
+      header:         message[PDU_CDMA_MSG_USERDATA_BODY].header,
+      body:           message[PDU_CDMA_MSG_USERDATA_BODY].body,
+      data:           null,
+      timestamp:      message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
+      status:         null,
+      scts:           null,
+      dt:             null,
+      encoding:       message[PDU_CDMA_MSG_USERDATA_BODY].encoding,
+      messageClass:   GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL]
+    };
+
+    return msg;
+  },
+
+
+  /**
+   * Helper for processing received SMS parcel data.
+   *
+   * @param length
+   *        Length of SMS string in the incoming parcel.
+   *
+   * @return Message parsed or null for invalid message.
+   */
+  processReceivedSms: function cdma_processReceivedSms(length) {
+    if (!length) {
+      if (DEBUG) debug("Received empty SMS!");
+      return [null, PDU_FCS_UNSPECIFIED];
+    }
+
+    let message = this.readMessage();
+    if (DEBUG) debug("Got new SMS: " + JSON.stringify(message));
+
+    // Determine result
+    if (!message) {
+      return [null, PDU_FCS_UNSPECIFIED];
+    }
+
+    return [message, PDU_FCS_OK];
+  },
+
+  /**
+   * Data readers
+   */
+  readInt: function readInt() {
+    return Buf.readUint32();
+  },
+
+  readByte: function readByte() {
+    return (Buf.readUint32() & 0xFF);
+  },
+
+  /**
+   * Decode CDMA address data into address string
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 3.4.3.3 Address Parameters
+   *
+   * Required key in addrInfo
+   * @param addrLength
+   *        Length of address
+   * @param digitMode
+   *        Address encoding method
+   * @param address
+   *        Array of encoded address data
+   */
+  decodeAddr: function cdma_decodeAddr(addrInfo) {
+    let result = "";
+    for (let i = 0; i < addrInfo.addrLength; i++) {
+      if (addrInfo.digitMode === PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF) {
+        result += this.dtmfChars.charAt(addrInfo.address[i]);
+      } else {
+        result += String.fromCharCode(addrInfo.address[i]);
+      }
+    }
+    return result;
+  },
+
+  /**
+   * Read userData in parcel buffer and decode into message object.
+   * Each subparameter will be stored in corresponding key.
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 3.4.3.7 Bearer Data
+   *                           4.5 Bearer Data Parameters
+   */
+  decodeUserData: function cdma_decodeUserData(message) {
+    let userDataLength = this.readInt();
+
+    while (userDataLength > 0) {
+      let id = this.readByte(),
+          length = this.readByte(),
+          userDataBuffer = [];
+
+      for (let i = 0; i < length; i++) {
+          userDataBuffer.push(this.readByte());
+      }
+
+      BitBufferHelper.startRead(userDataBuffer);
+
+      switch (id) {
+        case PDU_CDMA_MSG_USERDATA_MSG_ID:
+          message[id] = this.decodeUserDataMsgId();
+          break;
+        case PDU_CDMA_MSG_USERDATA_BODY:
+          message[id] = this.decodeUserDataMsg(message[PDU_CDMA_MSG_USERDATA_MSG_ID].userHeader);
+          break;
+        case PDU_CDMA_MSG_USERDATA_TIMESTAMP:
+          message[id] = this.decodeUserDataTimestamp();
+          break;
+        case PDU_CDMA_REPLY_OPTION:
+          message[id] = this.decodeUserDataReplyAction();
+          break;
+        case PDU_CDMA_MSG_USERDATA_CALLBACK_NUMBER:
+          message[id] = this.decodeUserDataCallbackNumber();
+          break;
+      }
+
+      userDataLength -= (length + 2);
+      userDataBuffer = [];
+    }
+  },
+
+  /**
+   * User data subparameter decoder: Message Identifier
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.1 Message Identifier
+   */
+  decodeUserDataMsgId: function cdma_decodeUserDataMsgId() {
+    let result = {};
+    result.msgType = BitBufferHelper.readBits(4);
+    result.msgId = BitBufferHelper.readBits(16);
+    result.userHeader = BitBufferHelper.readBits(1);
+
+    return result;
+  },
+
+  /**
+   * Decode user data header, we only care about segment information
+   * on CDMA.
+   *
+   * This function is mostly copied from gsmPduHelper.readUserDataHeader() but
+   * change the read function, because CDMA user header decoding is't byte-wise
+   * aligned.
+   */
+  decodeUserDataHeader: function cdma_decodeUserDataHeader(encoding) {
+    let header = {},
+        headerSize = BitBufferHelper.readBits(8),
+        userDataHeaderSize = headerSize + 1,
+        headerPaddingBits = 0;
+
+    // Calculate header size
+    if (encoding === PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+      // Length is in 7-bit
+      header.length = Math.ceil(userDataHeaderSize * 8 / 7);
+      // Calulate padding length
+      headerPaddingBits = (header.length * 7) - (userDataHeaderSize * 8);
+    } else if (encoding === PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
+      header.length = userDataHeaderSize;
+    } else {
+      header.length = userDataHeaderSize / 2;
+    }
+
+    while (headerSize) {
+      let identifier = BitBufferHelper.readBits(8),
+          length = BitBufferHelper.readBits(8);
+
+      headerSize -= (2 + length);
+
+      switch (identifier) {
+        case PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT: {
+            let ref = BitBufferHelper.readBits(8),
+                max = BitBufferHelper.readBits(8),
+                seq = BitBufferHelper.readBits(8);
+            if (max && seq && (seq <= max)) {
+              header.segmentRef = ref;
+              header.segmentMaxSeq = max;
+              header.segmentSeq = seq;
+            }
+          break;
+        }
+        case PDU_IEI_APPLICATION_PORT_ADDRESSING_SCHEME_8BIT: {
+          let dstp = BitBufferHelper.readBits(8),
+              orip = BitBufferHelper.readBits(8);
+          if ((dstp < PDU_APA_RESERVED_8BIT_PORTS)
+              || (orip < PDU_APA_RESERVED_8BIT_PORTS)) {
+            // 3GPP TS 23.040 clause 9.2.3.24.3: "A receiving entity shall
+            // ignore any information element where the value of the
+            // Information-Element-Data is Reserved or not supported"
+            break;
+          }
+          header.destinationPort = dstp;
+          header.originatorPort = orip;
+          break;
+        }
+        case PDU_IEI_APPLICATION_PORT_ADDRESSING_SCHEME_16BIT: {
+          let dstp = (BitBufferHelper.readBits(8) << 8) | BitBufferHelper.readBits(8),
+              orip = (BitBufferHelper.readBits(8) << 8) | BitBufferHelper.readBits(8);
+          // 3GPP TS 23.040 clause 9.2.3.24.4: "A receiving entity shall
+          // ignore any information element where the value of the
+          // Information-Element-Data is Reserved or not supported"
+          if ((dstp < PDU_APA_VALID_16BIT_PORTS)
+              && (orip < PDU_APA_VALID_16BIT_PORTS)) {
+            header.destinationPort = dstp;
+            header.originatorPort = orip;
+          }
+          break;
+        }
+        case PDU_IEI_CONCATENATED_SHORT_MESSAGES_16BIT: {
+          let ref = (BitBufferHelper.readBits(8) << 8) | BitBufferHelper.readBits(8),
+              max = BitBufferHelper.readBits(8),
+              seq = BitBufferHelper.readBits(8);
+          if (max && seq && (seq <= max)) {
+            header.segmentRef = ref;
+            header.segmentMaxSeq = max;
+            header.segmentSeq = seq;
+          }
+          break;
+        }
+        case PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT: {
+          let langShiftIndex = BitBufferHelper.readBits(8);
+          if (langShiftIndex < PDU_NL_SINGLE_SHIFT_TABLES.length) {
+            header.langShiftIndex = langShiftIndex;
+          }
+          break;
+        }
+        case PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT: {
+          let langIndex = BitBufferHelper.readBits(8);
+          if (langIndex < PDU_NL_LOCKING_SHIFT_TABLES.length) {
+            header.langIndex = langIndex;
+          }
+          break;
+        }
+        case PDU_IEI_SPECIAL_SMS_MESSAGE_INDICATION: {
+          let msgInd = BitBufferHelper.readBits(8) & 0xFF,
+              msgCount = BitBufferHelper.readBits(8);
+          /*
+           * TS 23.040 V6.8.1 Sec 9.2.3.24.2
+           * bits 1 0   : basic message indication type
+           * bits 4 3 2 : extended message indication type
+           * bits 6 5   : Profile id
+           * bit  7     : storage type
+           */
+          let storeType = msgInd & PDU_MWI_STORE_TYPE_BIT;
+          header.mwi = {};
+          mwi = header.mwi;
+
+          if (storeType == PDU_MWI_STORE_TYPE_STORE) {
+            // Store message because TP_UDH indicates so, note this may override
+            // the setting in DCS, but that is expected
+            mwi.discard = false;
+          } else if (mwi.discard === undefined) {
+            // storeType == PDU_MWI_STORE_TYPE_DISCARD
+            // only override mwi.discard here if it hasn't already been set
+            mwi.discard = true;
+          }
+
+          mwi.msgCount = msgCount & 0xFF;
+          mwi.active = mwi.msgCount > 0;
+
+          if (DEBUG) debug("MWI in TP_UDH received: " + JSON.stringify(mwi));
+
+          break;
+        }
+        default: {
+          // Drop unsupported id
+          for (let i = 0; i < length; i++) {
+            BitBufferHelper.readBits(8);
+          }
+          break;
+        }
+      }
+    }
+
+    // Consume padding bits
+    if (headerPaddingBits) {
+      BitBufferHelper.readBits(headerPaddingBits);
+    }
+
+    return header;
+  },
+
+  /**
+   * User data subparameter decoder : User Data
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.2 User Data
+   */
+  decodeUserDataMsg: function cdma_decodeUserDataMsg(hasUserHeader) {
+    let result = {},
+        encoding = BitBufferHelper.readBits(5);
+
+    if(encoding === PDU_CDMA_MSG_CODING_IS_91) {
+      let msgType = BitBufferHelper.readBits(8);
+    }
+
+    let msgBodySize = BitBufferHelper.readBits(8);
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    // Determine encoding method
+    switch (encoding) {
+      case PDU_CDMA_MSG_CODING_7BITS_ASCII:
+      case PDU_CDMA_MSG_CODING_IA5:
+      case PDU_CDMA_MSG_CODING_7BITS_GSM:
+        result.encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
+        break;
+      case PDU_CDMA_MSG_CODING_OCTET:
+      case PDU_CDMA_MSG_CODING_IS_91:
+      case PDU_CDMA_MSG_CODING_LATIN_HEBREW:
+      case PDU_CDMA_MSG_CODING_LATIN:
+        result.encoding = PDU_DCS_MSG_CODING_8BITS_ALPHABET;
+        break;
+      case PDU_CDMA_MSG_CODING_UNICODE:
+      case PDU_CDMA_MSG_CODING_SHIFT_JIS:
+      case PDU_CDMA_MSG_CODING_KOREAN:
+        result.encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
+        break;
+    }
+
+    // For segmented SMS, a user header is included before sms content
+    if (hasUserHeader) {
+      result.header = this.decodeUserDataHeader(result.encoding);
+      // header size is included in body size, they are decoded
+      msgBodySize -= result.header.length;
+    }
+
+    // Decode sms content
+    result.body = "";
+    let msgDigit;
+    switch (encoding) {
+      case PDU_CDMA_MSG_CODING_OCTET:         // TODO : Require Test
+        while(msgBodySize > 0) {
+          msgDigit = String.fromCharCode(BitBufferHelper.readBits(8));
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_IS_91:         // TODO : Require Test
+        // Referenced from android code
+        switch (msgType) {
+          case PDU_CDMA_MSG_CODING_IS_91_TYPE_SMS:
+          case PDU_CDMA_MSG_CODING_IS_91_TYPE_SMS_FULL:
+          case PDU_CDMA_MSG_CODING_IS_91_TYPE_VOICEMAIL_STATUS:
+            while(msgBodySize > 0) {
+              msgDigit = String.fromCharCode(BitBufferHelper.readBits(6) + 0x20);
+              result.body += msgDigit;
+              msgBodySize--;
+            }
+            break;
+          case PDU_CDMA_MSG_CODING_IS_91_TYPE_CLI:
+            let addrInfo = {};
+            addrInfo.digitMode = PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF;
+            addrInfo.numberMode = PDU_CDMA_MSG_ADDR_NUMBER_MODE_ANSI;
+            addrInfo.numberType = PDU_CDMA_MSG_ADDR_NUMBER_TYPE_UNKNOWN;
+            addrInfo.numberPlan = PDU_CDMA_MSG_ADDR_NUMBER_PLAN_UNKNOWN;
+            addrInfo.addrLength = msgBodySize;
+            addrInfo.address = [];
+            for (let i = 0; i < addrInfo.addrLength; i++) {
+              addrInfo.address.push(BitBufferHelper.readBits(4));
+            }
+            result.body = this.decodeAddr(addrInfo);
+            break;
+        }
+      case PDU_CDMA_MSG_CODING_7BITS_ASCII:
+      case PDU_CDMA_MSG_CODING_IA5:           // TODO : Require Test
+        while(msgBodySize > 0) {
+          msgDigit = BitBufferHelper.readBits(7);
+          if (msgDigit >= 32) {
+            msgDigit = String.fromCharCode(msgDigit);
+          } else {
+            if (msgDigit !== PDU_NL_EXTENDED_ESCAPE) {
+              msgDigit = langTable[msgDigit];
+            } else {
+              msgDigit = BitBufferHelper.readBits(7);
+              msgBodySize--;
+              msgDigit = langShiftTable[msgDigit];
+            }
+          }
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_UNICODE:
+        while(msgBodySize > 0) {
+          msgDigit = String.fromCharCode(BitBufferHelper.readBits(16));
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_7BITS_GSM:     // TODO : Require Test
+        while(msgBodySize > 0) {
+          msgDigit = BitBufferHelper.readBits(7);
+          if (msgDigit !== PDU_NL_EXTENDED_ESCAPE) {
+            msgDigit = langTable[msgDigit];
+          } else {
+            msgDigit = BitBufferHelper.readBits(7);
+            msgBodySize--;
+            msgDigit = langShiftTable[msgDigit];
+          }
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_LATIN:         // TODO : Require Test
+        // Reference : http://en.wikipedia.org/wiki/ISO/IEC_8859-1
+        while(msgBodySize > 0) {
+          msgDigit = String.fromCharCode(BitBufferHelper.readBits(8));
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_LATIN_HEBREW:  // TODO : Require Test
+        // Reference : http://en.wikipedia.org/wiki/ISO/IEC_8859-8
+        while(msgBodySize > 0) {
+          msgDigit = BitBufferHelper.readBits(8);
+          if (msgDigit === 0xDF) {
+            msgDigit = String.fromCharCode(0x2017);
+          } else if (msgDigit === 0xFD) {
+            msgDigit = String.fromCharCode(0x200E);
+          } else if (msgDigit === 0xFE) {
+            msgDigit = String.fromCharCode(0x200F);
+          } else if (msgDigit >= 0xE0 && msgDigit <= 0xFA) {
+            msgDigit = String.fromCharCode(0x4F0 + msgDigit);
+          } else {
+            msgDigit = String.fromCharCode(msgDigit);
+          }
+          result.body += msgDigit;
+          msgBodySize--;
+        }
+        break;
+      case PDU_CDMA_MSG_CODING_SHIFT_JIS:
+        // Reference : http://msdn.microsoft.com/en-US/goglobal/cc305152.aspx
+        //             http://demo.icu-project.org/icu-bin/convexp?conv=Shift_JIS
+      case PDU_CDMA_MSG_CODING_KOREAN:
+      case PDU_CDMA_MSG_CODING_GSM_DCS:
+      default:
+        break;
+    }
+
+    return result;
+  },
+
+  decodeBcd: function cdma_decodeBcd(value) {
+    return ((value >> 4) & 0xF) * 10 + (value & 0x0F);
+  },
+
+  /**
+   * User data subparameter decoder : Time Stamp
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.4 Message Center Time Stamp
+   */
+  decodeUserDataTimestamp: function cdma_decodeUserDataTimestamp() {
+    let year = this.decodeBcd(BitBufferHelper.readBits(8)),
+        month = this.decodeBcd(BitBufferHelper.readBits(8)) - 1,
+        date = this.decodeBcd(BitBufferHelper.readBits(8)),
+        hour = this.decodeBcd(BitBufferHelper.readBits(8)),
+        min = this.decodeBcd(BitBufferHelper.readBits(8)),
+        sec = this.decodeBcd(BitBufferHelper.readBits(8));
+
+    if (year >= 96 && year <= 99) {
+      year += 1900;
+    } else {
+      year += 2000;
+    }
+
+    let result = (new Date(year, month, date, hour, min, sec, 0)).valueOf();
+
+    return result;
+  },
+
+  /**
+   * User data subparameter decoder : Reply Option
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.11 Reply Option
+   */
+  decodeUserDataReplyAction: function cdma_decodeUserDataReplyAction() {
+    let replyAction = BitBufferHelper.readBits(4),
+        result = { userAck: (replyAction & 0x8) ? true : false,
+                   deliverAck: (replyAction & 0x4) ? true : false,
+                   readAck: (replyAction & 0x2) ? true : false,
+                   report: (replyAction & 0x1) ? true : false
+                 };
+
+    return result;
+  },
+
+  /**
+   * User data subparameter decoder : Call-Back Number
+   *
+   * @see 3GGP2 C.S0015-B 2.0, 4.5.15 Call-Back Number
+   */
+  decodeUserDataCallbackNumber: function cdma_decodeUserDataCallbackNumber() {
+    let digitMode = BitBufferHelper.readBits(1);
+    if (digitMode) {
+      let numberType = BitBufferHelper.readBits(3),
+          numberPlan = BitBufferHelper.readBits(4);
+    }
+    let numberFields = BitBufferHelper.readBits(8),
+        result = "";
+    for (let i = 0; i < numberFields; i++) {
+      if (digitMode === PDU_CDMA_MSG_ADDR_DIGIT_MODE_DTMF) {
+        let addrDigit = BitBufferHelper.readBits(4);
+        result += this.dtmfChars.charAt(addrDigit);
+      } else {
+        let addrDigit = BitBufferHelper.readBits(8);
+        result += String.fromCharCode(addrDigit);
+      }
+    }
+
+    return result;
+  }
+};
+
 let StkCommandParamsFactory = {
   createParam: function createParam(cmdDetails, ctlvs) {
     let param;
     switch (cmdDetails.typeOfCommand) {
       case STK_CMD_REFRESH:
         param = this.processRefresh(cmdDetails, ctlvs);
         break;
       case STK_CMD_POLL_INTERVAL:
--- a/dom/tests/mochitest/bugs/test_bug479143.html
+++ b/dom/tests/mochitest/bugs/test_bug479143.html
@@ -11,31 +11,42 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479143">Mozilla Bug 479143</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+  if (!navigator.platform.startsWith("Win")) {
+    SimpleTest.expectAssertions(1);
+  }
+
   SimpleTest.waitForExplicitFinish();
 
   setTimeout(function() {
     var interval = setInterval(function() { var i = 0; i++; }, 10);
 
     var xhr = new XMLHttpRequest();
     xhr.open("GET", "test_bug479143.html", false);
     xhr.send(null);
 
     window.showModalDialog("javascript:" +
                            "setTimeout(function() { window.close(); }, 1000);",
                            null);
 
     clearInterval(interval);
 
     ok(true, "did not crash");
+
+    // Garbage collecting the windows created in this test can cause
+    // assertions, so GC now to blame those assertions to this test.
+    // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
+    // bug 600703)
+    SpecialPowers.gc();
+
     SimpleTest.finish();
   }, 0);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug504862.html
+++ b/dom/tests/mochitest/bugs/test_bug504862.html
@@ -8,17 +8,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="runTest()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=504862">Mozilla Bug 504862</a>
 <script class="testbody" type="text/javascript">
 
 if (!navigator.platform.startsWith("Win")) {
-  SimpleTest.expectAssertions(1);
+  SimpleTest.expectAssertions(4);
 }
 
 /** Test for Bug 504862 **/
 SimpleTest.waitForExplicitFinish();
 function onMsgRcv(event)
 {
   is(event.data, "args: undefined", "Unexpected cross origin dialog arguments.");
 }
--- a/dom/tests/mochitest/bugs/test_bug61098.html
+++ b/dom/tests/mochitest/bugs/test_bug61098.html
@@ -16,17 +16,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 </p>
 <div id="content" style="display: none"> 
 </div>
 <pre id="test">
 </pre>
 <script class="testbody" type="text/javascript">
 /** Test for Bug 61098 **/
 
-SimpleTest.expectAssertions(8);
+if (!navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(9);
+} else {
+  SimpleTest.expectAssertions(8);
+}
 
 SimpleTest.waitForExplicitFinish();
 
 var mockPromptServiceRegisterer, mockPromptFactoryRegisterer;
 
 var promptState;
 
 function registerMockPromptService()
@@ -342,14 +346,20 @@ function runtests()
 
   w.close();
 
   resetDialogLoopBlocking();
 
   mockPromptFactoryRegisterer.unregister();
   mockPromptServiceRegisterer.unregister();
 
+  // Garbage collecting the windows created in this test can cause
+  // assertions, so GC now to blame those assertions to this test.
+  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
+  // bug 600703)
+  SpecialPowers.gc();
+
   SimpleTest.finish();
 }
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/chrome/selectAtPoint.html
+++ b/dom/tests/mochitest/chrome/selectAtPoint.html
@@ -159,18 +159,18 @@
                         .defaultView
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
 
     frame.contentWindow.scrollTo(0, 0);
 
     let rect = frame.getBoundingClientRect();
 
-    let targetPoint = { xPos: rect.left + (charDims.width / 2),
-                        yPos: rect.top + (charDims.height / 2) };
+    let targetPoint = { xPos: charDims.width / 2,
+                        yPos: charDims.height / 2 };
     setSingle(dwuFrame, targetPoint.xPos, targetPoint.yPos, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE);
     checkSelection(frame.contentWindow.document, "SELECT_WORDNOSPACE", "ttestselection2");
     setSingle(dwuFrame, targetPoint.xPos, targetPoint.yPos, Ci.nsIDOMWindowUtils.SELECT_WORD);
     if (isLinux || isMac) {
       checkSelection(frame.contentWindow.document, "SELECT_WORD", "ttestselection2");
     } else if (isWindows) {
       checkSelection(frame.contentWindow.document, "SELECT_WORD", "ttestselection2 ");
     }
--- a/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
@@ -12,16 +12,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   <body>
     <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
       Mozilla Bug 633602
     </a>
     <div id="content">
     </div>
     <pre id="test">
       <script type="application/javascript">
+
+        if (!navigator.platform.startsWith("Linux")) {
+          SimpleTest.expectAssertions(0, 1); // bug 845552
+        }
+
         /**
          * Pointer Lock tests for bug 633602.  These depend on the fullscreen api
          * which doesn't work when run in the mochitests' iframe, since the
          * mochitests' iframe doesn't have an allowfullscreen attribute.  To get
          * around this, all tests are run in a child window, which can go fullscreen.
          * This method is borrowed from content/html/content/test/test_fullscreen-api.html.
          **/
 
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
@@ -3984,5596 +3984,5598 @@ 4145c6718,6719
 < Hellenize
 ---
 > Hellenize/DSG
 > Heller
 4146a6721
 > Helli/M
 4149a6725
 > Helsa/M
-4152a6729
+4151a6728
+> Helvetica
+4152a6730
 > Helyn/M
-4156c6733,6735
+4156c6734,6736
 < Hendricks
 ---
 > Hendrick/MS
 > Hendrik/M
 > Hendrika/M
-4157a6737
+4157a6738
 > Henka/M
-4160a6741,6742
+4160a6742,6743
 > Henrie/M
 > Henrieta/M
-4161a6744,6745
+4161a6745,6746
 > Henriette/M
 > Henrik/M
-4162a6747
+4162a6748
 > Henryetta/M
-4166a6752
+4166a6753
 > Hephzibah/M
-4171a6758
+4171a6759
 > Herb/M
-4173a6761,6763
+4173a6762,6764
 > Herbie/M
 > Herby/M
 > Herc/M
-4174a6765
+4174a6766
 > Hercule/MS
-4176a6768
+4176a6769
 > Herculie/M
-4181a6774
+4181a6775
 > Hermann/M
-4183a6777,6780
+4183a6778,6781
 > Hermia/M
 > Hermie/M
 > Hermina/M
 > Hermine/M
-4184a6782
+4184a6783
 > Hermione/M
-4186a6785
+4186a6786
 > Hermon
-4187a6787
+4187a6788
 > Hermy/M
-4188a6789
+4188a6790
 > Hernando/M
-4190a6792
+4190a6793
 > Herold/M
-4194a6797
+4194a6798
 > Hersch/M
-4196a6800
+4196a6801
 > Hersh/M
-4198a6803,6804
+4198a6804,6805
 > Herta/M
 > Hertha/M
-4200a6807,6808
+4200a6808,6809
 > Herve/M
 > Hervey/M
-4209a6818,6819
+4209a6819,6820
 > Hesther/M
 > Hestia/M
-4210a6821
+4210a6822
 > Hetti/M
-4211a6823,6828
+4211a6824,6829
 > Hetty/M
 > Hew/M
 > Hewe/M
 > Hewet/M
 > Hewett/M
 > Hewie/M
-4219a6837
+4219a6838
 > Hi/M
-4233a6852
+4233a6853
 > Hilarius/M
-4236a6856,6858
+4236a6857,6859
 > Hildagard/M
 > Hildagarde/M
 > Hilde/M
-4237a6860,6862
+4237a6861,6863
 > Hildegaard/M
 > Hildegarde/M
 > Hildy/M
-4239a6865
+4239a6866
 > Hillard/M
-4241a6868,6874
+4241a6869,6875
 > Hillery/M
 > Hilliard
 > Hilliary/M
 > Hillie/M
 > Hillier/M
 > Hilly/RM
 > Hillyer/M
-4246a6880
+4246a6881
 > Hinda/M
-4255a6890
+4255a6891
 > Hinze/M
-4262a6898
+4262a6899
 > Hirsch/M
-4271a6908
+4271a6909
 > Hobard/M
-4274a6912,6913
+4274a6913,6914
 > Hobey/M
 > Hobie/M
-4277a6917
+4277a6918
 > Hoebart/M
-4301a6942
+4301a6943
 > Holli/SM
-4303d6943
+4303d6944
 < Hollis
-4305a6946
+4305a6947
 > Hollyanne/M
-4314a6956
+4314a6957
 > Homere/M
-4315a6958
+4315a6959
 > Homerus/M
-4320a6964
+4320a6965
 > Honey/M
-4324c6968,6969
+4324c6969,6970
 < Honorable
 ---
 > Honor/B
 > Honoria/M
-4338a6984
+4338a6985
 > Horatia/M
-4339a6986
+4339a6987
 > Horatius/M
-4345a6993,6997
+4345a6994,6998
 > Horst/M
 > Hort/MN
 > Horten/M
 > Hortense
 > Hortensia/M
-4361a7014,7015
+4361a7015,7016
 > Howey/M
 > Howie/M
-4363a7018
+4363a7019
 > Hoyt/M
-4368a7024
+4368a7025
 > Hube/RM
-4370a7027,7029
+4370a7028,7030
 > Huberto/M
 > Hubey/M
 > Hubie/M
-4379a7039,7040
+4379a7040,7041
 > Hughie
 > Hugibert/M
-4381a7043
+4381a7044
 > Hugues/M
-4383a7046
+4383a7047
 > Hulda/M
-4384a7048
+4384a7049
 > Humbert/M
-4387a7052,7054
+4387a7053,7055
 > Humfrey/M
 > Humfrid/M
 > Humfried/M
-4391a7059
+4391a7060
 > Hunfredo/M
-4398a7067
+4398a7068
 > Huntlee/M
-4400a7070,7071
+4400a7071,7072
 > Hurlee/M
 > Hurleigh/M
-4404a7076
+4404a7077
 > Husein/M
-4413a7086,7091
+4413a7087,7092
 > Hy/M
 > Hyacinth/M
 > Hyacintha/M
 > Hyacinthe/M
 > Hyacinthia/M
 > Hyacinthie/M
-4414a7093
+4414a7094
 > Hyatt/M
-4417a7097
+4417a7098
 > Hyman/M
-4418a7099,7100
+4418a7100,7101
 > Hymie
 > Hynda/M
-4455a7138
+4455a7139
 > Iain/M
-4456a7140
+4456a7141
 > Ianthe/M
-4458a7143,7144
+4458a7144,7145
 > Ibbie/M
 > Ibby/M
-4463a7150
+4463a7151
 > Ibrahim/M
-4470a7158
+4470a7159
 > Ichabod/M
-4474a7163,7168
+4474a7164,7169
 > Idalia/M
 > Idalina/M
 > Idaline/M
 > Idell/M
 > Idelle/M
 > Idette/M
-4475a7170,7172
+4475a7171,7173
 > Iggie/M
 > Iggy/M
 > Ignace/M
-4476a7174
+4476a7175
 > Ignacius/M
-4477a7176,7177
+4477a7177,7178
 > Ignaz/M
 > Ignazio/M
-4483c7183,7184
+4483c7184,7185
 < Ikea/M
 ---
 > IKEA/M
 > Ikey/M
-4485a7187,7190
+4485a7188,7191
 > Ilaire/M
 > Ilario/M
 > Ileana/M
 > Ileane/M
-4487a7193,7194
+4487a7194,7195
 > Ilise/M
 > Ilka/M
-4488a7196
+4488a7197
 > Illa/M
-4491a7200,7204
+4491a7201,7205
 > Ilsa/M
 > Ilse/M
 > Ilysa/M
 > Ilyse/M
 > Ilyssa/M
-4495a7209
+4495a7210
 > Immanuel
-4496a7211
+4496a7212
 > Imogen/M
-4497a7213
+4497a7214
 > Imojean/M
-4499c7215
+4499c7216
 < In/M
 ---
 > In/MP
-4522a7239,7240
+4522a7240,7241
 > Inesita/M
 > Inessa/M
-4524c7242,7253
+4524c7243,7254
 < Inge
 ---
 > Inga/M
 > Ingaberg/M
 > Ingaborg/M
 > Ingamar/M
 > Ingar/M
 > Inge/R
 > Ingeberg/M
 > Ingeborg/M
 > Ingelbert/M
 > Ingemar/M
 > Inger/M
 > Inglebert/M
-4525a7255,7257
+4525a7256,7258
 > Inglis/M
 > Ingmar/M
 > Ingra/M
-4528a7261,7266
+4528a7262,7267
 > Ingrim/M
 > Ingunna/M
 > Inigo/M
 > Inna/M
 > Inness/M
 > Innis/M
-4543a7282,7284
+4543a7283,7285
 > Iolande/M
 > Iolanthe/M
 > Iona
-4546a7288,7290
+4546a7289,7291
 > Iorgo/MS
 > Iormina/M
 > Iosep/M
-4560a7305
+4560a7306
 > Irena/M
-4561a7307
+4561a7308
 > Irina/M
-4567a7314
+4567a7315
 > Irita/M
-4573a7321
+4573a7322
 > Irv/MG
-4577a7326,7327
+4577a7327,7328
 > Irwinn/M
 > Isa
-4578a7329
+4578a7330
 > Isaak/M
-4579a7331
+4579a7332
 > Isabelita/M
-4581a7334,7339
+4581a7335,7340
 > Isac/M
 > Isacco/M
 > Isador/M
 > Isadora/M
 > Isadore/M
 > Isahella/M
-4582a7341
+4582a7342
 > Isak/M
-4583a7343
+4583a7344
 > Iseabal/M
-4589a7350,7354
+4589a7351,7355
 > Isiahi/M
 > Isidor/M
 > Isidora/M
 > Isidore/M
 > Isidoro/M
-4598a7364
+4598a7365
 > Isobel/M
-4606a7373,7376
+4606a7374,7377
 > Issi/M
 > Issiah/M
 > Issie/M
 > Issy/M
-4615a7386
+4615a7387
 > Itch/M
-4622c7393,7400
+4622c7394,7401
 < Ives
 ---
 > Ivar/M
 > Ive/RSM
 > Iver/M
 > Ivett/M
 > Ivette/M
 > Ivie/M
 > Ivonne/M
 > Ivor/M
-4627a7406,7407
+4627a7407,7408
 > Izabel/M
 > Izak/M
-4634c7414,7415
+4634c7415,7416
 < J/MD
 ---
 > Izzy/M
 > J/MDNX
-4639a7421,7426
+4639a7422,7427
 > Jabez/M
 > Jacenta/M
 > Jacinda/M
 > Jacinta/M
 > Jacintha/M
 > Jacinthe/M
-4640a7428,7429
+4640a7429,7430
 > Jackelyn/M
 > Jacki/M
-4641a7431
+4641a7432
 > Jacklin/M
-4642a7433,7434
+4642a7434,7435
 > Jackquelin/M
 > Jackqueline/M
-4646a7439
+4646a7440
 > Jaclin/M
-4652a7446
+4652a7447
 > Jacobo/M
-4654a7449
+4654a7450
 > Jacquelin/M
-4656a7452,7454
+4656a7453,7455
 > Jacquelynn/M
 > Jacquenetta/M
 > Jacquenette/M
-4657a7456,7459
+4657a7457,7460
 > Jacquetta/M
 > Jacquette/M
 > Jacqui/M
 > Jacquie/M
-4658a7461,7464
+4658a7462,7465
 > Jacynth/M
 > Jada/M
 > Jade/M
 > Jae/M
-4663a7470
+4663a7471
 > Jaimie/M
-4664a7472
+4664a7473
 > Jaine/M
-4668a7477,7478
+4668a7478,7479
 > Jakie/M
 > Jakob/M
-4676a7487
+4676a7488
 > Jameson
-4677a7489,7490
+4677a7490,7491
 > Jamesy/M
 > Jamey/M
-4679a7493,7498
+4679a7494,7499
 > Jamil/M
 > Jamill/M
 > Jamima/M
 > Jamison/M
 > Jammal/M
 > Jammie/M
-4683c7502,7511
+4683c7503,7512
 < Jane
 ---
 > Janaya/M
 > Janaye/M
 > Jandy/M
 > Jane/M
 > Janean/M
 > Janeczka/M
 > Janeen/M
 > Janek/M
 > Janel/M
 > Janela/M
-4684a7513
+4684a7514
 > Janella/M
-4686c7515,7520
+4686c7516,7521
 < Janet
 ---
 > Janene/M
 > Janenna/M
 > Janessa/M
 > Janet/M
 > Janeta/M
 > Janetta/M
-4687a7522,7524
+4687a7523,7525
 > Janeva/M
 > Janey/M
 > Jania/M
-4689a7527,7528
+4689a7528,7529
 > Janifer/M
 > Janina
-4692a7532
+4692a7533
 > Janith/M
-4693a7534
+4693a7535
 > Janka/M
-4694a7536,7537
+4694a7537,7538
 > Jannel/M
 > Jannelle/M
-4695a7539,7540
+4695a7540,7541
 > Janos/M
 > Janot/M
-4699a7545
+4699a7546
 > Jany/M
-4702a7549,7556
+4702a7550,7557
 > Jaquelin/M
 > Jaquelyn/M
 > Jaquenetta/M
 > Jaquenette/M
 > Jaquith/M
 > Jarad/M
 > Jard/M
 > Jareb/M
-4703a7558,7559
+4703a7559,7560
 > Jarib/M
 > Jarid/M
-4704a7561
+4704a7562
 > Jarrad/M
-4705a7563
+4705a7564
 > Jarret/M
-4706a7565
+4706a7566
 > Jarrid/M
-4708a7568,7571
+4708a7569,7572
 > Jase/M
 > Jasen/M
 > Jasmin/M
 > Jasmina/M
-4711a7575
+4711a7576
 > Jasun/M
-4714d7577
+4714d7578
 < JavaScript/M
-4721a7585,7588
+4721a7586,7589
 > Jaye/M
 > Jayme/M
 > Jaymee/M
 > Jaymie/M
-4722a7590
+4722a7591
 > Jaynell/M
-4723a7592,7593
+4723a7593,7594
 > Jazmin/M
 > Jdavie/M
-4724a7595,7597
+4724a7596,7598
 > Jeana/M
 > Jeane/M
 > Jeanelle/M
-4727a7601
+4727a7602
 > Jeanna/M
-4731a7606
+4731a7607
 > Jecho/M
-4732a7608,7610
+4732a7609,7611
 > Jedd/M
 > Jeddy/M
 > Jedediah/M
-4733a7612
+4733a7613
 > Jedidiah/M
-4740a7620
+4740a7621
 > Jeffie/M
-4742a7623,7624
+4742a7624,7625
 > Jeffy/M
 > Jehanna/M
-4744a7627
+4744a7628
 > Jehu
-4745a7629,7641
+4745a7630,7642
 > Jelene/M
 > Jemie/M
 > Jemima/M
 > Jemimah/M
 > Jemmie/M
 > Jemmy/M
 > Jen/M
 > Jena/M
 > Jenda/M
 > Jenelle/M
 > Jeni/M
 > Jenica/M
 > Jeniece/M
-4746a7643,7645
+4746a7644,7646
 > Jeniffer/M
 > Jenilee/M
 > Jenine/M
-4747a7647
+4747a7648
 > Jenn/MRJ
-4749c7649,7652
+4749c7650,7653
 < Jenner
 ---
 > Jennee/M
 > Jennette/M
 > Jenni/M
 > Jennica/M
-4751a7655,7656
+4751a7656,7657
 > Jennilee/M
 > Jennine/M
-4753a7659
+4753a7660
 > Jeno/M
-4755a7662
+4755a7663
 > Jerad/M
-4756a7664,7668
+4756a7665,7669
 > Jeralee/M
 > Jeramey/M
 > Jeramie/M
 > Jere/M
 > Jereme/M
-4758a7671,7672
+4758a7672,7673
 > Jeremias/M
 > Jeremie/M
-4761a7676
+4761a7677
 > Jermain/M
-4762a7678
+4762a7679
 > Jermayne/M
-4765a7682
+4765a7683
 > Jeromy/M
-4766a7684,7687
+4766a7685,7688
 > Jerrie/M
 > Jerrilee/M
 > Jerrilyn/M
 > Jerrine/M
-4768a7690
+4768a7691
 > Jerrome/M
-4769a7692
+4769a7693
 > Jerrylee/M
-4771a7695
+4771a7696
 > Jervis/M
-4772a7697,7701
+4772a7698,7702
 > Jessa/M
 > Jessalin/M
 > Jessalyn/M
 > Jessamine/M
 > Jessamyn/M
-4773a7703,7706
+4773a7704,7707
 > Jessee/M
 > Jesselyn/M
 > Jessey/M
 > Jessi/M
-4775a7709,7710
+4775a7710,7711
 > Jessika/M
 > Jessy/M
-4777a7713,7714
+4777a7714,7715
 > Jeth/M
 > Jethro
-4781a7719
+4781a7720
 > Jewelle/M
-4788a7727,7732
+4788a7728,7733
 > Jillana/M
 > Jillane/M
 > Jillayne/M
 > Jilleen/M
 > Jillene/M
 > Jilli/M
-4789a7734,7735
+4789a7735,7736
 > Jillie/M
 > Jilly/M
-4798c7744,7745
+4798c7745,7746
 < Jo/M
 ---
 > Jo/MY
 > Joachim
-4799a7747,7749
+4799a7748,7750
 > Joana/M
 > Joane/M
 > Joanie/M
-4802c7752
+4802c7753
 < Joanne/M
 ---
 > Joanne/SM
-4804a7755,7761
+4804a7756,7762
 > Jobey/M
 > Jobi/M
 > Jobie/M
 > Jobina/M
 > Joby/M
 > Jobye/M
 > Jobyna/M
-4805a7763,7764
+4805a7764,7765
 > Jocelin/M
 > Joceline/M
-4806a7766
+4806a7767
 > Jocelyne/M
-4808a7769,7770
+4808a7770,7771
 > Jocko/M
 > Jodee/M
-4813c7775,7785
+4813c7776,7786
 < Joel
 ---
 > Joeann/M
 > Joel/Y
 > Joela/M
 > Joelie/M
 > Joell/MN
 > Joella/M
 > Joelle/M
 > Joellen/M
 > Joelly/M
 > Joellyn/M
 > Joelynn/M
-4814a7787
+4814a7788
 > Joete/M
-4816a7790
+4816a7791
 > Johan/M
-4818a7793
+4818a7794
 > Johannah/M
-4821a7797,7798
+4821a7798,7799
 > Johna/MH
 > Johnath/M
-4823a7801
+4823a7802
 > Johnette/M
-4824a7803
+4824a7804
 > Johnna/M
-4828a7808,7812
+4828a7809,7813
 > Johny/M
 > Joice/M
 > Jojo/M
 > Jolee/M
 > Joleen/M
-4829a7814,7816
+4829a7815,7817
 > Joletta/M
 > Joli/M
 > Jolie/M
-4830a7818
+4830a7819
 > Joline/M
-4831a7820,7822
+4831a7821,7823
 > Joly/M
 > Jolyn/M
 > Jolynn/M
-4838,4839c7829,7832
+4838,4839c7830,7833
 < Jones
 < Joni/M
 ---
 > Jone/SM
 > Jonell/M
 > Joni/SM
 > Jonie/M
-4841a7835
+4841a7836
 > Jordain/M
-4842a7837
+4842a7838
 > Jordana/M
-4843a7839,7842
+4843a7840,7843
 > Jordanna/M
 > Jordon/M
 > Jorey/M
 > Jorgan/M
-4844a7844,7849
+4844a7845,7850
 > Jori/M
 > Jorie/M
 > Jorrie/M
 > Jorry/M
 > Jory/M
 > Joscelin/M
-4845a7851
+4845a7852
 > Josee/M
-4848a7855
+4848a7856
 > Joseito/M
-4849a7857,7858
+4849a7858,7859
 > Josepha/M
 > Josephina/M
-4853a7863
+4853a7864
 > Josey/M
-4854a7865
+4854a7866
 > Joshia/M
-4855a7867,7868
+4855a7868,7869
 > Joshuah/M
 > Josi/M
-4856a7870
+4856a7871
 > Josias/M
-4857a7872
+4857a7873
 > Josselyn/M
-4858a7874
+4858a7875
 > Josy/M
-4859a7876,7877
+4859a7877,7878
 > Jourdain/M
 > Jourdan/M
-4862a7881,7883
+4862a7882,7884
 > Joya/M
 > Joyan/M
 > Joyann/M
-4864a7886,7887
+4864a7887,7888
 > Joycelin/M
 > Joye/M
-4865a7889
+4865a7890
 > Jozef/M
-4867a7892
+4867a7893
 > Jsandye/M
-4872a7898
+4872a7899
 > Jud
-4881a7908,7909
+4881a7909,7910
 > Judi/MH
 > Judie/M
-4882a7911,7912
+4882a7912,7913
 > Juditha/M
 > Judon/M
-4884a7915
+4884a7916
 > Judye/M
-4885a7917
+4885a7918
 > Juieta/M
-4887c7919,7921
+4887c7920,7922
 < Jules
 ---
 > Jule/SM
 > Julee/M
 > Juli/M
-4890a7925,7927
+4890a7926,7928
 > Juliane/M
 > Juliann/M
 > Julianna/M
-4892a7930
+4892a7931
 > Julienne/M
-4893a7932,7933
+4893a7933,7934
 > Julieta/M
 > Julietta/M
-4894a7935,7936
+4894a7936,7937
 > Julina/M
 > Juline/M
-4895a7938,7939
+4895a7939,7940
 > Julissa/M
 > Julita/M
-4901a7946
+4901a7947
 > Junette/M
-4904a7950,7952
+4904a7951,7953
 > Junia/M
 > Junie/M
 > Junina/M
-4910a7959
+4910a7960
 > Justen/M
-4912a7962
+4912a7963
 > Justina/M
-4914a7965,7968
+4914a7966,7969
 > Justinn/M
 > Justino/M
 > Justis/M
 > Justus/M
-4917c7971,7972
+4917c7972,7973
 < K/SMNGJ
 ---
 > Jyoti/M
 > K/SMNRGJ
-4929a7985,7988
+4929a7986,7989
 > Kacey/M
 > Kacie/M
 > Kacy/M
 > Kaela/M
-4932a7992,7993
+4932a7993,7994
 > Kahaleel/M
 > Kahlil/M
-4933a7995,7996
+4933a7996,7997
 > Kai/M
 > Kaia/M
-4934a7998,8002
+4934a7999,8003
 > Kaila/M
 > Kaile/M
 > Kailey/M
 > Kain/M
 > Kaine/M
-4936a8005,8009
+4936a8006,8010
 > Kaitlyn/M
 > Kaitlynn/M
 > Kaja/M
 > Kakalina/M
 > Kala/M
-4940a8014,8016
+4940a8015,8017
 > Kale/M
 > Kaleb/M
 > Kaleena/M
-4943a8020,8028
+4943a8021,8029
 > Kalie/M
 > Kalil/M
 > Kalila/M
 > Kalina/M
 > Kalinda/M
 > Kalindi/M
 > Kalle/M
 > Kalli/M
 > Kally/M
-4944a8030
+4944a8031
 > Kalvin/M
-4947a8034,8037
+4947a8035,8038
 > Kameko/M
 > Kamila/M
 > Kamilah/M
 > Kamillah/M
-4951a8042
+4951a8043
 > Kandace/M
-4953a8045
+4953a8046
 > Kandy
-4954a8047
+4954a8048
 > Kania/M
-4961a8055
+4961a8056
 > Kanya/M
-4967a8062,8063
+4967a8063,8064
 > Karalee/M
 > Karalynn/M
-4968a8065,8066
+4968a8066,8067
 > Kare/M
 > Karee/M
-4969a8068
+4969a8069
 > Karel/M
-4970a8070
+4970a8071
 > Karena/M
-4972a8073,8077
+4972a8074,8078
 > Karia/M
 > Karie/M
 > Karil/M
 > Karilynn/M
 > Karim/M
-4975c8080,8085
+4975c8081,8086
 < Karl/M
 ---
 > Karine/M
 > Kariotta/M
 > Karisa/M
 > Karissa/M
 > Karita/M
 > Karl/MNX
-4976a8087,8094
+4976a8088,8095
 > Karlan/M
 > Karlee/M
 > Karleen/M
 > Karlen/M
 > Karlene/M
 > Karlie/M
 > Karlik/M
 > Karlis
-4978c8096,8103
+4978c8097,8104
 < Karo/M
 ---
 > Karlotta/M
 > Karlotte/M
 > Karly/M
 > Karlyn/M
 > Karmen/M
 > Karna/M
 > Karney/M
 > Karo/MY
-4979a8105,8112
+4979a8106,8113
 > Karola/M
 > Karole/M
 > Karolina/M
 > Karoline/M
 > Karoly/M
 > Karon/M
 > Karrah/M
 > Karrie/M
-4980a8114,8117
+4980a8115,8118
 > Karry/M
 > Kary/M
 > Karyl/M
 > Karylin/M
-4984a8122
+4984a8123
 > Kaspar/M
-4985a8124,8133
+4985a8125,8134
 > Kasper/M
 > Kass
 > Kassandra/M
 > Kassey/M
 > Kassi/M
 > Kassia/M
 > Kassie/M
 > Kat/M
 > Kata/M
 > Katalin/M
-4986a8135
+4986a8136
 > Katee/M
-4987a8137,8142
+4987a8138,8143
 > Katerina/M
 > Katerine/M
 > Katey/M
 > Kath/M
 > Katha/M
 > Katharina/M
-4988a8144,8146
+4988a8145,8147
 > Katharyn/M
 > Kathe/M
 > Katherina/M
-4990a8149
+4990a8150
 > Kathi/M
-4993a8153
+4993a8154
 > Kathlin/M
-4996a8157
+4996a8158
 > Kathryne/M
-4997a8159,8160
+4997a8160,8161
 > Kathye/M
 > Kati/M
-4999a8163,8166
+4999a8164,8167
 > Katine/M
 > Katinka/M
 > Katleen/M
 > Katlin/M
-5003a8171,8176
+5003a8172,8177
 > Katrine
 > Katrinka/M
 > Katti/M
 > Kattie/M
 > Katuscha/M
 > Katusha/M
-5004a8178
+5004a8179
 > Katya/M
-5011a8186
+5011a8187
 > Kaycee/M
-5013a8189,8194
+5013a8190,8195
 > Kayle/M
 > Kaylee/M
 > Kayley/M
 > Kaylil/M
 > Kaylyn/M
 > Kayne/M
-5019a8201,8204
+5019a8202,8205
 > Kean
 > Keane/M
 > Kearney/M
 > Keary/M
-5022a8208,8214
+5022a8209,8215
 > Keefe/RM
 > Keefer/M
 > Keelby/M
 > Keeley/M
 > Keelia/M
 > Keely/M
 > Keen/M
-5023a8216
+5023a8217
 > Keene/M
-5025a8219
+5025a8220
 > Keir/M
-5027a8222,8231
+5027a8223,8232
 > Kelbee/M
 > Kelby/M
 > Kelcey/M
 > Kelci/M
 > Kelcie/M
 > Kelcy/M
 > Kele/M
 > Kelila/M
 > Kellby/M
 > Kellen/M
-5030a8235
+5030a8236
 > Kellia/M
-5031a8237
+5031a8238
 > Kellina/M
-5032a8239
+5032a8240
 > Kellsie/M
-5033a8241
+5033a8242
 > Kellyann/M
-5034a8243,8244
+5034a8244,8245
 > Kelsi/M
 > Kelsy/M
-5036a8247
+5036a8248
 > Kelwin/M
-5039a8251
+5039a8252
 > Kendal/M
-5040a8253
+5040a8254
 > Kendell/M
-5042c8255,8256
+5042c8256,8257
 < Kendrick/M
 ---
 > Kendre/M
 > Kendrick/MS
-5043a8258,8259
+5043a8259,8260
 > Kenn/M
 > Kenna/M
-5046a8263,8264
+5046a8264,8265
 > Kennett/M
 > Kennie/M
-5048a8267
+5048a8268
 > Kenon/M
-5059a8279,8280
+5059a8280,8281
 > Ker/M
 > Kerby/M
-5061a8283,8286
+5061a8284,8287
 > Keriann/M
 > Kerianne/M
 > Kerk/M
 > Kermie/M
-5062a8288
+5062a8289
 > Kermy/M
-5066a8293,8295
+5066a8294,8296
 > Kerrie/M
 > Kerrill/M
 > Kerrin/M
-5067a8297,8303
+5067a8298,8304
 > Kerstin/M
 > Kerwin/M
 > Kerwinn/M
 > Kesley/M
 > Keslie/M
 > Kessia/M
 > Kessiah/M
-5068a8305,8309
+5068a8306,8310
 > Ketti/M
 > Kettie/M
 > Ketty/M
 > Kev/MN
 > Kevan/M
-5070a8312
+5070a8313
 > Kevina/M
-5071a8314
+5071a8315
 > Kevon/M
-5072a8316
+5072a8317
 > Kevyn/M
-5079a8324
+5079a8325
 > Khalil/M
-5094a8340,8342
+5094a8341,8343
 > Ki/M
 > Kiah/M
 > Kial/M
-5097a8346,8347
+5097a8347,8348
 > Kiele/M
 > Kienan/M
-5098a8349
+5098a8350
 > Kiersten/M
-5101a8353
+5101a8354
 > Kikelia/M
-5103a8356,8358
+5103a8357,8359
 > Kile/M
 > Kiley/M
 > Kilian/M
-5104a8360,8362
+5104a8361,8363
 > Killian/M
 > Killie/M
 > Killy/M
-5106a8365,8367
+5106a8366,8368
 > Kimball/M
 > Kimbell/M
 > Kimberlee/M
-5107a8369
+5107a8370
 > Kimberli/M
-5108a8371,8378
+5108a8372,8379
 > Kimberlyn/M
 > Kimble/M
 > Kimbra/M
 > Kimmi/M
 > Kimmie/M
 > Kimmy/M
 > Kin/M
 > Kincaid/M
-5109a8380,8381
+5109a8381,8382
 > Kingsley
 > Kingsly/M
-5112a8385
+5112a8386
 > Kinna/M
-5113a8387,8388
+5113a8388,8389
 > Kinnie/M
 > Kinny/M
-5115a8391
+5115a8392
 > Kinsley/M
-5118a8395,8402
+5118a8396,8403
 > Kipp/MR
 > Kippar/M
 > Kipper/M
 > Kippie/M
 > Kippy/M
 > Kira/M
 > Kirbee/M
 > Kirbie/M
-5124a8409
+5124a8410
 > Kiri/M
-5131a8417,8420
+5131a8418,8421
 > Kirsteni/M
 > Kirsti/M
 > Kirstin/M
 > Kirstyn/M
-5134a8424,8426
+5134a8425,8427
 > Kissee/M
 > Kissiah/M
 > Kissie/M
-5138a8431,8432
+5138a8432,8433
 > Kitti/M
 > Kittie/M
-5140a8435,8436
+5140a8436,8437
 > Kizzee/M
 > Kizzie/M
-5142a8439,8441
+5142a8440,8442
 > Klara/M
 > Klarika/M
 > Klarrisa/M
-5146a8446,8449
+5146a8447,8450
 > Klemens/M
 > Klement/M
 > Kleon/M
 > Kliment/M
-5172a8476
+5172a8477
 > Koenraad/M
-5183a8488,8492
+5183a8489,8493
 > Konstance/M
 > Konstantin/M
 > Konstantine/M
 > Konstanze/M
 > Koo/M
-5185a8495,8497
+5185a8496,8498
 > Kora/M
 > Koral/M
 > Koralle/M
-5187a8500,8501
+5187a8501,8502
 > Kordula/M
 > Kore/M
-5189a8504,8509
+5189a8505,8510
 > Korella/M
 > Koren/M
 > Koressa/M
 > Korey/M
 > Kori/M
 > Korie/M
-5190a8511,8514
+5190a8512,8515
 > Korney/M
 > Korrie/M
 > Korry/M
 > Kort/M
-5212a8537
+5212a8538
 > Krisha/M
-5213a8539
+5213a8540
 > Krishnah/M
-5214a8541,8543
+5214a8542,8544
 > Krispin/M
 > Krissie/M
 > Krissy/M
-5215a8545,8548
+5215a8546,8549
 > Kristal/M
 > Kristan/M
 > Kriste/M
 > Kristel/M
-5217c8550,8551
+5217c8551,8552
 < Kristi/M
 ---
 > Kristi/MN
 > Kristian/M
-5218a8553
+5218a8554
 > Kristien/M
-5221a8557,8561
+5221a8558,8562
 > Kristo/SM
 > Kristofer/M
 > Kristoffer/M
 > Kristofor/M
 > Kristoforo/M
-5223a8564
+5223a8565
 > Kristyn/M
-5230a8572
+5230a8573
 > Krysta/M
-5231a8574,8576
+5231a8575,8577
 > Krystalle/M
 > Krystle/M
 > Krystyna/M
-5259a8605
+5259a8606
 > Kyla/M
-5260a8607,8612
+5260a8608,8613
 > Kylen/M
 > Kylie/M
 > Kylila/M
 > Kylynn/M
 > Kym/M
 > Kynthia/M
-5262a8615
+5262a8616
 > Kyrstin/M
-5277c8630
+5277c8631
 < LG
 ---
 > LG/M
-5295a8649
+5295a8650
 > Lacee/M
-5297a8652
+5297a8653
 > Lacie/M
-5302a8658
+5302a8659
 > Laetitia/M
-5308a8665,8667
+5308a8666,8668
 > Laina/M
 > Lainey/M
 > Laird/M
-5315a8675
+5315a8676
 > Lalo/M
-5324a8685,8686
+5324a8686,8687
 > Lammond/M
 > Lamond/M
-5326a8689
+5326a8690
 > Lanae/M
-5337a8701,8702
+5337a8702,8703
 > Lanette/M
 > Laney/M
-5342a8708,8715
+5342a8709,8716
 > Langsdon/M
 > Langston/M
 > Lani/M
 > Lanie/M
 > Lanita/M
 > Lanna/M
 > Lanni/M
 > Lannie/M
-5352a8726
+5352a8727
 > Laraine/M
-5355a8730,8735
+5355a8731,8736
 > Lari/M
 > Larina/M
 > Larine/M
 > Larisa/M
 > Larissa/M
 > Lark/M
-5360a8741
+5360a8742
 > Laryssa/M
-5366a8748
+5366a8749
 > Latashia/M
-5367a8750
+5367a8751
 > Latia/M
-5373a8757,8758
+5373a8758,8759
 > Latrena/M
 > Latrina/M
-5378a8764,8765
+5378a8765,8766
 > Laughton
 > Launce/M
-5380a8768,8770
+5380a8769,8771
 > Lauraine/M
 > Laural/M
 > Lauralee/M
-5381a8772,8774
+5381a8773,8775
 > Laure/M
 > Lauree/M
 > Laureen/M
-5383c8776,8778
+5383c8777,8779
 < Lauren/M
 ---
 > Laurella/M
 > Lauren/SM
 > Laurena/M
-5384a8780
+5384a8781
 > Laurene/M
-5385a8782,8783
+5385a8783,8784
 > Lauretta/M
 > Laurette/M
-5386a8785,8786
+5386a8786,8787
 > Laurianne/M
 > Laurice/M
-5387a8788,8789
+5387a8789,8790
 > Lauritz/M
 > Lauryn/M
-5388a8791
+5388a8792
 > Lavena/M
-5389a8793
+5389a8794
 > Laverna/M
-5390a8795,8797
+5390a8796,8798
 > Lavina/M
 > Lavinia/M
 > Lavinie/M
-5392a8800
+5392a8801
 > Law
-5394a8803
+5394a8804
 > Lawry/M
-5395a8805,8806
+5395a8806,8807
 > Lawton/M
 > Lay/M
-5397a8809,8813
+5397a8810,8814
 > Layne/M
 > Layney/M
 > Layton/M
 > Lazar/M
 > Lazare/M
-5400c8816
+5400c8817
 < Le/SM
 ---
 > Le/SMN
-5407a8824
+5407a8825
 > Leandra/M
-5410a8828,8829
+5410a8829,8830
 > Leanor/M
 > Leanora/M
-5416a8836
+5416a8837
 > Lebbie/M
-5421a8842,8843
+5421a8843,8844
 > Leeann/M
 > Leeanne/M
-5422a8845,8850
+5422a8846,8851
 > Leela/M
 > Leelah/M
 > Leeland/M
 > Leena/M
 > Leesa/M
 > Leese/M
-5425a8854
+5425a8855
 > Lefty/M
-5429a8859
+5429a8860
 > Legra/M
-5431a8862
+5431a8863
 > Leia/M
-5436a8868,8869
+5436a8869,8870
 > Leigha/M
 > Leighton/M
-5437a8871
+5437a8872
 > Leilah/M
-5438a8873,8874
+5438a8874,8875
 > Leisha/M
 > Lek/M
-5439a8876
+5439a8877
 > Lelah/M
-5441a8879
+5441a8880
 > Lem/M
-5442a8881,8883
+5442a8882,8884
 > Lemar/M
 > Lemmie/M
 > Lemmy/M
-5447a8889,8891
+5447a8890,8892
 > Lenci/M
 > Lenee/M
 > Lenette/M
-5451a8896,8899
+5451a8897,8900
 > Lenka/M
 > Lenna/M
 > Lennard/M
 > Lennie/M
-5459a8908,8909
+5459a8909,8910
 > Leodora/M
 > Leoine/M
-5460a8911
+5460a8912
 > Leoline/M
-5462a8914
+5462a8915
 > Leonanie/M
-5465a8918
+5465a8919
 > Leone/M
-5466a8920,8922
+5466a8921,8923
 > Leonelle/M
 > Leonerd/M
 > Leonhard/M
-5468a8925
+5468a8926
 > Leonie/M
-5469a8927,8930
+5469a8928,8931
 > Leonora/M
 > Leonore/M
 > Leontine/M
 > Leontyne/M
-5471a8933
+5471a8934
 > Leora/M
-5475a8938
+5475a8939
 > Leroi/M
-5477a8941
+5477a8942
 > Leshia/M
-5478a8943
+5478a8944
 > Lesli/M
-5479a8945
+5479a8946
 > Lesly/M
-5484a8951
+5484a8952
 > Lesya/M
-5487a8955
+5487a8956
 > Lethia/M
-5488a8957
+5488a8958
 > Letisha/M
-5489a8959,8960
+5489a8960,8961
 > Letizia/M
 > Letta/M
-5490a8962,8966
+5490a8963,8967
 > Letti/M
 > Lettie/M
 > Letty/M
 > Leupold/M
 > Lev
-5492a8969
+5492a8970
 > Levey/M
-5494a8972
+5494a8973
 > Levin/M
-5497a8976
+5497a8977
 > Levon/M
-5499a8979,8980
+5499a8980,8981
 > Lewes
 > Lewie/M
-5501a8983,8986
+5501a8984,8987
 > Lewiss
 > Lexi/SM
 > Lexie/M
 > Lexine/M
-5503a8989,8992
+5503a8990,8993
 > Lexy/M
 > Leyla/M
 > Lezley/M
 > Lezlie/M
-5506a8996,9006
+5506a8997,9007
 > Lia/M
 > Liam/M
 > Lian/M
 > Liana/M
 > Liane/M
 > Lianna/M
 > Lianne/M
 > Lib
 > Libbey/M
 > Libbi/M
 > Libbie/M
-5515a9016
+5515a9017
 > Licha/M
-5516a9018
+5516a9019
 > Lida/M
-5522a9025
+5522a9026
 > Lief/M
-5523a9027
+5523a9028
 > Liesa/M
-5525,5526c9029,9032
+5525,5526c9030,9033
 < Lila/M
 < Lilia/M
 ---
 > Lil/MY
 > Lila/SM
 > Lilah/M
 > Lilia/MS
-5528a9035
+5528a9036
 > Liliane/M
-5530a9038
+5530a9039
 > Lilla/M
-5531a9040
+5531a9041
 > Lilli/MS
-5535a9045
+5535a9046
 > Lilllie/M
-5538a9049
+5538a9050
 > Lilyan/M
-5547a9059
+5547a9060
 > Linc/M
-5551a9064,9066
+5551a9065,9067
 > Lindi/M
 > Lindie/M
 > Lindon/M
-5553a9069
+5553a9070
 > Lindsy/M
-5554a9071,9076
+5554a9072,9077
 > Linea/M
 > Linell/M
 > Linet/M
 > Linette/M
 > Link/M
 > Linn/M
-5555a9078,9082
+5555a9079,9083
 > Linnea/M
 > Linnell/M
 > Linnet/M
 > Linnie/M
 > Linoel/M
-5560a9088
+5560a9089
 > Linzy/M
-5561a9090
+5561a9091
 > Lionello/M
-5566a9096
+5566a9097
 > Lira/M
-5567a9098,9099
+5567a9099,9100
 > Lisabeth/M
 > Lisbeth/M
-5568a9101,9107
+5568a9102,9108
 > Lise/M
 > Lisetta/M
 > Lisette/M
 > Lisha/M
 > Lishe/M
 > Lisle/M
 > Lissa/M
-5569a9109,9111
+5569a9110,9112
 > Lissi/M
 > Lissie/M
 > Lissy/M
-5573a9116
+5573a9117
 > Lita/M
-5577a9121,9123
+5577a9122,9124
 > Liuka/M
 > Liv/M
 > Liva/M
-5583a9130,9132
+5583a9131,9133
 > Livvie/M
 > Livvy/M
 > Livvyy/M
-5586a9136,9138
+5586a9137,9139
 > Lizabeth/M
 > Lizbeth/M
 > Lizette/M
-5591a9144
+5591a9145
 > Llywellyn/M
-5595a9149
+5595a9150
 > Lock/M
-5600a9155
+5600a9156
 > Lodovico/M
-5601a9157
+5601a9158
 > Loella/M
-5608a9165
+5608a9166
 > Loise/M
-5610a9168
+5610a9169
 > Loleta/M
-5613a9172
+5613a9173
 > Lolly/M
-5618a9178
+5618a9179
 > Lona/M
-5620a9181
+5620a9182
 > Lonee/M
-5624a9186,9189
+5624a9187,9190
 > Loni/M
 > Lonna/M
 > Lonnard/M
 > Lonni/M
-5625a9191
+5625a9192
 > Lonny/M
-5627a9194
+5627a9195
 > Lorain/M
-5628a9196,9199
+5628a9197,9200
 > Loralee/M
 > Loralie/M
 > Loralyn/M
 > Lorant/M
-5630a9202,9203
+5630a9203,9204
 > Loree/M
 > Loreen/M
-5632c9205,9206
+5632c9206,9207
 < Loren
 ---
 > Lorelle/M
 > Loren/S
-5636a9211
+5636a9212
 > Lorenza/M
-5638a9214
+5638a9215
 > Lorette/M
-5639a9216,9218
+5639a9217,9219
 > Loria/M
 > Lorianna/M
 > Lorianne/M
-5640a9220,9225
+5640a9221,9226
 > Lorilee/M
 > Lorilyn/M
 > Lorin/M
 > Lorinda/M
 > Lorine/M
 > Lorita/M
-5641a9227
+5641a9228
 > Lorne/M
-5642a9229
+5642a9230
 > Lorrayne/M
-5643a9231
+5643a9232
 > Lorri/M
-5644a9233,9235
+5644a9234,9236
 > Lorrin/M
 > Lorry/M
 > Lory/M
-5645a9237
+5645a9238
 > Lothaire/M
-5647a9240,9242
+5647a9241,9243
 > Lotta/M
 > Lotte/M
 > Lotti/M
-5648a9244
+5648a9245
 > Lotty/M
-5654a9251
+5654a9252
 > Louisette/M
-5659a9257
+5659a9258
 > Loutitia/M
-5663a9262
+5663a9263
 > Lovell
-5668a9268,9269
+5668a9269,9270
 > Lowrance/M
 > Loy/M
-5670a9272
+5670a9273
 > Loydie/M
-5681c9283,9284
+5681c9284,9285
 < Lucas
 ---
 > Luca/SM
 > Lucais/M
-5683c9286,9288
+5683c9287,9289
 < Lucia/M
 ---
 > Lucho/M
 > Luci/MN
 > Lucia/MS
-5684a9290
+5684a9291
 > Luciana/M
-5685a9292
+5685a9293
 > Lucie/M
-5686a9294
+5686a9295
 > Lucienne/M
-5687a9296
+5687a9297
 > Lucila/M
-5688a9298
+5688a9299
 > Lucilia/M
-5689a9300
+5689a9301
 > Lucina
-5690a9302
+5690a9303
 > Lucine/M
-5691a9304
+5691a9305
 > Lucita/M
-5694a9308
+5694a9309
 > Lucky/M
-5699a9314,9316
+5699a9315,9317
 > Ludovico/M
 > Ludovika/M
 > Ludvig/M
-5701a9319
+5701a9320
 > Luelle/M
-5708a9327,9328
+5708a9328,9329
 > Luise/M
 > Lukas/M
-5710a9331
+5710a9332
 > Lulita/M
-5717a9339,9340
+5717a9340,9341
 > Lura/M
 > Lurette/M
-5718a9342,9345
+5718a9343,9346
 > Lurleen/M
 > Lurlene/M
 > Lurline/M
 > Lusa/M
-5720a9348
+5720a9349
 > Lutero/M
-5731a9360
+5731a9361
 > Ly/MY
-5735a9365
+5735a9366
 > Lyda/M
-5737a9368,9369
+5737a9369,9370
 > Lydie/M
 > Lydon/M
-5742a9375
+5742a9376
 > Lyn/M
-5744a9378,9380
+5744a9379,9381
 > Lynde/M
 > Lyndel/M
 > Lyndell/M
-5745a9382,9388
+5745a9383,9389
 > Lyndsay/M
 > Lyndsey/M
 > Lyndsie/M
 > Lyndy/M
 > Lynea/M
 > Lynelle/M
 > Lynett/M
-5747a9391
+5747a9392
 > Lynna/M
-5748a9393,9397
+5748a9394,9398
 > Lynnea/M
 > Lynnell/M
 > Lynnelle/M
 > Lynnet/M
 > Lynnett/M
-5749a9399
+5749a9400
 > Lynsey/M
-5754a9405
+5754a9406
 > Lyssa/M
-5768c9419
+5768c9420
 < MHz
 ---
 > MHz/M
-5789a9441
+5789a9442
 > Mab
-5790a9443
+5790a9444
 > Mabelle/M
-5818a9472
+5818a9473
 > Mada/M
-5820a9475,9477
+5820a9476,9478
 > Madalena/M
 > Madalyn/M
 > Maddalena/M
-5821a9479,9480
+5821a9480,9481
 > Maddi/M
 > Maddie/M
-5822a9482
+5822a9483
 > Maddy/M
-5823a9484,9485
+5823a9485,9486
 > Madel/M
 > Madelaine/M
-5824a9487,9490
+5824a9488,9491
 > Madelena/M
 > Madelene/M
 > Madelin/M
 > Madelina/M
-5825a9492,9494
+5825a9493,9495
 > Madella/M
 > Madelle/M
 > Madelon/M
-5828a9498,9499
+5828a9499,9500
 > Madlen/M
 > Madlin/M
-5832a9504
+5832a9505
 > Mady/M
-5833a9506
+5833a9507
 > Maegan/M
-5836a9510,9513
+5836a9511,9514
 > Mag/M
 > Magda/M
 > Magdaia/M
 > Magdalen
-5840a9518,9519
+5840a9519,9520
 > Maggee/M
 > Maggi/M
-5841a9521
+5841a9522
 > Maggy/M
-5845a9526
+5845a9527
 > Magnum
-5852a9534,9535
+5852a9535,9536
 > Mahala/M
 > Mahalia/M
-5860a9544,9545
+5860a9545,9546
 > Mahmoud/M
 > Mahmud/M
-5862a9548,9549
+5862a9549,9550
 > Maia/M
 > Maible/M
-5863a9551,9552
+5863a9552,9553
 > Maiga/M
 > Maighdiln/M
-5870a9560,9562
+5870a9561,9563
 > Mair/M
 > Maire/M
 > Maisey/M
-5871a9564,9565
+5871a9565,9566
 > Maison/M
 > Maitilde/M
-5873a9568
+5873a9569
 > Maje/M
-5879a9575,9576
+5879a9576,9577
 > Mal
 > Mala/M
-5885a9583
+5885a9584
 > Malanie/M
-5894a9593
+5894a9594
 > Malchy/M
-5900a9600
+5900a9601
 > Malena/M
-5901a9602
+5901a9603
 > Malia/M
-5903a9605
+5903a9606
 > Malina/M
-5904a9607
+5904a9608
 > Malinde/M
-5905a9609,9610
+5905a9610,9611
 > Malissa/M
 > Malissia/M
-5906a9612
+5906a9613
 > Mallissa/M
-5907a9614
+5907a9615
 > Mallorie/M
-5909a9617
+5909a9618
 > Malorie/M
-5916a9625,9629
+5916a9626,9630
 > Malva/M
 > Malvin/M
 > Malvina/M
 > Malynda/M
 > Mame/M
-5932a9646
+5932a9647
 > Manda/M
-5934a9649
+5934a9650
 > Mandel/M
-5936a9652,9653
+5936a9653,9654
 > Mandi/M
 > Mandie/M
-5938a9656
+5938a9657
 > Mandriva/M
-5950a9669,9673
+5950a9670,9674
 > Mannie/M
 > Manny/M
 > Mano/M
 > Manolo/M
 > Manon/M
-5957a9681
+5957a9682
 > Manya/M
-5964c9688
+5964c9689
 < Mar/SM
 ---
 > Mar/SMN
-5965a9690
+5965a9691
 > Marabel/M
-5973a9699,9700
+5973a9700,9701
 > Marcela/M
 > Marcelia/M
-5975a9703,9707
+5975a9704,9708
 > Marcelle/M
 > Marcellina/M
 > Marcelline/M
 > Marcello/M
 > Marcellus
-5977a9710,9711
+5977a9711,9712
 > Marchall/M
 > Marchelle/M
-5981a9716,9717
+5981a9717,9718
 > Marcile/M
 > Marcille/M
-5987a9724,9730
+5987a9725,9731
 > Mareah/M
 > Maren/M
 > Marena/M
 > Maressa/M
 > Marga/M
 > Margalit/M
 > Margalo/M
-5988a9732,9737
+5988a9733,9738
 > Margareta/M
 > Margarete/M
 > Margaretha/M
 > Margarethe/M
 > Margaretta/M
 > Margarette/M
-5990a9740
+5990a9741
 > Margaux
-5991a9742
+5991a9743
 > Margeaux/M
-5992a9744,9746
+5992a9745,9747
 > Marget/M
 > Margette/M
 > Margi/M
-5993a9748
+5993a9749
 > Margit/M
-5994a9750
+5994a9751
 > Margot/M
-5997a9754
+5997a9755
 > Margy/M
-5999a9757
+5999a9758
 > Mariam/M
-6001a9760,9761
+6001a9761,9762
 > Mariann/M
 > Marianna/M
-6004a9765,9767
+6004a9766,9768
 > Maribelle/M
 > Maribeth/M
 > Marice/M
-6005a9769
+6005a9770
 > Maridel/M
-6006a9771,9776
+6006a9772,9777
 > Marieann/M
 > Mariejeanne/M
 > Mariel/M
 > Mariele/M
 > Marielle/M
 > Mariellen/M
-6007a9778,9785
+6007a9779,9786
 > Mariette/M
 > Marigold/M
 > Marijn/M
 > Marijo/M
 > Marika/M
 > Marilee/M
 > Marilin/M
 > Marillin/M
-6011a9790
+6011a9791
 > Marinna/M
-6013a9793
+6013a9794
 > Mariquilla/M
-6014a9795
+6014a9796
 > Mariska/M
-6016a9798
+6016a9799
 > Marita/M
-6017a9800
+6017a9801
 > Maritsa
-6020a9804,9809
+6020a9805,9810
 > Mariya/M
 > Marj/M
 > Marja/M
 > Marje/M
 > Marji/M
 > Marjie/M
-6022a9812
+6022a9813
 > Marjy/M
-6024a9815
+6024a9816
 > Marketa/M
-6025a9817
+6025a9818
 > Markos
-6026a9819
+6026a9820
 > Markus/M
-6027a9821
+6027a9822
 > Marlane/M
-6029a9824,9827
+6029a9825,9828
 > Marleah/M
 > Marlee/M
 > Marleen/M
 > Marlena/M
-6031a9830
+6031a9831
 > Marlie/M
-6032a9832,9833
+6032a9833,9834
 > Marline/M
 > Marlo/M
-6033a9835
+6033a9836
 > Marlow/M
-6034a9837,9838
+6034a9838,9839
 > Marlyn/M
 > Marmaduke/M
-6035a9840
+6035a9841
 > Marna/M
-6036a9842,9845
+6036a9843,9846
 > Marney/M
 > Marni/M
 > Marnia/M
 > Marnie/M
-6044a9854
+6044a9855
 > Marrilee/M
-6045a9856,9857
+6045a9857,9858
 > Marris/M
 > Marrissa/M
-6051a9864
+6051a9865
 > Marshal/M
-6052a9866,9867
+6052a9867,9868
 > Marsiella/M
 > Mart/MN
-6053a9869
+6053a9870
 > Martainn/M
-6054a9871,9873
+6054a9872,9874
 > Martelle/M
 > Marten/M
 > Martguerita/M
-6055a9875,9877
+6055a9876,9878
 > Marthe/M
 > Marthena/M
 > Marti/M
-6057a9880,9881
+6057a9881,9882
 > Martica/M
 > Martie/M
-6061a9886,9887
+6061a9887,9888
 > Martino/M
 > Martita/M
-6062a9889,9891
+6062a9890,9892
 > Martyn/M
 > Martynne/M
 > Marv/MN
-6063a9893
+6063a9894
 > Marve/M
-6064a9895
+6064a9896
 > Marven/M
-6065a9897
+6065a9898
 > Marwin/M
-6070a9903
+6070a9904
 > Marya/M
-6071a9905
+6071a9906
 > Maryanna/M
-6072a9907,9908
+6072a9908,9909
 > Marybelle/M
 > Marybeth/M
-6073a9910,9912
+6073a9911,9913
 > Maryjane/M
 > Maryjo/M
 > Maryl/M
-6074a9914,9916
+6074a9915,9917
 > Marylee/M
 > Marylin/M
 > Marylinda/M
-6075a9918,9921
+6075a9919,9922
 > Marylynne/M
 > Maryrose/M
 > Marys
 > Marysa/M
-6082a9929
+6082a9930
 > Masha/M
-6091a9939,9940
+6091a9940,9941
 > Massimiliano/M
 > Massimo/M
-6094c9943,9946
+6094c9944,9947
 < Mather
 ---
 > Mata/M
 > Matelda/M
 > Mateo/M
 > Mathe/MR
-6096a9949
+6096a9950
 > Mathian/M
-6097a9951,9952
+6097a9952,9953
 > Mathilda/M
 > Mathilde/M
-6098a9954
+6098a9955
 > Matias/M
-6099a9956
+6099a9957
 > Matilde/M
-6102a9960
+6102a9961
 > Matteo/M
-6103a9962,9963
+6103a9963,9964
 > Matthaeus/M
 > Mattheus/M
-6105a9966,9970
+6105a9967,9971
 > Matthieu/M
 > Matthiew/M
 > Matthus/M
 > Matti/M
 > Mattias/M
-6106a9972
+6106a9973
 > Matty/M
-6108a9975
+6108a9976
 > Maudie/M
-6112a9980
+6112a9981
 > Maure/M
-6113a9982,9983
+6113a9983,9984
 > Maureene/M
 > Maurene/M
-6116a9987
+6116a9988
 > Maurie/M
-6117a9989,9990
+6117a9990,9991
 > Maurise/M
 > Maurita/M
-6121a9995,9997
+6121a9996,9998
 > Maurits/M
 > Maurizia/M
 > Maurizio/M
-6123a10000
+6123a10001
 > Maury
-6126a10004
+6126a10005
 > Mavra/M
-6127a10006,10008
+6127a10007,10009
 > Maxi/M
 > Maxie/M
 > Maxim
-6128a10010,10012
+6128a10011,10013
 > Maximilianus/M
 > Maximilien/M
 > Maximo/M
-6130a10015
+6130a10016
 > Maxy/M
-6133a10019,10020
+6133a10020,10021
 > Maybelle/M
 > Maye/M
-6136a10024,10025
+6136a10025,10026
 > Mayne/M
 > Maynord/M
-6137a10027
+6137a10028
 > Mayor/M
-6203a10094
+6203a10095
 > Meaghan/M
-6204a10096
+6204a10097
 > Meara/M
-6205a10098
+6205a10099
 > Mechelle/M
-6216c10109
+6216c10110
 < Meg/M
 ---
 > Meg/MN
-6217a10111,10114
+6217a10112,10115
 > Megen/M
 > Meggi/M
 > Meggie/M
 > Meggy/M
-6218a10116,10118
+6218a10117,10119
 > Meghann/M
 > Mehetabel/M
 > Mei/MR
-6225c10125,10127
+6225c10126,10128
 < Mel/M
 ---
 > Mel/MY
 > Mela/M
 > Melamie/M
-6227a10130
+6227a10131
 > Melania/M
-6228a10132,10133
+6228a10133,10134
 > Melantha/M
 > Melany/M
-6233a10139,10142
+6233a10140,10143
 > Melesa/M
 > Melessa/M
 > Melicent/M
 > Melina/M
-6234a10144
+6234a10145
 > Melinde/M
-6236a10147,10149
+6236a10148,10150
 > Melisandra/M
 > Melisenda/M
 > Melisent/M
-6237a10151,10159
+6237a10152,10160
 > Melisse/M
 > Melita/M
 > Melitta/M
 > Mella/M
 > Melli/M
 > Mellicent/M
 > Mellie/M
 > Mellisa/M
 > Mellisent/M
-6238a10161,10164
+6238a10162,10165
 > Melloney/M
 > Melly/M
 > Melodee/M
 > Melodie/M
-6239a10166,10168
+6239a10167,10169
 > Melonie/M
 > Melony/M
 > Melosa/M
-6244a10174
+6244a10175
 > Melvyn/M
-6247a10178
+6247a10179
 > Menard/M
-6254a10186
+6254a10187
 > Mendie/M
-6256a10189
+6256a10190
 > Mendy/M
-6277a10211
+6277a10212
 > Merci/M
-6278a10213
+6278a10214
 > Mercie/M
-6281a10217,10218
+6281a10218,10219
 > Mercy/M
 > Meredeth/M
-6282a10220,10227
+6282a10221,10228
 > Meredithe/M
 > Merell/M
 > Meridel/M
 > Meridith/M
 > Meriel/M
 > Merilee/M
 > Merill/M
 > Merilyn/M
-6283a10229,10232
+6283a10230,10233
 > Meris
 > Merissa/M
 > Merl/M
 > Merla/M
-6285a10235,10236
+6285a10236,10237
 > Merlina/M
 > Merline/M
-6286a10238,10239
+6286a10239,10240
 > Merna/M
 > Merola/M
-6287a10241,10242
+6287a10242,10243
 > Merralee/M
 > Merrel/M
-6289a10245,10250
+6289a10246,10251
 > Merridie/M
 > Merrie/M
 > Merrielle/M
 > Merrile/M
 > Merrilee/M
 > Merrili/M
-6290a10252
+6290a10253
 > Merrily/M
-6292a10255,10256
+6292a10256,10257