Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 03 Jun 2014 14:56:10 +0200
changeset 205620 89c75d9c9007ab847de0964c031b961a3258a2c0
parent 205619 b60ea4e7a62098bd4fc853e8ae8cdb891ea31693 (current diff)
parent 205537 78245b8d422d70450671c33e98261319661cb372 (diff)
child 205621 f4b71c9f7e5d6f70e4f0100882f48d52f5ebf982
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.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 to b2g-inbound
browser/themes/shared/devtools/images/responsive-background.png
browser/themes/shared/devtools/images/responsive-horizontal-resizer.png
browser/themes/shared/devtools/images/responsive-se-resizer.png
browser/themes/shared/devtools/images/responsive-vertical-resizer.png
browser/themes/shared/devtools/responsiveui-rotate.png
browser/themes/shared/devtools/responsiveui-screenshot.png
browser/themes/shared/devtools/responsiveui-touch.png
js/src/jit-test/tests/basic/latin1-indexOf.js
js/src/jit-test/tests/basic/latin1-indexing.js
js/src/jit-test/tests/basic/latin1.js
js/src/jsworkers.cpp
js/src/jsworkers.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -971,16 +971,19 @@ pref("gfx.canvas.willReadFrequently.enab
 
 // Disable autofocus until we can have it not bring up the keyboard.
 // https://bugzilla.mozilla.org/show_bug.cgi?id=965763
 pref("browser.autofocus", false);
 
 // Enable wakelock
 pref("dom.wakelock.enabled", true);
 
+// Disable touch caret by default
+pref("touchcaret.enabled", false);
+
 // Enable sync and mozId with Firefox Accounts.
 #ifdef MOZ_SERVICES_FXACCOUNTS
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 #endif
 
 // Enable mapped array buffer
 pref("dom.mapped_arraybuffer.enabled", true);
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -666,16 +666,19 @@
 @BINPATH@/res/table-add-row-before-hover.gif
 @BINPATH@/res/table-add-row-before.gif
 @BINPATH@/res/table-remove-column-active.gif
 @BINPATH@/res/table-remove-column-hover.gif
 @BINPATH@/res/table-remove-column.gif
 @BINPATH@/res/table-remove-row-active.gif
 @BINPATH@/res/table-remove-row-hover.gif
 @BINPATH@/res/table-remove-row.gif
+@BINPATH@/res/text_selection_handle.png
+@BINPATH@/res/text_selection_handle@1.5.png
+@BINPATH@/res/text_selection_handle@2.png
 @BINPATH@/res/grabber.gif
 #ifdef XP_MACOSX
 @BINPATH@/res/cursors/*
 #endif
 @BINPATH@/res/fonts/*
 @BINPATH@/res/dtd/*
 @BINPATH@/res/html/*
 @BINPATH@/res/langGroups.properties
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1512,16 +1512,19 @@ pref("social.sidebar.unload_timeout_ms",
 pref("dom.identity.enabled", false);
 
 // Turn on the CSP 1.0 parser for Content Security Policy headers
 pref("security.csp.speccompliant", true);
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
 // If this turns true, Moz*Gesture events are not called stopPropagation()
 // before content.
 pref("dom.debug.propagate_gesture_events_through_content", false);
 
 // The request URL of the GeoLocation backend.
--- a/browser/devtools/layoutview/test/browser.ini
+++ b/browser/devtools/layoutview/test/browser.ini
@@ -1,12 +1,17 @@
 [DEFAULT]
 skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
 subsuite = devtools
 support-files =
+  doc_layoutview_iframe1.html
+  doc_layoutview_iframe2.html
   head.js
 
 [browser_layoutview.js]
 [browser_layoutview_rotate-labels-on-sides.js]
+[browser_layoutview_update-after-navigation.js]
+[browser_layoutview_update-after-reload.js]
+[browser_layoutview_update-in-iframes.js]
 [browser_editablemodel.js]
 [browser_editablemodel_allproperties.js]
 [browser_editablemodel_border.js]
 [browser_editablemodel_stylerules.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/layoutview/test/browser_layoutview_update-after-navigation.js
@@ -0,0 +1,99 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the layout-view continues to work after a page navigation and that
+// it also works after going back
+
+let test = asyncTest(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
+  let {toolbox, inspector, view} = yield openLayoutView();
+  yield runTests(inspector, view);
+  yield destroyToolbox(inspector);
+});
+
+addTest("Test that the layout-view works on the first page",
+function*(inspector, view) {
+  info("Selecting the test node");
+  yield selectNode("p", inspector);
+
+  info("Checking that the layout-view shows the right value");
+  let paddingElt = view.doc.querySelector(".padding.top > span");
+  is(paddingElt.textContent, "50");
+
+  info("Listening for layout-view changes and modifying the padding");
+  let onUpdated = waitForUpdate(inspector);
+  getNode("p").style.padding = "20px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(paddingElt.textContent, "20");
+});
+
+addTest("Navigate to the second page",
+function*(inspector, view) {
+  yield navigateTo(TEST_URL_ROOT + "doc_layoutview_iframe2.html");
+  yield inspector.once("markuploaded");
+});
+
+addTest("Test that the layout-view works on the second page",
+function*(inspector, view) {
+  info("Selecting the test node");
+  yield selectNode("p", inspector);
+
+  info("Checking that the layout-view shows the right value");
+  let sizeElt = view.doc.querySelector(".size > span");
+  is(sizeElt.textContent, "100x100");
+
+  info("Listening for layout-view changes and modifying the size");
+  let onUpdated = waitForUpdate(inspector);
+  getNode("p").style.width = "200px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(sizeElt.textContent, "200x100");
+});
+
+addTest("Go back to the first page",
+function*(inspector, view) {
+  content.history.back();
+  yield inspector.once("markuploaded");
+});
+
+addTest("Test that the layout-view works on the first page after going back",
+function*(inspector, view) {
+  info("Selecting the test node");
+  yield selectNode("p", inspector);
+
+  info("Checking that the layout-view shows the right value, which is the" +
+    "modified value from step one because of the bfcache");
+  let paddingElt = view.doc.querySelector(".padding.top > span");
+  is(paddingElt.textContent, "20");
+
+  info("Listening for layout-view changes and modifying the padding");
+  let onUpdated = waitForUpdate(inspector);
+  getNode("p").style.padding = "100px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(paddingElt.textContent, "100");
+});
+
+function navigateTo(url) {
+  info("Navigating to " + url);
+
+  let def = promise.defer();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    info("URL " + url + " loading complete");
+    waitForFocus(def.resolve, content);
+  }, true);
+  content.location = url;
+
+  return def.promise;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/layoutview/test/browser_layoutview_update-after-reload.js
@@ -0,0 +1,42 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the layout-view continues to work after the page is reloaded
+
+let test = asyncTest(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
+  let {toolbox, inspector, view} = yield openLayoutView();
+
+  info("Test that the layout-view works on the first page");
+  yield assertLayoutView(inspector, view);
+
+  info("Reload the page");
+  content.location.reload();
+  yield inspector.once("markuploaded");
+
+  info("Test that the layout-view works on the reloaded page");
+  yield assertLayoutView(inspector, view);
+
+  yield destroyToolbox(inspector);
+});
+
+function* assertLayoutView(inspector, view) {
+  info("Selecting the test node");
+  yield selectNode("p", inspector);
+
+  info("Checking that the layout-view shows the right value");
+  let paddingElt = view.doc.querySelector(".padding.top > span");
+  is(paddingElt.textContent, "50");
+
+  info("Listening for layout-view changes and modifying the padding");
+  let onUpdated = waitForUpdate(inspector);
+  getNode("p").style.padding = "20px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(paddingElt.textContent, "20");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/layoutview/test/browser_layoutview_update-in-iframes.js
@@ -0,0 +1,61 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the layout-view for elements within iframes also updates when they
+// change
+
+let test = asyncTest(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
+  let iframe2 = getNode("iframe").contentDocument.querySelector("iframe");
+
+  let {toolbox, inspector, view} = yield openLayoutView();
+  yield runTests(inspector, view, iframe2);
+  yield destroyToolbox(inspector);
+});
+
+addTest("Test that resizing an element in an iframe updates its box model",
+function*(inspector, view, iframe2) {
+  info("Selecting the nested test node");
+  let node = iframe2.contentDocument.querySelector("div");
+  yield selectNode(node, inspector);
+
+  info("Checking that the layout-view shows the right value");
+  let sizeElt = view.doc.querySelector(".size > span");
+  is(sizeElt.textContent, "400x200");
+
+  info("Listening for layout-view changes and modifying its size");
+  let onUpdated = waitForUpdate(inspector);
+  node.style.width = "200px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(sizeElt.textContent, "200x200");
+});
+
+addTest("Test reflows are still sent to the layout-view after deleting an iframe",
+function*(inspector, view, iframe2) {
+  info("Deleting the iframe2");
+  iframe2.remove();
+  yield inspector.once("inspector-updated");
+
+  info("Selecting the test node in iframe1");
+  let node = getNode("iframe").contentDocument.querySelector("p");
+  yield selectNode(node, inspector);
+
+  info("Checking that the layout-view shows the right value");
+  let sizeElt = view.doc.querySelector(".size > span");
+  is(sizeElt.textContent, "100x100");
+
+  info("Listening for layout-view changes and modifying its size");
+  let onUpdated = waitForUpdate(inspector);
+  node.style.width = "200px";
+  yield onUpdated;
+  ok(true, "Layout-view got updated");
+
+  info("Checking that the layout-view shows the right value after update");
+  is(sizeElt.textContent, "200x100");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/layoutview/test/doc_layoutview_iframe1.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<p style="padding:50px;color:#f06;">Root page</p>
+<iframe src="doc_layoutview_iframe2.html"></iframe>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/layoutview/test/doc_layoutview_iframe2.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<p style="width:100px;height:100px;background:red;">iframe 1</p>
+<iframe src="data:text/html,<div style='width:400px;height:200px;background:yellow;'>iframe 2</div>"></iframe>
\ No newline at end of file
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -18,22 +18,24 @@ support-files = head.js
 # [browser_scratchpad_confirm_close.js]
 # Disable test due to bug 807234 becoming basically permanent
 [browser_scratchpad_tab.js]
 [browser_scratchpad_wrong_window_focus.js]
 [browser_scratchpad_unsaved.js]
 [browser_scratchpad_falsy.js]
 [browser_scratchpad_edit_ui_updates.js]
 [browser_scratchpad_revert_to_saved.js]
+[browser_scratchpad_run_error_goto_line.js]
 [browser_scratchpad_contexts.js]
 [browser_scratchpad_execute_print.js]
 [browser_scratchpad_files.js]
 [browser_scratchpad_initialization.js]
 [browser_scratchpad_inspect.js]
 [browser_scratchpad_long_string.js]
 [browser_scratchpad_open.js]
 [browser_scratchpad_open_error_console.js]
 [browser_scratchpad_throw_output.js]
 [browser_scratchpad_pprint-02.js]
 [browser_scratchpad_pprint.js]
+[browser_scratchpad_pprint_error_goto_line.js]
 [browser_scratchpad_restore.js]
 [browser_scratchpad_tab_switch.js]
 [browser_scratchpad_ui.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_pprint_error_goto_line.js
@@ -0,0 +1,66 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html;charset=utf8,test Scratchpad pretty print error goto line.";
+}
+
+function testJumpToPrettyPrintError(sp, error, remark) {
+  info("will test jumpToLine after prettyPrint error" + remark);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    is(/Invalid regexp flag \(3:10\)/.test(error), true, "prettyPrint expects error in editor text:\n" + error);
+    const errorLine = 3, errorColumn = 10;
+    const editorDoc = sp.editor.container.contentDocument;
+    sp.editor.jumpToLine();
+    const lineInput = editorDoc.querySelector("input");
+    const errorLocation = lineInput.value;
+    const [ inputLine, inputColumn ] = errorLocation.split(":");
+    is(inputLine, errorLine, "jumpToLine input field is set from editor selection (line)");
+    is(inputColumn, errorColumn, "jumpToLine input field is set from editor selection (column)");
+    EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    const cursor = sp.editor.getCursor();
+    is(inputLine, cursor.line + 1, "jumpToLine goto error location (line)");
+    is(inputColumn, cursor.ch + 1, "jumpToLine goto error location (column)");
+}
+
+function runTests(sw, sp)
+{
+  sp.setText([
+    "// line 1",
+    "// line 2",
+    "var re = /a bad /regexp/; // line 3 is an obvious syntax error!",
+    "// line 4",
+    "// line 5",
+    ""
+  ].join("\n"));
+  sp.prettyPrint().then(aFulfill => {
+    ok(false, "Expecting Invalid regexp flag (3:10)");
+    finish();
+  }, error => {
+    testJumpToPrettyPrintError(sp, error, " (Bug 1005471, first time)");
+  });
+  sp.prettyPrint().then(aFulfill => {
+    ok(false, "Expecting Invalid regexp flag (3:10)");
+    finish();
+  }, error => {
+    // Second time verifies bug in earlier implementation fixed.
+    testJumpToPrettyPrintError(sp, error, " (second time)");
+    finish();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_run_error_goto_line.js
@@ -0,0 +1,49 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html;charset=utf8,test Scratchpad pretty print.";
+}
+
+function runTests(sw)
+{
+  const sp = sw.Scratchpad;
+  sp.setText([
+    "// line 1",
+    "// line 2",
+    "var re = /a bad /regexp/; // line 3 is an obvious syntax error!",
+    "// line 4",
+    "// line 5",
+    ""
+  ].join("\n"));
+  sp.run().then(() => {
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    let errorLine = 3;
+    let editorDoc = sp.editor.container.contentDocument;
+    sp.editor.jumpToLine();
+    let lineInput = editorDoc.querySelector("input");
+    let inputLine = lineInput.value;
+    is(inputLine, errorLine, "jumpToLine input field is set from editor selection");
+    EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+    // CodeMirror lines and columns are 0-based, Scratchpad UI and error
+    // stack are 1-based.
+    let cursor = sp.editor.getCursor();
+    is(cursor.line + 1, inputLine, "jumpToLine goto error location (line)");
+    is(cursor.ch + 1, 1, "jumpToLine goto error location (column)");
+    finish();
+  });
+}
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -1,8 +1,9 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu, Cc, Ci, components } = require("chrome");
@@ -15,16 +16,21 @@ const DETECT_INDENT = "devtools.editor.d
 const DETECT_INDENT_MAX_LINES = 500;
 const L10N_BUNDLE = "chrome://browser/locale/devtools/sourceeditor.properties";
 const XUL_NS      = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // Maximum allowed margin (in number of lines) from top or bottom of the editor
 // while shifting to a line which was initially out of view.
 const MAX_VERTICAL_OFFSET = 3;
 
+// Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
+// line in text selection.
+const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
+const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
+
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const events  = require("devtools/toolkit/event-emitter");
 
 Cu.import("resource://gre/modules/Services.jsm");
 const L10N = Services.strings.createBundle(L10N_BUNDLE);
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
@@ -747,17 +753,38 @@ Editor.prototype = {
 
     inp.type = "text";
     inp.style.width = "10em";
     inp.style.MozMarginStart = "1em";
 
     div.appendChild(txt);
     div.appendChild(inp);
 
-    this.openDialog(div, (line) => this.setCursor({ line: line - 1, ch: 0 }));
+    if (!this.hasMultipleSelections()) {
+      let cm = editors.get(this);
+      let sel = cm.getSelection();
+      // Scratchpad inserts and selects a comment after an error happens:
+      // "@Scratchpad/1:10:2". Parse this to get the line and column.
+      // In the string above this is line 10, column 2.
+      let match = sel.match(RE_SCRATCHPAD_ERROR);
+      if (match) {
+        let [ , line, column ] = match;
+        inp.value = column ? line + ":" + column : line;
+        inp.selectionStart = inp.selectionEnd = inp.value.length;
+      }
+    }
+
+    this.openDialog(div, (line) => {
+      // Handle LINE:COLUMN as well as LINE
+      let match = line.toString().match(RE_JUMP_TO_LINE);
+      if (match) {
+        let [ , line, column ] = match;
+        this.setCursor({line: line - 1, ch: column ? column - 1 : 0 });
+      }
+    });
   },
 
   /**
    * Moves the content of the current line or the lines selected up a line.
    */
   moveLineUp: function () {
     let cm = editors.get(this);
     let start = cm.getCursor("start");
--- a/browser/devtools/sourceeditor/test/browser.ini
+++ b/browser/devtools/sourceeditor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
   css_statemachine_testcases.css
   css_statemachine_tests.json
   css_autocompletion_tests.json
   vimemacs.html
   head.js
 
 [browser_editor_basic.js]
 [browser_editor_cursor.js]
+[browser_editor_goto_line.js]
 [browser_editor_history.js]
 [browser_editor_markers.js]
 [browser_editor_movelines.js]
 [browser_editor_addons.js]
 [browser_codemirror.js]
 [browser_css_autocompletion.js]
 [browser_css_getInfo.js]
 [browser_css_statemachine.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_goto_line.js
@@ -0,0 +1,130 @@
+/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function testJumpToLine (ed, inputLine, expectCursor) {
+  ed.jumpToLine();
+  let editorDoc = ed.container.contentDocument;
+  let lineInput = editorDoc.querySelector("input");
+  lineInput.value = inputLine;
+  EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
+  // CodeMirror lines and columns are 0-based, Scratchpad UI is 1-based.
+  ch(ed.getCursor(), expectCursor, "jumpToLine " + inputLine + " expects cursor " + expectCursor.toSource());
+}
+
+function test() {
+  waitForExplicitFinish();
+  setup((ed, win) => {
+    let textLines = [
+      "// line 1",
+      "//  line 2",
+      "//   line 3",
+      "//    line 4",
+      "//     line 5",
+      ""];
+    ed.setText(textLines.join("\n"));
+    waitForFocus(function () {
+      let testVectors = [
+        // Various useless inputs go to line 0, column 0 or do nothing.
+        ["",
+         {line:0, ch:0}],
+        [":",
+         {line:0, ch:0}],
+        [" ",
+         {line:0, ch:0}],
+        [" : ",
+         {line:0, ch:0}],
+        ["a:b",
+         {line:0, ch:0}],
+        ["LINE:COLUMN",
+         {line:0, ch:0}],
+        ["-1",
+         {line:0, ch:0}],
+        [":-1",
+         {line:0, ch:0}],
+        ["-1:-1",
+         {line:0, ch:0}],
+        ["0",
+         {line:0, ch:0}],
+        [":0",
+         {line:0, ch:0}],
+        ["0:0",
+         {line:0, ch:0}],
+        // Starting here expect data needs to get updated for length changes to
+        // "textLines" above.
+        // Just jump to line
+        ["1",
+         {line:0, ch:0}],
+        // Jump to second character in line
+        ["1:2",
+         {line:0, ch:1}],
+        // Jump to last character on line
+        ["1:9",
+         {line:0, ch:8}],
+        // Jump just after last character on line (end of line)
+        ["1:10",
+         {line:0, ch:9}],
+        // Jump one character past end of line (gets clamped to end of line)
+        ["1:11",
+         {line:0, ch:9}],
+        ["2",
+         {line:1, ch:0}],
+        ["2:2",
+         {line:1, ch:1}],
+        ["2:10",
+         {line:1, ch:9}],
+        ["2:11",
+         {line:1, ch:10}],
+        ["2:12",
+         {line:1, ch:10}],
+        ["3",
+         {line:2, ch:0}],
+        ["3:2",
+         {line:2, ch:1}],
+        ["3:11",
+         {line:2, ch:10}],
+        ["3:12",
+         {line:2, ch:11}],
+        ["3:13",
+         {line:2, ch:11}],
+        ["4",
+         {line:3, ch:0}],
+        ["4:2",
+         {line:3, ch:1}],
+        ["4:12",
+         {line:3, ch:11}],
+        ["4:13",
+         {line:3, ch:12}],
+        ["4:14",
+         {line:3, ch:12}],
+        ["5",
+         {line:4, ch:0}],
+        ["5:2",
+         {line:4, ch:1}],
+        ["5:13",
+         {line:4, ch:12}],
+        ["5:14",
+         {line:4, ch:13}],
+        ["5:15",
+         {line:4, ch:13}],
+        // One line beyond last newline in editor text:
+        ["6",
+         {line:5, ch:0}],
+        ["6:2",
+         {line:5, ch:0}],
+        // Two line beyond last newline in editor text (gets clamped):
+        ["7",
+         {line:5, ch:0}],
+        ["7:2",
+         {line:5, ch:0}]
+      ];
+      testVectors.forEach(function (vector) {
+        testJumpToLine(ed, vector[0], vector[1]);
+      });
+      teardown(ed, win);
+    });
+  });
+}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
@@ -1,57 +1,51 @@
 /* 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/. */
 
 // This test makes sure that the style editor does not store any
 // content CSS files in the permanent cache when opened from PB mode.
 
-let gUI;
-
 function test() {
   waitForExplicitFinish();
-  let windowsToClose = [];
+  let gUI;
   let testURI = 'http://' + TEST_HOST + '/browser/browser/devtools/styleeditor/test/test_private.html';
 
-  function checkCache() {
-    checkDiskCacheFor(TEST_HOST, function() {
-      gUI = null;
-      finish();
-    });
-  }
+  info("Opening a new private window");
+  let win = OpenBrowserWindow({private: true});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    executeSoon(startTest);
+  }, false);
 
-  function doTest(aWindow) {
-    aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
-      aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+  function startTest() {
+    win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+      win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+
+      info("Clearing the browser cache");
       cache.clear();
-      openStyleEditorInWindow(aWindow, function(panel) {
+
+      info("Opening the style editor in the private window");
+      openStyleEditorInWindow(win, function(panel) {
         gUI = panel.UI;
         gUI.on("editor-added", onEditorAdded);
       });
     }, true);
 
-    aWindow.gBrowser.selectedBrowser.loadURI(testURI);
+    info("Loading the test URL in the new private window");
+    win.content.location = testURI;
   }
 
   function onEditorAdded(aEvent, aEditor) {
+    info("The style editor is ready")
     aEditor.getSourceEditor().then(checkCache);
   }
 
-  function testOnWindow(options, callback) {
-    let win = OpenBrowserWindow(options);
-    win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      windowsToClose.push(win);
-      executeSoon(function() callback(win));
-    }, false);
-  };
-
-  registerCleanupFunction(function() {
-    windowsToClose.forEach(function(win) {
+  function checkCache() {
+    checkDiskCacheFor(TEST_HOST, function() {
       win.close();
+      win = null;
+      gUI = null;
+      finish();
     });
-  });
-
-  testOnWindow({private: true}, function(win) {
-    doTest(win);
-  });
+  }
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -267,20 +267,28 @@ browser.jar:
   skin/classic/browser/devtools/debugger-step-over.png (../shared/devtools/images/debugger-step-over.png)
   skin/classic/browser/devtools/debugger-step-over@2x.png (../shared/devtools/images/debugger-step-over@2x.png)
   skin/classic/browser/devtools/debugger-blackbox.png  (../shared/devtools/images/debugger-blackbox.png)
   skin/classic/browser/devtools/debugger-blackbox@2x.png  (../shared/devtools/images/debugger-blackbox@2x.png)
   skin/classic/browser/devtools/debugger-toggleBreakpoints.png (../shared/devtools/images/debugger-toggleBreakpoints.png)
   skin/classic/browser/devtools/debugger-toggleBreakpoints@2x.png (../shared/devtools/images/debugger-toggleBreakpoints@2x.png)
   skin/classic/browser/devtools/tracer-icon.png        (../shared/devtools/images/tracer-icon.png)
   skin/classic/browser/devtools/tracer-icon@2x.png     (../shared/devtools/images/tracer-icon@2x.png)
-  skin/classic/browser/devtools/responsive-se-resizer.png (../shared/devtools/images/responsive-se-resizer.png)
-  skin/classic/browser/devtools/responsive-vertical-resizer.png (../shared/devtools/images/responsive-vertical-resizer.png)
-  skin/classic/browser/devtools/responsive-horizontal-resizer.png (../shared/devtools/images/responsive-horizontal-resizer.png)
-  skin/classic/browser/devtools/responsive-background.png (../shared/devtools/images/responsive-background.png)
+  skin/classic/browser/devtools/responsive-se-resizer.png          (../shared/devtools/images/responsivemode/responsive-se-resizer.png)
+  skin/classic/browser/devtools/responsive-se-resizer@2x.png       (../shared/devtools/images/responsivemode/responsive-se-resizer@2x.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer.png    (../shared/devtools/images/responsivemode/responsive-vertical-resizer.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer@2x.png (../shared/devtools/images/responsivemode/responsive-vertical-resizer@2x.png)
+  skin/classic/browser/devtools/responsive-horizontal-resizer.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer.png)
+  skin/classic/browser/devtools/responsive-horizontal-resizer@2x.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer@2x.png)
+  skin/classic/browser/devtools/responsiveui-rotate.png          (../shared/devtools/images/responsivemode/responsiveui-rotate.png)
+  skin/classic/browser/devtools/responsiveui-rotate@2x.png       (../shared/devtools/images/responsivemode/responsiveui-rotate@2x.png)
+  skin/classic/browser/devtools/responsiveui-touch.png           (../shared/devtools/images/responsivemode/responsiveui-touch.png)
+  skin/classic/browser/devtools/responsiveui-touch@2x.png        (../shared/devtools/images/responsivemode/responsiveui-touch@2x.png)
+  skin/classic/browser/devtools/responsiveui-screenshot.png      (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
+  skin/classic/browser/devtools/responsiveui-screenshot@2x.png   (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
   skin/classic/browser/devtools/toggle-tools.png          (../shared/devtools/images/toggle-tools.png)
   skin/classic/browser/devtools/dock-bottom@2x.png        (../shared/devtools/images/dock-bottom@2x.png)
   skin/classic/browser/devtools/dock-side@2x.png          (../shared/devtools/images/dock-side@2x.png)
   skin/classic/browser/devtools/floating-scrollbars.css   (devtools/floating-scrollbars.css)
   skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
 * skin/classic/browser/devtools/inspector.css               (../shared/devtools/inspector.css)
   skin/classic/browser/devtools/profiler-stopwatch.svg      (../shared/devtools/images/profiler-stopwatch.svg)
   skin/classic/browser/devtools/profiler-stopwatch-checked.svg      (../shared/devtools/images/profiler-stopwatch-checked.svg)
@@ -304,19 +312,16 @@ browser.jar:
   skin/classic/browser/devtools/vview-lock.png              (../shared/devtools/images/vview-lock.png)
   skin/classic/browser/devtools/vview-lock@2x.png           (../shared/devtools/images/vview-lock@2x.png)
   skin/classic/browser/devtools/vview-open-inspector.png    (../shared/devtools/images/vview-open-inspector.png)
   skin/classic/browser/devtools/vview-open-inspector@2x.png (../shared/devtools/images/vview-open-inspector@2x.png)
   skin/classic/browser/devtools/undock@2x.png               (../shared/devtools/images/undock@2x.png)
   skin/classic/browser/devtools/font-inspector.css          (../shared/devtools/font-inspector.css)
   skin/classic/browser/devtools/computedview.css            (../shared/devtools/computedview.css)
   skin/classic/browser/devtools/arrow-e.png                 (../shared/devtools/images/arrow-e.png)
-  skin/classic/browser/devtools/responsiveui-rotate.png     (../shared/devtools/responsiveui-rotate.png)
-  skin/classic/browser/devtools/responsiveui-touch.png      (../shared/devtools/responsiveui-touch.png)
-  skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
   skin/classic/browser/devtools/projecteditor/projecteditor.css         (../shared/devtools/projecteditor/projecteditor.css)
   skin/classic/browser/devtools/projecteditor/file-icons-sheet@2x.png       (../shared/devtools/projecteditor/file-icons-sheet@2x.png)
   skin/classic/browser/devtools/app-manager/connection-footer.css     (../shared/devtools/app-manager/connection-footer.css)
   skin/classic/browser/devtools/app-manager/index.css                 (../shared/devtools/app-manager/index.css)
   skin/classic/browser/devtools/app-manager/device.css                (../shared/devtools/app-manager/device.css)
   skin/classic/browser/devtools/app-manager/projects.css              (../shared/devtools/app-manager/projects.css)
   skin/classic/browser/devtools/app-manager/help.css                  (../shared/devtools/app-manager/help.css)
   skin/classic/browser/devtools/app-manager/warning.svg               (../shared/devtools/app-manager/images/warning.svg)
--- a/browser/themes/linux/preferences/in-content/preferences.css
+++ b/browser/themes/linux/preferences/in-content/preferences.css
@@ -1,14 +1,22 @@
 /* - This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../../shared/incontentprefs/preferences.css
 
+tab[selected] {
+  /* Override styles for tab[selected] from
+     toolkit/themes/linux/global/tabbox.css */
+  margin-bottom: 0;
+  border-bottom-left-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
 button > .button-box,
 menulist > .menulist-label-box {
   -moz-appearance: none;
 }
 
 button[type="menu"] > .button-box > .button-menu-dropmarker {
   -moz-appearance: none !important;
 }
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -387,20 +387,28 @@ browser.jar:
   skin/classic/browser/devtools/debugger-blackbox.png       (../shared/devtools/images/debugger-blackbox.png)
   skin/classic/browser/devtools/debugger-blackbox@2x.png    (../shared/devtools/images/debugger-blackbox@2x.png)
   skin/classic/browser/devtools/debugger-toggleBreakpoints.png (../shared/devtools/images/debugger-toggleBreakpoints.png)
   skin/classic/browser/devtools/debugger-toggleBreakpoints@2x.png (../shared/devtools/images/debugger-toggleBreakpoints@2x.png)
   skin/classic/browser/devtools/tracer-icon.png             (../shared/devtools/images/tracer-icon.png)
   skin/classic/browser/devtools/tracer-icon@2x.png          (../shared/devtools/images/tracer-icon@2x.png)
   skin/classic/browser/devtools/floating-scrollbars.css     (devtools/floating-scrollbars.css)
   skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
-  skin/classic/browser/devtools/responsive-se-resizer.png   (../shared/devtools/images/responsive-se-resizer.png)
-  skin/classic/browser/devtools/responsive-vertical-resizer.png (../shared/devtools/images/responsive-vertical-resizer.png)
-  skin/classic/browser/devtools/responsive-horizontal-resizer.png (../shared/devtools/images/responsive-horizontal-resizer.png)
-  skin/classic/browser/devtools/responsive-background.png   (../shared/devtools/images/responsive-background.png)
+  skin/classic/browser/devtools/responsive-se-resizer.png          (../shared/devtools/images/responsivemode/responsive-se-resizer.png)
+  skin/classic/browser/devtools/responsive-se-resizer@2x.png       (../shared/devtools/images/responsivemode/responsive-se-resizer@2x.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer.png    (../shared/devtools/images/responsivemode/responsive-vertical-resizer.png)
+  skin/classic/browser/devtools/responsive-vertical-resizer@2x.png (../shared/devtools/images/responsivemode/responsive-vertical-resizer@2x.png)
+  skin/classic/browser/devtools/responsive-horizontal-resizer.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer.png)
+  skin/classic/browser/devtools/responsive-horizontal-resizer@2x.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer@2x.png)
+  skin/classic/browser/devtools/responsiveui-rotate.png          (../shared/devtools/images/responsivemode/responsiveui-rotate.png)
+  skin/classic/browser/devtools/responsiveui-rotate@2x.png       (../shared/devtools/images/responsivemode/responsiveui-rotate@2x.png)
+  skin/classic/browser/devtools/responsiveui-touch.png           (../shared/devtools/images/responsivemode/responsiveui-touch.png)
+  skin/classic/browser/devtools/responsiveui-touch@2x.png        (../shared/devtools/images/responsivemode/responsiveui-touch@2x.png)
+  skin/classic/browser/devtools/responsiveui-screenshot.png      (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
+  skin/classic/browser/devtools/responsiveui-screenshot@2x.png   (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
   skin/classic/browser/devtools/toggle-tools.png            (../shared/devtools/images/toggle-tools.png)
   skin/classic/browser/devtools/dock-bottom@2x.png          (../shared/devtools/images/dock-bottom@2x.png)
   skin/classic/browser/devtools/dock-side@2x.png          (../shared/devtools/images/dock-side@2x.png)
 * skin/classic/browser/devtools/inspector.css               (../shared/devtools/inspector.css)
   skin/classic/browser/devtools/profiler-stopwatch.svg      (../shared/devtools/images/profiler-stopwatch.svg)
   skin/classic/browser/devtools/profiler-stopwatch-checked.svg      (../shared/devtools/images/profiler-stopwatch-checked.svg)
   skin/classic/browser/devtools/tool-options.svg            (../shared/devtools/images/tool-options.svg)
   skin/classic/browser/devtools/tool-webconsole.svg         (../shared/devtools/images/tool-webconsole.svg)
@@ -422,19 +430,16 @@ browser.jar:
   skin/classic/browser/devtools/vview-lock.png              (../shared/devtools/images/vview-lock.png)
   skin/classic/browser/devtools/vview-lock@2x.png           (../shared/devtools/images/vview-lock@2x.png)
   skin/classic/browser/devtools/vview-open-inspector.png    (../shared/devtools/images/vview-open-inspector.png)
   skin/classic/browser/devtools/vview-open-inspector@2x.png (../shared/devtools/images/vview-open-inspector@2x.png)
   skin/classic/browser/devtools/undock@2x.png               (../shared/devtools/images/undock@2x.png)
   skin/classic/browser/devtools/font-inspector.css          (../shared/devtools/font-inspector.css)
   skin/classic/browser/devtools/computedview.css            (../shared/devtools/computedview.css)
   skin/classic/browser/devtools/arrow-e.png                 (../shared/devtools/images/arrow-e.png)
-  skin/classic/browser/devtools/responsiveui-rotate.png     (../shared/devtools/responsiveui-rotate.png)
-  skin/classic/browser/devtools/responsiveui-touch.png      (../shared/devtools/responsiveui-touch.png)
-  skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
   skin/classic/browser/devtools/projecteditor/projecteditor.css         (../shared/devtools/projecteditor/projecteditor.css)
   skin/classic/browser/devtools/projecteditor/file-icons-sheet@2x.png       (../shared/devtools/projecteditor/file-icons-sheet@2x.png)
   skin/classic/browser/devtools/app-manager/connection-footer.css     (../shared/devtools/app-manager/connection-footer.css)
   skin/classic/browser/devtools/app-manager/index.css                 (../shared/devtools/app-manager/index.css)
   skin/classic/browser/devtools/app-manager/device.css                (../shared/devtools/app-manager/device.css)
   skin/classic/browser/devtools/app-manager/projects.css              (../shared/devtools/app-manager/projects.css)
   skin/classic/browser/devtools/app-manager/help.css                  (../shared/devtools/app-manager/help.css)
   skin/classic/browser/devtools/app-manager/warning.svg               (../shared/devtools/app-manager/images/warning.svg)
--- a/browser/themes/osx/preferences/in-content/preferences.css
+++ b/browser/themes/osx/preferences/in-content/preferences.css
@@ -1,14 +1,28 @@
 /* - 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 ../../../shared/incontentprefs/preferences.css
 
+prefpane .groupbox-title {
+  background: none;
+  margin-bottom: 0;
+}
+
+tabs {
+  padding-right: 0;
+  padding-left: 0;
+}
+
+tab[selected] {
+  text-shadow: none;
+}
+
 menulist:not([editable="true"]) > menupopup > menuitem[checked="true"]::before,
 menulist:not([editable="true"]) > menupopup > menuitem[selected="true"]::before {
   display: none;
 }
 
 menulist:not([editable="true"]) > .menulist-dropmarker {
   display: -moz-box;
   margin-top: 1px;
deleted file mode 100644
index 271dd5ebcfbdc858f83f0f7ea18a59bb1980d74f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
rename from browser/themes/shared/devtools/images/responsive-horizontal-resizer.png
rename to browser/themes/shared/devtools/images/responsivemode/responsive-horizontal-resizer.png
index 86ce453b22601b6d1e6e2b6be5a4e8a6e108b7f2..7d113f0df411078d756230cdc62e7a9257febeae
GIT binary patch
literal 102
zc%17D@N?(olHy`uVBq!ia0vp^5<tw(!3HFSzV~heQo5cljv*T7lM@!O|M>Ja{pR)e
z`_qDi>wo{;&6CI2;hf98ujY5`IR|qCDVHA%3{RwtR&Ad4TokC0!PC{xWt~$(69CO8
BA;<s#
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bb3c4bde064c0817c853ce90efb018b335a8ea0e
GIT binary patch
literal 129
zc%17D@N?(olHy`uVBq!ia0vp^20+Zm!3HEVi{?iGDGyH<#}JR>$q5py%`7$l{sce2
z-9JAze*gbjmNN`b^eSvmsz}hVkh>}De2Y1L-`_V9n>;z5RIKsL5b2rbw0P--3{8_L
du3irYhDlndT-S5_S_d?f!PC{xWt~$(69COGEFk~@
rename from browser/themes/shared/devtools/images/responsive-se-resizer.png
rename to browser/themes/shared/devtools/images/responsivemode/responsive-se-resizer.png
index bdc03ee24ae6e011d00e983b93c9f270b6bb813f..35b54d62cb4b0b79b9719a25a5176fb58182247a
GIT binary patch
literal 129
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`9-c0aAr_~T6C_xfnfKNFj{Nra
z`Fo>HCiQ>+Wvg-2|NORlPR>~cn+>x(xtL4Ns<bldopq^fRywO-zv%bOB?b(hO1~!x
c%vWMyQ0`i~>Qi#`R-l;-p00i_>zopr00hh|)&Kwi
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9dbf4fe8e8e42a01ce4436e30f02c7ec506b406e
GIT binary patch
literal 205
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJDV{ElAr-gYPVweyFyL{iR&c#j
z;Z~^s$ny8blWvj%yCx_ty`Ye$5Fjy0oJ(?X(8`ciVM<oN1u`8Yy<>xS6x+0%SZ!b7
zp2%rvaf)@;(g!QfR?8iI&{!GtL;m4;nGCk`Pa6bmAXq|X-=Y1S$7P?l@Bh8y?<yIG
z<g|*7{pClQGAmZbPh@_{eX+-S-N%n+tD?m(3EkwBb<E8Gx{txr)z4*}Q$iB}bOldq
rename from browser/themes/shared/devtools/images/responsive-vertical-resizer.png
rename to browser/themes/shared/devtools/images/responsivemode/responsive-vertical-resizer.png
index 1bb622dd397bf5b62cc89c03f5f77195713bf0da..3b4e78c6fd55144019fcf9df882cc7d5377852ae
GIT binary patch
literal 105
zc%17D@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE<K+MqzpV=978mMlM@tJ_SO83{Py<w
zd!rIFiJHIvveh^iZ+2I8SM8k2qPjx=e%<uNn?Eu!@ZL3ACA5;U2&k9A)78&qol`;+
E0NwK;1poj5
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cbae60621ddc6b71a468964e16162b5fdd419c9a
GIT binary patch
literal 141
zc%17D@N?(olHy`uVBq!ia0vp^d_Zi#!VDxYi-p+(DgFST5LY1m`SZ^&-+q4k@$>8V
zUuDl;d;kiumIV0)Gw5`5&FmLr*8>V@c)B=-NQ5URIItgTQ#f)U;SrA$509Rnf{E8;
k@7WfO0tPXnjvTTK41%?~-cCko!azL?p00i_>zopr06pR<L;wH)
rename from browser/themes/shared/devtools/responsiveui-rotate.png
rename to browser/themes/shared/devtools/images/responsivemode/responsiveui-rotate.png
index 1e30f708697532e260694d36d6d0ae50cd491ef7..2bacbd2d559b72468aca1b72e07d8d900359a63a
GIT binary patch
literal 245
zc$@+C01E$!P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002INkl<Zc-qyH
zJqiLb5QQTiMNrV%N^CuV=n1T>xA6qtz*^A8QuJmfncpHZTkT@Vih|&SDU!$cW+ue_
zuK;+JH9YeeQFnlHj==W-d<sAnQ5rU3^IBj^sUJzUW@L->7Ev}Hcx~sGfIM8T*4Ql*
z<rq=-5#;bzfbDD-_1;mYt|1~C1(ZuMpaw~=L^Q~~cIO0jchaf^cTNyU7X`Q+VBJA=
v=0278JOSXP`$iAc`8T}%#Gd^|_iH@?(^^+@;3c~}00000NkvXXu0mjfzR6`a
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..eeeb823287fddd28de8d3127559a3675f545a25b
GIT binary patch
literal 438
zc$@*Y0ZIOeP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004fNkl<Zc-rlj
zyGlbr5QgIgW0S@{f)-X<_yoRyg@uBmjoPLNh}fGbhzj}!5^JNx$4aAScP_huks)Y6
zPvTiuSdcR?)z0#NyJzM<TPmW6Q~|)7NyRPjbMQt)TrkLc7X3M(ySL=qnt-ll(Kik7
z4uDkihz9}mn3sY0Qa9yF^rvq`OQXbd1Bl2BkgHkp9o-O^9c$g8h)idRYyKo&G2pJW
zFe;H70KA>GHumHO-1V?HK7#=C+O;4FfHAzH-#i(>xNbMVEd#C&+~2a6Hw?OcAimIs
z`xAf{qQ$I2Ab)GETNROs5&kf5(Kn(2)MUPkyPA$tj**?z#*G3{hx>B!BRzG9y+#43
zy$<Ks-HFI-sCe8B06%^fP=7sCJRX32dm!$gy4`LiP&^($$4nOtxtj>_s3vR<=ADjz
zAs!F#@zYSVN%T^Y>Hxr>lv0I7K2tkR2B0q)ApamD<%GB34tnU<k^zXwltJEA8%_yP
geifN374hHW9o`}do}$9<-~a#s07*qoM6N<$f+ZBg@&Et;
rename from browser/themes/shared/devtools/responsiveui-screenshot.png
rename to browser/themes/shared/devtools/images/responsivemode/responsiveui-screenshot.png
index 82e6c3f3c5446fb17b430f688a33fbd28042120f..084220ed1d903c4450c11583a4e9da9398282454
GIT binary patch
literal 303
zc$@(*0nq-5P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002?Nkl<Zc-qaA
zO-ckY5QP=RtqLOkAb10};!4GZf(t!__YidN2~<QK#DyCVCYV&cq|)%DdS(QB8pG@+
z@Ig_$`a%&hH+x5w+Y$CAnm_-L%14a0_wO*tMS)%UKrhJGB$p-zquQMZrom@^1(P?~
zeLy+-B3B%*1@_R*^$I*la(T4Uf-|aD1@>4K#FS3F7l30)<=qLuVY_N7r&Yl>xz>h}
z2jiZ?1|;W2WUDF&A*^==UJsvGcdCMvnzLcJ94PErU{9*H-{w+tUKK>mFC;g*Pc6bt
zg}qh<P8-b41;AZLb8|Ufsb7F{<Bh36xt)#A_XF{K_MA(_L%aY0002ovPDHLkV1nhQ
BfWZI&
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..927c5cf0bcf649168a687e60dd4757ede293e63f
GIT binary patch
literal 531
zc$@(f0_^>XP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80005nNkl<Zc-rk&
z&nrYx6n;e!SxhN~6blQ3QnIlj`3vkM3$wHo3oEuZQkspj5amY_{;_H1-gEA`S6#j$
zZ{B#kH}6tvPJQ*d=R4<o^X_@)Ow_S|W#0RqH}wC2zTkyxwOWacJJux6SwL(rwYKxY
z1ZcJuB+do4XCVbtku)!e{7BwA?n>NeiR5Js$=U+Tf!2CBk+bW|;&pP1#P$Y(zX{Yp
ztrh{q_6mf*0BW@e$QrCjE)>g#QZl2IA0na=B{GXM__F57FTl7b=9f+HeQ%Uv@PPwv
zPytBTV7DA)G-kJim~aBLt)%?o0U+hm-3lTA4U-(N4UuUw?&O1mXY@ouTpwZ!3&0di
z6ra*04@n-*8VxBYCus3EKz1R{Bjb+JxbVffh%GFjX3RwFwRV5kT=0SO*^E41Sb#Bp
zGCry`$zvK9zWAs`Z1F2_&0|xu9Hw#Mi*pfMya|onZvMu3Op&zg44lCi=OQ-59Js?)
zY;G<ZWpVr3xLW|vIE!--gV-R0h{)|lISnyEcY$JA&srSZ;L8Xg<96%b52h7{51i23
zFo}CbW=FAG!T8nM&M9S9aRy)e>zM6190gG9537hTYh$a5?e%X$lRZVPsAC;_w^uVM
VXou&f+`s?;002ovPDHLkV1jZN^11*3
rename from browser/themes/shared/devtools/responsiveui-touch.png
rename to browser/themes/shared/devtools/images/responsivemode/responsiveui-touch.png
index c25b44d04402adadda813d70c849c1c19507e161..90587034ce786c0f0d061a3e893e2d4af2492345
GIT binary patch
literal 470
zc$@*&0V)28P)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^0004<Nkl<Zc-qaB
zF-ycS6vvAeCqWMU7LE?ua>K1oPNJXSocs=g)7>K8C<q=Rh3j?koLp|vkKp7WB8}p3
z2)1b(m6-q8m~dcCI{Dy{m)9omm;PVl=r$g6mlhw}31x>#UUU#6=NMW`yz57#?MHFo
z4aq5D<Q!8&L7KC@$%HS#5x>!5$8`O)co^?DFJBvQ#9l~tjRA0S6i#T-rTKdoF>>Yr
zae%_&^w+{~5F=*{Ac}TSG^ND|&N*+`0C*AUmjE2z8j)oHSV^D*Ahwl22S98kfowPl
zfY`PPF-icHvoZxBd%<qC0+78>8=xMD0;mb10BV9LfNBtX0PXK^?E#dp3~m=3X=1av
zG-vH3V<$9YXH$TT9alo+?5DH027KNhk|Qr7_rjmQ0phzV06%)_sne!xRcv~pcfJ4#
zssQj@u3rxyH^io&a`W;+np6Scxue?)=ZloFwKOXZ$}iA21%NfrA8_q%`VqM*0b(lv
zT@H+p#8d*9E(b<PVk*IJATEl`N`X1@e>EYR3`J(8z#Mtmg#W_609(Vpb)!QZy#N3J
M07*qoM6N<$g07*(+W-In
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e4645039cf57e2276838fbb9d4ad7aa27a96a1e9
GIT binary patch
literal 927
zc$@*B17Q4#P)<h;3K|Lk000e1NJLTq002M$001Be1^@s6qMd$(000AKNkl<Zc-rll
zziSjh6vwwnj0sUuY%H|WBBT*gc)LM~b^*1p5ft%Hu+X5DKZ2n6tI(jvU?aieb}s<~
ziXf!15V5fk!6*ijn@EBoaJMu20~zO=WK`C3Z*x2E()i$UGj9goyw84jc5bY!HaRI<
zgoqDTd3@gI(Oo#&h+)hkwmy}^cE26Az4A@lWiA|T#QLc7FD)%?^+kNq7twGn#~9{N
z<?ngQwyR=?lu!J_hcJgEIiBwC;={oxiuQh$aw8mV#54pcWedrZ*TuW+p;Mf-?cz*3
z9BssYK^T75Y8(J0u8UU%wkO9C(-0~`95S6B64%A|Ot6mRIAR(?!7Cpz4gl{tio?@J
z-}j@QDi5E)(MAkom{S$u1=IQQo})NCZS)K7OuyqX21gq)j4cCj*)8J$u-TzFNFM#=
z!g)9WM;kGWVGib+R{(QS93+oE3b#Eb;b<d<G0eeSG61@-i`TB>)Bto}7q4B%$pAps
zSpZPI0+4l90GLa%31$VL=8|lJnE@2bN69{e^#O#Jq?D~xpTP_OhPsO%Hjo{J>H(lW
z2&Dqhb7(Cc0KKIHfUOIF<hh{P0Fvi|rUOWx3z`iec`j%=0D48F1IVEW8URT}&;v+U
z1UUeDMWh4BsR()iNktF?pjQM6$>Z}<b`k?f_u|a9bp;Rv!JhR5&|8`<)J-C#Y~wsU
z=8O2MFQSo(2u75AZ<qj35#m5<a(&6+l?Rl+QnLNxl-Osr_}3-?IHlQBsmU3W9X^ll
z)mpp}06aXNmVe*q_sy#C_q7&p2Eg3qdVhaUVxd$3co3aQm-yTjER+ra8%+hkgZQi;
z#A9>-;(@gMSfJyT10n$M@hp2lYo!u)t?mPZYT?gT0RWJj3jmML)yv=ie5%cM%MSJd
z$73(mqJR7Wpqf(wZUdJhVpo+%x6xL=ck%$hE4J4Bz2026TKt<;0B>{d0oScY3||KP
zLKXQwgK~Ex`Sq1JRFMZjp;Yd*@|aw?0e;j-@whe0eM3~7?^F<`8Uj#aFDw(rIp3)u
zJYG}rL3Dp{adC^0i_$UHr3#u}BY=<lOVc|nQ?{_Mu&FAd(<|5E;dsrW5f{+|A;J?-
zj%Cnrq*X;-Q)>96JgikT;wT)SRw&2%Z~yHt`w46JtHcwcv8@0A002ovPDHLkV1i`T
Brj7sr
--- a/browser/themes/shared/devtools/responsivedesign.inc.css
+++ b/browser/themes/shared/devtools/responsivedesign.inc.css
@@ -2,17 +2,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/. */
 %endif
 
 /* Responsive Mode */
 
 .browserContainer[responsivemode] {
-  background: #222 url("chrome://browser/skin/devtools/responsive-background.png");
+  background-color: #222;
   padding: 0 20px 20px 20px;
 }
 
 .browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
@@ -38,16 +38,21 @@
   border: 1px solid hsla(210,8%,5%,.45);
   border-radius: 0;
   background: linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
   box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
   margin: 0 3px;
   color: inherit;
 }
 
+.devtools-responsiveui-toolbarbutton > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
 .devtools-responsiveui-toolbarbutton > .toolbarbutton-menubutton-button {
   -moz-box-orient: horizontal;
 }
 
 .devtools-responsiveui-menulist:-moz-focusring,
 .devtools-responsiveui-toolbarbutton:-moz-focusring {
   outline: 1px dotted hsla(210,30%,85%,0.7);
   outline-offset: -4px;
@@ -130,44 +135,88 @@
 .devtools-responsiveui-close {
   list-style-image: url("chrome://browser/skin/devtools/close.png");
 }
 
 .devtools-responsiveui-rotate {
   list-style-image: url("chrome://browser/skin/devtools/responsiveui-rotate.png");
 }
 
+@media (min-resolution: 2dppx) {
+  .devtools-responsiveui-close {
+    list-style-image: url("chrome://browser/skin/devtools/close@2x.png");
+  }
+
+  .devtools-responsiveui-rotate {
+    list-style-image: url("chrome://browser/skin/devtools/responsiveui-rotate@2x.png");
+  }
+}
+
 .devtools-responsiveui-touch {
   list-style-image: url("chrome://browser/skin/devtools/responsiveui-touch.png");
   -moz-image-region: rect(0px,16px,16px,0px);
 }
 
 .devtools-responsiveui-touch[checked] {
   -moz-image-region: rect(0px,32px,16px,16px);
 }
 
+@media (min-resolution: 2dppx) {
+  .devtools-responsiveui-touch {
+    list-style-image: url("chrome://browser/skin/devtools/responsiveui-touch@2x.png");
+    -moz-image-region: rect(0px,32px,32px,0px);
+  }
+
+  .devtools-responsiveui-touch[checked] {
+    -moz-image-region: rect(0px,64px,32px,32px);
+  }
+}
+
 .devtools-responsiveui-screenshot {
   list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot.png");
 }
 
+@media (min-resolution: 2dppx) {
+  .devtools-responsiveui-screenshot {
+    list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot@2x.png");
+  }
+}
+
 .devtools-responsiveui-resizebarV {
   width: 7px;
   height: 24px;
   cursor: ew-resize;
   transform: translate(12px, -12px);
+  background-size: cover;
   background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
 }
 
 .devtools-responsiveui-resizebarH {
   width: 24px;
   height: 7px;
   cursor: ns-resize;
   transform: translate(-12px, 12px);
+  background-size: cover;
   background-image: url("chrome://browser/skin/devtools/responsive-horizontal-resizer.png");
 }
 
 .devtools-responsiveui-resizehandle {
   width: 16px;
   height: 16px;
   cursor: se-resize;
   transform: translate(12px, 12px);
+  background-size: cover;
   background-image: url("chrome://browser/skin/devtools/responsive-se-resizer.png");
 }
+
+@media (min-resolution: 2dppx) {
+  .devtools-responsiveui-resizebarV {
+    background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer@2x.png");
+  }
+
+  .devtools-responsiveui-resizebarH {
+    background-image: url("chrome://browser/skin/devtools/responsive-horizontal-resizer@2x.png");
+  }
+
+  .devtools-responsiveui-resizehandle {
+    background-image: url("chrome://browser/skin/devtools/responsive-se-resizer@2x.png");
+  }
+}
\ No newline at end of file
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -7,17 +7,17 @@
 
 @font-face {
   font-family: "Clear Sans";
   src: url("chrome://browser/content/fonts/ClearSans-Regular.woff") format('woff');
 }
 
 page {
   -moz-appearance: none;
-  background-image: linear-gradient(#FFFFFF, #EDEDED 100px);
+  background-color: white;
 }
 
 * {
   -moz-user-select: text;
 }
 
 caption {
   -moz-appearance: none;
@@ -79,62 +79,49 @@ tabpanels {
   border: none;
   padding: 0;
   background-color: transparent;
 }
 
 tabs {
   -moz-margin-start: 60px;
   margin-bottom: 15px;
-  border-top: 2px solid;
-  border-bottom: 2px solid;
-  -moz-border-top-colors: #BBBBBB #F9F9F9;
-  -moz-border-bottom-colors: #F9F9F9 #BBBBBB;
+  border-top: 1px solid #c1c1c1;
+  border-bottom: 1px solid #c1c1c1;
+  background-color: #fbfbfb;
 }
 
 .tabs-left,
 .tabs-right {
   border-bottom: none;
 }
 
 tab {
   -moz-appearance: none;
   margin-top: 0;
-  padding: 0;
-  -moz-margin-end: 30px;
-  min-height: 60px;
-  background-color: transparent;
+  padding: 0 20px;
+  min-height: 44px;
+  color: #424f5a;
+  background-color: #fbfbfb;
   border-width: 0;
-  border-bottom: 3px solid transparent;
+  transition: background-color 50ms ease 0s;
+}
+
+tab:hover {
+  background-color: #ebebeb;
 }
 
 tab[selected] {
-  border-bottom-color: #FF9500;
+  background-color: #ebebeb;
+  box-shadow: inset 0 -4px 0 0 #ff9500;
 }
 
 .tab-text {
   font-size: 1.3rem;
   line-height: 22px;
-  color: #333333;
-  border: 1px solid transparent;
-  border-radius: 5px;
-}
-
-tab:not([selected]):hover > .tab-middle > .tab-text {
-  background-color: rgba(255,255,255,0.5);
-  border-color: #FFFFFF;
-}
-
-tab:not([selected]):hover:active > .tab-middle > .tab-text {
-  background-color: rgba(0,0,0,0.03);
-}
-
-tab[selected] > .tab-middle > .tab-text {
-  font-weight: bold;
-  color: #424E5A;
 }
 
 /* buttons and menulists */
 
 button,
 menulist {
   -moz-appearance: none;
   height: 30px;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -305,20 +305,28 @@ browser.jar:
         skin/classic/browser/devtools/debugger-step-over.png        (../shared/devtools/images/debugger-step-over.png)
         skin/classic/browser/devtools/debugger-step-over@2x.png     (../shared/devtools/images/debugger-step-over@2x.png)
         skin/classic/browser/devtools/debugger-blackbox.png         (../shared/devtools/images/debugger-blackbox.png)
         skin/classic/browser/devtools/debugger-blackbox@2x.png      (../shared/devtools/images/debugger-blackbox@2x.png)
         skin/classic/browser/devtools/debugger-toggleBreakpoints.png  (../shared/devtools/images/debugger-toggleBreakpoints.png)
         skin/classic/browser/devtools/debugger-toggleBreakpoints@2x.png  (../shared/devtools/images/debugger-toggleBreakpoints@2x.png)
         skin/classic/browser/devtools/tracer-icon.png               (../shared/devtools/images/tracer-icon.png)
         skin/classic/browser/devtools/tracer-icon@2x.png            (../shared/devtools/images/tracer-icon@2x.png)
-        skin/classic/browser/devtools/responsive-se-resizer.png     (../shared/devtools/images/responsive-se-resizer.png)
-        skin/classic/browser/devtools/responsive-vertical-resizer.png (../shared/devtools/images/responsive-vertical-resizer.png)
-        skin/classic/browser/devtools/responsive-horizontal-resizer.png (../shared/devtools/images/responsive-horizontal-resizer.png)
-        skin/classic/browser/devtools/responsive-background.png     (../shared/devtools/images/responsive-background.png)
+        skin/classic/browser/devtools/responsive-se-resizer.png     (../shared/devtools/images/responsivemode/responsive-se-resizer.png)
+        skin/classic/browser/devtools/responsive-se-resizer@2x.png       (../shared/devtools/images/responsivemode/responsive-se-resizer@2x.png)
+        skin/classic/browser/devtools/responsive-vertical-resizer.png    (../shared/devtools/images/responsivemode/responsive-vertical-resizer.png)
+        skin/classic/browser/devtools/responsive-vertical-resizer@2x.png (../shared/devtools/images/responsivemode/responsive-vertical-resizer@2x.png)
+        skin/classic/browser/devtools/responsive-horizontal-resizer.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer.png)
+        skin/classic/browser/devtools/responsive-horizontal-resizer@2x.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer@2x.png)
+        skin/classic/browser/devtools/responsiveui-rotate.png       (../shared/devtools/images/responsivemode/responsiveui-rotate.png)
+        skin/classic/browser/devtools/responsiveui-rotate@2x.png       (../shared/devtools/images/responsivemode/responsiveui-rotate@2x.png)
+        skin/classic/browser/devtools/responsiveui-touch.png        (../shared/devtools/images/responsivemode/responsiveui-touch.png)
+        skin/classic/browser/devtools/responsiveui-touch@2x.png        (../shared/devtools/images/responsivemode/responsiveui-touch@2x.png)
+        skin/classic/browser/devtools/responsiveui-screenshot.png   (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
+        skin/classic/browser/devtools/responsiveui-screenshot@2x.png   (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
         skin/classic/browser/devtools/toggle-tools.png              (../shared/devtools/images/toggle-tools.png)
         skin/classic/browser/devtools/dock-bottom@2x.png            (../shared/devtools/images/dock-bottom@2x.png)
         skin/classic/browser/devtools/dock-side@2x.png              (../shared/devtools/images/dock-side@2x.png)
         skin/classic/browser/devtools/floating-scrollbars.css       (devtools/floating-scrollbars.css)
         skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
 *       skin/classic/browser/devtools/inspector.css                 (../shared/devtools/inspector.css)
         skin/classic/browser/devtools/profiler-stopwatch.svg        (../shared/devtools/images/profiler-stopwatch.svg)
         skin/classic/browser/devtools/profiler-stopwatch-checked.svg  (../shared/devtools/images/profiler-stopwatch-checked.svg)
@@ -341,19 +349,16 @@ browser.jar:
         skin/classic/browser/devtools/vview-lock.png                (../shared/devtools/images/vview-lock.png)
         skin/classic/browser/devtools/vview-lock@2x.png             (../shared/devtools/images/vview-lock@2x.png)
         skin/classic/browser/devtools/vview-open-inspector.png      (../shared/devtools/images/vview-open-inspector.png)
         skin/classic/browser/devtools/vview-open-inspector@2x.png   (../shared/devtools/images/vview-open-inspector@2x.png)
         skin/classic/browser/devtools/undock@2x.png                 (../shared/devtools/images/undock@2x.png)
         skin/classic/browser/devtools/font-inspector.css            (../shared/devtools/font-inspector.css)
         skin/classic/browser/devtools/computedview.css              (../shared/devtools/computedview.css)
         skin/classic/browser/devtools/arrow-e.png                   (../shared/devtools/images/arrow-e.png)
-        skin/classic/browser/devtools/responsiveui-rotate.png       (../shared/devtools/responsiveui-rotate.png)
-        skin/classic/browser/devtools/responsiveui-touch.png        (../shared/devtools/responsiveui-touch.png)
-        skin/classic/browser/devtools/responsiveui-screenshot.png   (../shared/devtools/responsiveui-screenshot.png)
         skin/classic/browser/devtools/projecteditor/projecteditor.css           (../shared/devtools/projecteditor/projecteditor.css)
         skin/classic/browser/devtools/projecteditor/file-icons-sheet@2x.png       (../shared/devtools/projecteditor/file-icons-sheet@2x.png)
         skin/classic/browser/devtools/app-manager/connection-footer.css     (../shared/devtools/app-manager/connection-footer.css)
         skin/classic/browser/devtools/app-manager/index.css                 (../shared/devtools/app-manager/index.css)
         skin/classic/browser/devtools/app-manager/device.css                (../shared/devtools/app-manager/device.css)
         skin/classic/browser/devtools/app-manager/projects.css              (../shared/devtools/app-manager/projects.css)
         skin/classic/browser/devtools/app-manager/help.css                  (../shared/devtools/app-manager/help.css)
         skin/classic/browser/devtools/app-manager/warning.svg               (../shared/devtools/app-manager/images/warning.svg)
@@ -700,20 +705,28 @@ browser.jar:
         skin/classic/aero/browser/devtools/debugger-step-over.png    (../shared/devtools/images/debugger-step-over.png)
         skin/classic/aero/browser/devtools/debugger-step-over@2x.png  (../shared/devtools/images/debugger-step-over@2x.png)
         skin/classic/aero/browser/devtools/debugger-blackbox.png      (../shared/devtools/images/debugger-blackbox.png)
         skin/classic/aero/browser/devtools/debugger-blackbox@2x.png   (../shared/devtools/images/debugger-blackbox@2x.png)
         skin/classic/aero/browser/devtools/debugger-toggleBreakpoints.png (../shared/devtools/images/debugger-toggleBreakpoints.png)
         skin/classic/aero/browser/devtools/debugger-toggleBreakpoints@2x.png (../shared/devtools/images/debugger-toggleBreakpoints@2x.png)
         skin/classic/aero/browser/devtools/tracer-icon.png           (../shared/devtools/images/tracer-icon.png)
         skin/classic/aero/browser/devtools/tracer-icon@2x.png        (../shared/devtools/images/tracer-icon@2x.png)
-        skin/classic/aero/browser/devtools/responsive-se-resizer.png (../shared/devtools/images/responsive-se-resizer.png)
-        skin/classic/aero/browser/devtools/responsive-vertical-resizer.png (../shared/devtools/images/responsive-vertical-resizer.png)
-        skin/classic/aero/browser/devtools/responsive-horizontal-resizer.png (../shared/devtools/images/responsive-horizontal-resizer.png)
-        skin/classic/aero/browser/devtools/responsive-background.png (../shared/devtools/images/responsive-background.png)
+        skin/classic/aero/browser/devtools/responsive-se-resizer.png          (../shared/devtools/images/responsivemode/responsive-se-resizer.png)
+        skin/classic/aero/browser/devtools/responsive-se-resizer@2x.png       (../shared/devtools/images/responsivemode/responsive-se-resizer@2x.png)
+        skin/classic/aero/browser/devtools/responsive-vertical-resizer.png    (../shared/devtools/images/responsivemode/responsive-vertical-resizer.png)
+        skin/classic/aero/browser/devtools/responsive-vertical-resizer@2x.png (../shared/devtools/images/responsivemode/responsive-vertical-resizer@2x.png)
+        skin/classic/aero/browser/devtools/responsive-horizontal-resizer.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer.png)
+        skin/classic/aero/browser/devtools/responsive-horizontal-resizer@2x.png  (../shared/devtools/images/responsivemode/responsive-horizontal-resizer@2x.png)
+        skin/classic/aero/browser/devtools/responsiveui-rotate.png          (../shared/devtools/images/responsivemode/responsiveui-rotate.png)
+        skin/classic/aero/browser/devtools/responsiveui-rotate@2x.png       (../shared/devtools/images/responsivemode/responsiveui-rotate@2x.png)
+        skin/classic/aero/browser/devtools/responsiveui-touch.png           (../shared/devtools/images/responsivemode/responsiveui-touch.png)
+        skin/classic/aero/browser/devtools/responsiveui-touch@2x.png        (../shared/devtools/images/responsivemode/responsiveui-touch@2x.png)
+        skin/classic/aero/browser/devtools/responsiveui-screenshot.png      (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
+        skin/classic/aero/browser/devtools/responsiveui-screenshot@2x.png   (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
         skin/classic/aero/browser/devtools/toggle-tools.png          (../shared/devtools/images/toggle-tools.png)
         skin/classic/aero/browser/devtools/dock-bottom@2x.png        (../shared/devtools/images/dock-bottom@2x.png)
         skin/classic/aero/browser/devtools/dock-side@2x.png          (../shared/devtools/images/dock-side@2x.png)
         skin/classic/aero/browser/devtools/floating-scrollbars.css   (devtools/floating-scrollbars.css)
         skin/classic/aero/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
 *       skin/classic/aero/browser/devtools/inspector.css             (../shared/devtools/inspector.css)
         skin/classic/aero/browser/devtools/profiler-stopwatch.svg    (../shared/devtools/images/profiler-stopwatch.svg)
         skin/classic/aero/browser/devtools/profiler-stopwatch-checked.svg      (../shared/devtools/images/profiler-stopwatch-checked.svg)
@@ -736,19 +749,16 @@ browser.jar:
         skin/classic/aero/browser/devtools/vview-lock.png            (../shared/devtools/images/vview-lock.png)
         skin/classic/aero/browser/devtools/vview-lock@2x.png         (../shared/devtools/images/vview-lock@2x.png)
         skin/classic/aero/browser/devtools/vview-open-inspector.png  (../shared/devtools/images/vview-open-inspector.png)
         skin/classic/aero/browser/devtools/vview-open-inspector@2x.png (../shared/devtools/images/vview-open-inspector@2x.png)
         skin/classic/aero/browser/devtools/undock@2x.png             (../shared/devtools/images/undock@2x.png)
         skin/classic/aero/browser/devtools/font-inspector.css        (../shared/devtools/font-inspector.css)
         skin/classic/aero/browser/devtools/computedview.css          (../shared/devtools/computedview.css)
         skin/classic/aero/browser/devtools/arrow-e.png               (../shared/devtools/images/arrow-e.png)
-        skin/classic/aero/browser/devtools/responsiveui-rotate.png   (../shared/devtools/responsiveui-rotate.png)
-        skin/classic/aero/browser/devtools/responsiveui-touch.png    (../shared/devtools/responsiveui-touch.png)
-        skin/classic/aero/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
         skin/classic/aero/browser/devtools/projecteditor/projecteditor.css       (../shared/devtools/projecteditor/projecteditor.css)
         skin/classic/aero/browser/devtools/projecteditor/file-icons-sheet@2x.png       (../shared/devtools/projecteditor/file-icons-sheet@2x.png)
         skin/classic/aero/browser/devtools/app-manager/connection-footer.css     (../shared/devtools/app-manager/connection-footer.css)
         skin/classic/aero/browser/devtools/app-manager/index.css                 (../shared/devtools/app-manager/index.css)
         skin/classic/aero/browser/devtools/app-manager/device.css                (../shared/devtools/app-manager/device.css)
         skin/classic/aero/browser/devtools/app-manager/projects.css              (../shared/devtools/app-manager/projects.css)
         skin/classic/aero/browser/devtools/app-manager/help.css                  (../shared/devtools/app-manager/help.css)
         skin/classic/aero/browser/devtools/app-manager/warning.svg               (../shared/devtools/app-manager/images/warning.svg)
--- a/browser/themes/windows/preferences/in-content/preferences.css
+++ b/browser/themes/windows/preferences/in-content/preferences.css
@@ -1,14 +1,18 @@
 /* - 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 ../../../shared/incontentprefs/preferences.css
 
+caption {
+  background-color: transparent;
+}
+
 menulist:not([editable="true"]) > .menulist-dropmarker {
   margin-top: 1px;
   margin-bottom: 1px;
 }
 
 checkbox {
   -moz-padding-start: 0;
 }
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -1034,17 +1034,17 @@ nsScriptSecurityManager::CanCreateWrappe
 // XXX Special case for nsIXPCException ?
     ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
     if (objClassInfo.IsDOMClass())
     {
         return NS_OK;
     }
 
     // We give remote-XUL whitelisted domains a free pass here. See bug 932906.
-    if (!xpc::AllowXBLScope(js::GetContextCompartment(cx)))
+    if (!xpc::AllowContentXBLScope(js::GetContextCompartment(cx)))
     {
         return NS_OK;
     }
 
     if (nsContentUtils::IsCallerChrome())
     {
         return NS_OK;
     }
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -183,17 +183,17 @@ public:
 
   /**
    * Get a JSContext from the document's scope object.
    */
   static JSContext* GetContextFromDocument(nsIDocument *aDocument);
 
   static bool     IsCallerChrome();
   static bool     ThreadsafeIsCallerChrome();
-  static bool     IsCallerXBL();
+  static bool     IsCallerContentXBL();
 
   static bool     IsImageSrcSetDisabled();
 
   static bool LookupBindingMember(JSContext* aCx, nsIContent *aContent,
                                   JS::Handle<jsid> aId,
                                   JS::MutableHandle<JSPropertyDescriptor> aDesc);
 
   /**
--- a/content/base/src/nsCSPParser.cpp
+++ b/content/base/src/nsCSPParser.cpp
@@ -691,17 +691,18 @@ nsCSPParser::sourceList(nsTArray<nsCSPBa
   if (isNone) {
     // If the directive contains no other srcs, then we set the 'none'
     if (outSrcs.Length() == 0) {
       nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
       outSrcs.AppendElement(keyword);
     }
     // Otherwise, we ignore 'none' and report a warning
     else {
-      const char16_t* params[] = { NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONE)).get() };
+      NS_ConvertUTF8toUTF16 unicodeNone(CSP_EnumToKeyword(CSP_NONE));
+      const char16_t* params[] = { unicodeNone.get() };
       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
                                params, ArrayLength(params));
     }
   }
 }
 
 void
 nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
@@ -882,17 +883,18 @@ nsCSPParser::parseContentSecurityPolicy(
 
   // Check that report-only policies define a report-uri, otherwise log warning.
   if (aReportOnly) {
     policy->setReportOnlyFlag(true);
     if (!policy->directiveExists(CSP_REPORT_URI)) {
       nsAutoCString prePath;
       nsresult rv = aSelfURI->GetPrePath(prePath);
       NS_ENSURE_SUCCESS(rv, policy);
-      const char16_t* params[] = { NS_ConvertUTF8toUTF16(prePath).get() };
+      NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
+      const char16_t* params[] = { unicodePrePath.get() };
       parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
                                       params, ArrayLength(params));
     }
   }
 
   if (policy->getNumDirectives() == 0) {
     // Individual errors were already reported in the parser, but if
     // we do not have an enforcable directive at all, we return null.
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -362,17 +362,18 @@ CSPService::AsyncOnChannelRedirect(nsICh
       return NS_OK;
     }
   }
 
   // The redirecting channel isn't a writable property bag, we won't be able
   // to enforce the load policy if it redirects again, so we stop it now.
   nsAutoCString newUriSpec;
   rv = newUri->GetSpec(newUriSpec);
-  const char16_t *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
+  NS_ConvertUTF8toUTF16 unicodeSpec(newUriSpec);
+  const char16_t *formatParams[] = { unicodeSpec.get() };
   if (NS_SUCCEEDED(rv)) {
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Redirect Error"), nullptr,
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "InvalidRedirectChannelWarning",
                                     formatParams, 1);
   }
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1709,32 +1709,32 @@ bool
 nsContentUtils::ThreadsafeIsCallerChrome()
 {
   return NS_IsMainThread() ?
     IsCallerChrome() :
     mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
 }
 
 bool
-nsContentUtils::IsCallerXBL()
+nsContentUtils::IsCallerContentXBL()
 {
     JSContext *cx = GetCurrentJSContext();
     if (!cx)
         return false;
 
     JSCompartment *c = js::GetContextCompartment(cx);
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, just always claim to be XBL.
-    if (!xpc::AllowXBLScope(c)) {
+    if (!xpc::AllowContentXBLScope(c)) {
       MOZ_ASSERT(nsContentUtils::AllowXULXBLForPrincipal(xpc::GetCompartmentPrincipal(c)));
       return true;
     }
 
-    return xpc::IsXBLScope(c);
+    return xpc::IsContentXBLScope(c);
 }
 
 
 bool
 nsContentUtils::IsImageSrcSetDisabled()
 {
   return Preferences::GetBool("dom.disable_image_src_set") &&
          !IsCallerChrome();
@@ -5580,17 +5580,17 @@ nsContentTypeParser::GetParameter(const 
                                     aResult);
 }
 
 /* static */
 
 bool
 nsContentUtils::CanAccessNativeAnon()
 {
-  return IsCallerChrome() || IsCallerXBL();
+  return IsCallerChrome() || IsCallerContentXBL();
 }
 
 /* static */ nsresult
 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
                                    bool aTrusted,
                                    nsIDOMEvent* aSourceEvent,
                                    nsIPresShell* aShell,
                                    bool aCtrl,
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -2655,17 +2655,17 @@ nsINode::WrapObject(JSContext *aCx)
       !hasHadScriptHandlingObject &&
       !nsContentUtils::IsCallerChrome()) {
     Throw(aCx, NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx));
   MOZ_ASSERT_IF(ChromeOnlyAccess(),
-                xpc::IsInXBLScope(obj) || !xpc::UseXBLScope(js::GetObjectCompartment(obj)));
+                xpc::IsInContentXBLScope(obj) || !xpc::UseContentXBLScope(js::GetObjectCompartment(obj)));
   return obj;
 }
 
 already_AddRefed<nsINode>
 nsINode::CloneNode(bool aDeep, ErrorResult& aError)
 {
   bool callUserDataHandlers = NodeType() != nsIDOMNode::DOCUMENT_NODE ||
                               !static_cast<nsIDocument*>(this)->CreatingStaticClone();
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -2645,17 +2645,17 @@ nsObjectLoadingContent::ScriptRequestPlu
   // The below methods pull the cx off the stack, so make sure they match.
   //
   // NB: Sometimes there's a null cx on the stack, in which case |cx| is the
   // safe JS context. But in that case, IsCallerChrome() will return true,
   // so the ensuing expression is short-circuited.
   MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContext(),
                 aCx == nsContentUtils::GetCurrentJSContext());
   bool callerIsContentJS = (!nsContentUtils::IsCallerChrome() &&
-                            !nsContentUtils::IsCallerXBL() &&
+                            !nsContentUtils::IsCallerContentXBL() &&
                             js::IsContextRunningJS(aCx));
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   *aResult = nullptr;
 
   // The first time content script attempts to access placeholder content, fire
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -78,28 +78,31 @@ WebGLMemoryPressureObserver::Observe(nsI
 {
     if (strcmp(aTopic, "memory-pressure"))
         return NS_OK;
 
     bool wantToLoseContext = true;
 
     if (!mContext->mCanLoseContextInForeground &&
         ProcessPriorityManager::CurrentProcessIsForeground())
+    {
         wantToLoseContext = false;
-    else if (!nsCRT::strcmp(aSomeData,
-                            MOZ_UTF16("heap-minimize")))
+    } else if (!nsCRT::strcmp(aSomeData,
+                              MOZ_UTF16("heap-minimize")))
+    {
         wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
+    }
 
-    if (wantToLoseContext)
+    if (wantToLoseContext) {
         mContext->ForceLoseContext();
+    }
 
     return NS_OK;
 }
 
-
 WebGLContextOptions::WebGLContextOptions()
     : alpha(true), depth(true), stencil(false),
       premultipliedAlpha(true), antialias(true),
       preserveDrawingBuffer(false)
 {
     // Set default alpha state based on preference.
     if (Preferences::GetBool("webgl.default-no-alpha", false))
         alpha = false;
@@ -163,22 +166,22 @@ WebGLContext::WebGLContext()
     mGLMaxTransformFeedbackSeparateAttribs = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
     WebGLMemoryTracker::AddWebGLContext(this);
 
-    mAllowRestore = true;
+    mAllowContextRestore = true;
+    mLastLossWasSimulated = false;
     mContextLossTimerRunning = false;
-    mDrawSinceContextLossTimerSet = false;
+    mRunContextLossTimerAgain = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextNotLost;
-    mContextLostErrorSet = false;
     mLoseContextOnHeapMinimize = false;
     mCanLoseContextInForeground = true;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
     mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
     if (mMaxWarnings < -1)
@@ -566,21 +569,18 @@ WebGLContext::SetDimensions(int32_t widt
 
     mWidth = width;
     mHeight = height;
     mViewportWidth = width;
     mViewportHeight = height;
     mResetLayer = true;
     mOptionsFrozen = true;
 
-    mHasRobustness = gl->HasRobustness();
-
     // increment the generation number
     ++mGeneration;
-
 #if 0
     if (mGeneration > 0) {
         // XXX dispatch context lost event
     }
 #endif
 
     MakeContextCurrent();
 
@@ -1110,162 +1110,239 @@ WebGLContext::PresentScreenBuffer()
 void
 WebGLContext::DummyFramebufferOperation(const char *info)
 {
     GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
         ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
 }
 
+static bool
+CheckContextLost(GLContext* gl, bool* out_isGuilty)
+{
+    MOZ_ASSERT(gl);
+    MOZ_ASSERT(out_isGuilty);
+
+    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
+
+    GLenum resetStatus = LOCAL_GL_NO_ERROR;
+    if (gl->HasRobustness()) {
+        gl->MakeCurrent();
+        resetStatus = gl->fGetGraphicsResetStatus();
+    } else if (isEGL) {
+        // Simulate a ARB_robustness guilty context loss for when we
+        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
+        // but we can't make any distinction.
+        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
+            resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+        }
+    }
+
+    if (resetStatus == LOCAL_GL_NO_ERROR) {
+        *out_isGuilty = false;
+        return false;
+    }
+
+    // Assume guilty unless we find otherwise!
+    bool isGuilty = true;
+    switch (resetStatus) {
+    case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+        // Either nothing wrong, or not our fault.
+        isGuilty = false;
+        break;
+    case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
+        NS_WARNING("WebGL content on the page definitely caused the graphics"
+                   " card to reset.");
+        break;
+    case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
+        NS_WARNING("WebGL content on the page might have caused the graphics"
+                   " card to reset");
+        // If we can't tell, assume guilty.
+        break;
+    default:
+        MOZ_ASSERT(false, "Unreachable.");
+        // If we do get here, let's pretend to be guilty as an escape plan.
+        break;
+    }
+
+    if (isGuilty) {
+        NS_WARNING("WebGL context on this page is considered guilty, and will"
+                   " not be restored.");
+    }
+
+    *out_isGuilty = isGuilty;
+    return true;
+}
+
+bool
+WebGLContext::TryToRestoreContext()
+{
+    if (NS_FAILED(SetDimensions(mWidth, mHeight)))
+        return false;
+
+    return true;
+}
+
+class UpdateContextLossStatusTask : public nsRunnable
+{
+    nsRefPtr<WebGLContext> mContext;
+
+public:
+    UpdateContextLossStatusTask(WebGLContext* context)
+        : mContext(context)
+    {
+    }
+
+    NS_IMETHOD Run() {
+        mContext->UpdateContextLossStatus();
+
+        return NS_OK;
+    }
+};
+
+void
+WebGLContext::EnqueueUpdateContextLossStatus()
+{
+    nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
+    NS_DispatchToCurrentThread(task);
+}
+
 // We use this timer for many things. Here are the things that it is activated for:
 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
 //    CONTEXT_LOST_WEBGL error has been triggered.
 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
 //    GPU periodically to see if the reset status bit has been set.
 // In all of these situations, we use this timer to send the script context lost
 // and restored events asynchronously. For example, if it triggers a context loss,
 // the webglcontextlost event will be sent to it the next time the robustness timer
 // fires.
 // Note that this timer mechanism is not used unless one of these 3 criteria
 // are met.
 // At a bare minimum, from context lost to context restores, it would take 3
 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
 void
-WebGLContext::RobustnessTimerCallback(nsITimer* timer)
+WebGLContext::UpdateContextLossStatus()
 {
-    TerminateContextLossTimer();
-
     if (!mCanvasElement) {
         // the canvas is gone. That happens when the page was closed before we got
         // this timer event. In this case, there's nothing to do here, just don't crash.
         return;
     }
+    if (mContextStatus == ContextNotLost) {
+        // We don't know that we're lost, but we might be, so we need to
+        // check. If we're guilty, don't allow restores, though.
 
-    // If the context has been lost and we're waiting for it to be restored, do
-    // that now.
+        bool isGuilty = true;
+        MOZ_ASSERT(gl); // Shouldn't be missing gl if we're NotLost.
+        bool isContextLost = CheckContextLost(gl, &isGuilty);
+
+        if (isContextLost) {
+            if (isGuilty)
+                mAllowContextRestore = false;
+
+            ForceLoseContext();
+        }
+
+        // Fall through.
+    }
+
     if (mContextStatus == ContextLostAwaitingEvent) {
-        bool defaultAction;
+        // The context has been lost and we haven't yet triggered the
+        // callback, so do that now.
+
+        bool useDefaultHandler;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextlost"),
                                              true,
                                              true,
-                                             &defaultAction);
+                                             &useDefaultHandler);
+        // We sent the callback, so we're just 'regular lost' now.
+        mContextStatus = ContextLost;
+        // If we're told to use the default handler, it means the script
+        // didn't bother to handle the event. In this case, we shouldn't
+        // auto-restore the context.
+        if (useDefaultHandler)
+            mAllowContextRestore = false;
 
-        // If the script didn't handle the event, we don't allow restores.
-        if (defaultAction)
-            mAllowRestore = false;
+        // Fall through.
+    }
+
+    if (mContextStatus == ContextLost) {
+        // Context is lost, and we've already sent the callback. We
+        // should try to restore the context if we're both allowed to,
+        // and supposed to.
 
-        // If the script handled the event and we are allowing restores, then
-        // mark it to be restored. Otherwise, leave it as context lost
-        // (unusable).
-        if (!defaultAction && mAllowRestore) {
-            ForceRestoreContext();
-            // Restart the timer so that it will be restored on the next
-            // callback.
-            SetupContextLossTimer();
-        } else {
+        // Are we allowed to restore the context?
+        if (!mAllowContextRestore)
+            return;
+
+        // If we're only simulated-lost, we shouldn't auto-restore, and
+        // instead we should wait for restoreContext() to be called.
+        if (mLastLossWasSimulated)
+            return;
+
+        ForceRestoreContext();
+        return;
+    }
+
+    if (mContextStatus == ContextLostAwaitingRestore) {
+        // Context is lost, but we should try to restore it.
+
+        if (!mAllowContextRestore) {
+            // We might decide this after thinking we'd be OK restoring
+            // the context, so downgrade.
             mContextStatus = ContextLost;
-        }
-    } else if (mContextStatus == ContextLostAwaitingRestore) {
-        // Try to restore the context. If it fails, try again later.
-        if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
-            SetupContextLossTimer();
             return;
         }
+
+        if (!TryToRestoreContext()) {
+            // Failed to restore. Try again later.
+            RunContextLossTimer();
+            return;
+        }
+
+        // Revival!
         mContextStatus = ContextNotLost;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextrestored"),
                                              true,
                                              true);
-        // Set all flags back to the state they were in before the context was
-        // lost.
         mEmitContextLostErrorOnce = true;
-        mAllowRestore = true;
-    }
-
-    MaybeRestoreContext();
-    return;
-}
-
-void
-WebGLContext::MaybeRestoreContext()
-{
-    // Don't try to handle it if we already know it's busted.
-    if (mContextStatus != ContextNotLost || gl == nullptr)
         return;
-
-    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
-         isANGLE = gl->IsANGLE();
-
-    GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
-    if (mHasRobustness) {
-        gl->MakeCurrent();
-        resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
-    } else if (isEGL) {
-        // Simulate a ARB_robustness guilty context loss for when we
-        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
-        // but we can't make any distinction, so we must assume the worst
-        // case.
-        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
-            resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
-        }
-    }
-
-    if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
-        // It's already lost, but clean up after it and signal to JS that it is
-        // lost.
-        ForceLoseContext();
-    }
-
-    switch (resetStatus) {
-        case GLContext::CONTEXT_NO_ERROR:
-            // If there has been activity since the timer was set, it's possible
-            // that we did or are going to miss something, so clear this flag and
-            // run it again some time later.
-            if (mDrawSinceContextLossTimerSet)
-                SetupContextLossTimer();
-            break;
-        case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
-            NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
-            mAllowRestore = false;
-            break;
-        case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
-            break;
-        case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
-            NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
-            if (isEGL && isANGLE) {
-                // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
-                // This means that we can't restore it or risk restoring a guilty context. Should this ever change,
-                // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
-                // only use for it. See ANGLE issue 261.
-                mAllowRestore = false;
-            }
-            break;
     }
 }
 
 void
 WebGLContext::ForceLoseContext()
 {
-    if (mContextStatus == ContextLostAwaitingEvent)
-        return;
-
+    printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
+    MOZ_ASSERT(!IsContextLost());
     mContextStatus = ContextLostAwaitingEvent;
-    // Queue up a task to restore the event.
-    SetupContextLossTimer();
+    mContextLostErrorSet = false;
+    mLastLossWasSimulated = false;
+
+    // Burn it all!
     DestroyResourcesAndContext();
+
+    // Queue up a task, since we know the status changed.
+    EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::ForceRestoreContext()
 {
+    printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
     mContextStatus = ContextLostAwaitingRestore;
+    mAllowContextRestore = true; // Hey, you did say 'force'.
+
+    // Queue up a task, since we know the status changed.
+    EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
 
 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
 {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -197,19 +197,16 @@ public:
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack,
                     int32_t x, int32_t y, int32_t w, int32_t h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(uint32_t nativeID,
                     int32_t x, int32_t y, int32_t w, int32_t h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
 
-    bool LoseContext();
-    bool RestoreContext();
-
     void SynthesizeGLError(GLenum err);
     void SynthesizeGLError(GLenum err, const char *fmt, ...);
 
     void ErrorInvalidEnum(const char *fmt = 0, ...);
     void ErrorInvalidOperation(const char *fmt = 0, ...);
     void ErrorInvalidValue(const char *fmt = 0, ...);
     void ErrorInvalidFramebufferOperation(const char *fmt = 0, ...);
     void ErrorInvalidEnumInfo(const char *info, GLenum enumvalue);
@@ -256,21 +253,24 @@ public:
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[kMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
     void ClearBackbufferIfNeeded();
 
     bool MinCapabilityMode() const { return mMinCapability; }
 
-    void RobustnessTimerCallback(nsITimer* timer);
-    static void RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer);
-    void SetupContextLossTimer();
+    void UpdateContextLossStatus();
+    void EnqueueUpdateContextLossStatus();
+    static void ContextLossCallbackStatic(nsITimer* timer, void* thisPointer);
+    void RunContextLossTimer();
     void TerminateContextLossTimer();
 
+    bool TryToRestoreContext();
+
     void AssertCachedBindings();
     void AssertCachedState();
 
     // WebIDL WebGLRenderingContext API
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
     GLsizei DrawingBufferHeight() const { return IsContextLost() ? 0 : mHeight; }
 
@@ -661,18 +661,22 @@ public:
                                           GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
                                           WebGLboolean aTranspose);
     bool ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location);
     void ValidateProgram(WebGLProgram *prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object);
     bool ValidateSamplerUniformSetter(const char* info,
                                     WebGLUniformLocation *location,
                                     GLint value);
-
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
+// -----------------------------------------------------------------------------
+// WEBGL_lose_context
+public:
+    void LoseContext();
+    void RestoreContext();
 
 // -----------------------------------------------------------------------------
 // Asynchronous Queries (WebGLContextAsyncQueries.cpp)
 public:
     already_AddRefed<WebGLQuery> CreateQuery();
     void DeleteQuery(WebGLQuery *query);
     void BeginQuery(GLenum target, WebGLQuery *query);
     void EndQuery(GLenum target);
@@ -864,17 +868,16 @@ protected:
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mMinCapability;
     bool mDisableExtensions;
-    bool mHasRobustness;
     bool mIsMesa;
     bool mLoseContextOnHeapMinimize;
     bool mCanLoseContextInForeground;
     bool mShouldPresent;
     bool mBackbufferNeedsClear;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
@@ -1111,17 +1114,16 @@ protected:
                              GLenum internalFormat,
                              GLsizei width,
                              GLsizei height,
                              GLint border,
                              GLenum format,
                              GLenum type,
                              const GLvoid *data);
 
-    void MaybeRestoreContext();
     void ForceLoseContext();
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
 
@@ -1173,33 +1175,34 @@ protected:
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
 
     GLint mStencilRefFront, mStencilRefBack;
     GLuint mStencilValueMaskFront, mStencilValueMaskBack,
-              mStencilWriteMaskFront, mStencilWriteMaskBack;
+           mStencilWriteMaskFront, mStencilWriteMaskBack;
     realGLboolean mColorWriteMask[4];
     realGLboolean mDepthWriteMask;
     GLfloat mColorClearValue[4];
     GLint mStencilClearValue;
     GLfloat mDepthClearValue;
 
     GLint mViewportX;
     GLint mViewportY;
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
     nsCOMPtr<nsITimer> mContextRestorer;
-    bool mAllowRestore;
+    bool mAllowContextRestore;
+    bool mLastLossWasSimulated;
     bool mContextLossTimerRunning;
-    bool mDrawSinceContextLossTimerSet;
+    bool mRunContextLossTimerAgain;
     ContextStatus mContextStatus;
     bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
     int mAlreadyGeneratedWarnings;
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -123,17 +123,17 @@ WebGLContext::DrawArrays(GLenum mode, GL
         return;
 
     if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
         return;
 
     if (!DrawArrays_check(first, count, 1, "drawArrays"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawArrays(mode, first, count);
 
     Draw_cleanup();
 }
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 {
@@ -144,17 +144,17 @@ WebGLContext::DrawArraysInstanced(GLenum
         return;
 
     if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
         return;
 
     if (!DrawInstanced_check("drawArraysInstanced"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawArraysInstanced(mode, first, count, primcount);
 
     Draw_cleanup();
 }
 
 bool
 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
                                  WebGLintptr byteOffset, GLsizei primcount,
@@ -291,17 +291,17 @@ WebGLContext::DrawElements(GLenum mode, 
 
     GLuint upperBound = UINT_MAX;
     if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
                             &upperBound))
     {
         return;
     }
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
 
     if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
         gl->fDrawRangeElements(mode, 0, upperBound,
                                count, type, reinterpret_cast<GLvoid*>(byteOffset));
     } else {
         gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
     }
 
@@ -319,17 +319,17 @@ WebGLContext::DrawElementsInstanced(GLen
         return;
 
     if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
         return;
 
     if (!DrawInstanced_check("drawElementsInstanced"))
         return;
 
-    SetupContextLossTimer();
+    RunContextLossTimer();
     gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
 
     Draw_cleanup();
 }
 
 void WebGLContext::Draw_cleanup()
 {
     UndoFakeVertexAttrib0();
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3852,37 +3852,44 @@ WebGLContext::TexSubImage2D(GLenum targe
     return TexSubImage2D_base(target, level, xoffset, yoffset,
                               pixels->Width(), pixels->Height(),
                               4*pixels->Width(), format, type,
                               arr.Data(), arr.Length(),
                               -1,
                               WebGLTexelFormat::RGBA8, false);
 }
 
-bool
+void
 WebGLContext::LoseContext()
 {
     if (IsContextLost())
-        return false;
+        return ErrorInvalidOperation("loseContext: Context is already lost.");
 
     ForceLoseContext();
-
-    return true;
+    mLastLossWasSimulated = true;
 }
 
-bool
+void
 WebGLContext::RestoreContext()
 {
-    if (!IsContextLost() || !mAllowRestore) {
-        return false;
+    if (!IsContextLost())
+        return ErrorInvalidOperation("restoreContext: Context is not lost.");
+
+    if (!mLastLossWasSimulated) {
+        return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
+                                     " Cannot simulate restore.");
     }
+    // If we're currently lost, and the last loss was simulated, then
+    // we're currently only simulated-lost, allowing us to call
+    // restoreContext().
+
+    if (!mAllowContextRestore)
+        return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
 
     ForceRestoreContext();
-
-    return true;
 }
 
 bool
 BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize)
 {
     switch (uType) {
         case LOCAL_GL_INT:
         case LOCAL_GL_INT_VEC2:
--- a/content/canvas/src/WebGLContextLossTimer.cpp
+++ b/content/canvas/src/WebGLContextLossTimer.cpp
@@ -3,38 +3,50 @@
  * 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 "WebGLContext.h"
 
 using namespace mozilla;
 
 /* static */ void
-WebGLContext::RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer) {
-    static_cast<WebGLContext*>(thisPointer)->RobustnessTimerCallback(timer);
+WebGLContext::ContextLossCallbackStatic(nsITimer* timer, void* thisPointer)
+{
+    (void)timer;
+    WebGLContext* context = static_cast<WebGLContext*>(thisPointer);
+
+    context->TerminateContextLossTimer();
+
+    context->UpdateContextLossStatus();
 }
 
 void
-WebGLContext::SetupContextLossTimer() {
+WebGLContext::RunContextLossTimer()
+{
     // If the timer was already running, don't restart it here. Instead,
     // wait until the previous call is done, then fire it one more time.
-    // This is an optimization to prevent unnecessary cross-communication
-    // between threads.
+    // This is an optimization to prevent unnecessary
+    // cross-communication between threads.
     if (mContextLossTimerRunning) {
-        mDrawSinceContextLossTimerSet = true;
+        mRunContextLossTimerAgain = true;
         return;
     }
-
-    mContextRestorer->InitWithFuncCallback(RobustnessTimerCallbackStatic,
-                                            static_cast<void*>(this),
-                                            1000,
-                                            nsITimer::TYPE_ONE_SHOT);
+    mContextRestorer->InitWithFuncCallback(ContextLossCallbackStatic,
+                                           static_cast<void*>(this),
+                                           1000,
+                                           nsITimer::TYPE_ONE_SHOT);
     mContextLossTimerRunning = true;
-    mDrawSinceContextLossTimerSet = false;
+    mRunContextLossTimerAgain = false;
 }
 
 void
-WebGLContext::TerminateContextLossTimer() {
-    if (mContextLossTimerRunning) {
-        mContextRestorer->Cancel();
-        mContextLossTimerRunning = false;
+WebGLContext::TerminateContextLossTimer()
+{
+    if (!mContextLossTimerRunning)
+        return;
+
+    mContextRestorer->Cancel();
+    mContextLossTimerRunning = false;
+
+    if (mRunContextLossTimerAgain) {
+        RunContextLossTimer();
     }
 }
--- a/content/canvas/src/WebGLExtensionLoseContext.cpp
+++ b/content/canvas/src/WebGLExtensionLoseContext.cpp
@@ -16,20 +16,18 @@ WebGLExtensionLoseContext::WebGLExtensio
 
 WebGLExtensionLoseContext::~WebGLExtensionLoseContext()
 {
 }
 
 void
 WebGLExtensionLoseContext::LoseContext()
 {
-    if (!mContext->LoseContext())
-        mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
+    mContext->LoseContext();
 }
 
-void 
+void
 WebGLExtensionLoseContext::RestoreContext()
 {
-    if (!mContext->RestoreContext())
-        mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
+    mContext->RestoreContext();
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionLoseContext)
--- a/content/canvas/test/webgl-conformance/conformance/context/context-lost-restored.html
+++ b/content/canvas/test/webgl-conformance/conformance/context/context-lost-restored.html
@@ -94,17 +94,17 @@ function testLosingAndRestoringContext()
     debug("");
     debug("Test losing and restoring a context.");
 
     canvas.addEventListener("webglcontextlost", function(e) {
       testLostContext(e);
       // restore the context after this event has exited.
       setTimeout(function() {
         shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
-        // The context should still be lost. It will not get restored until the 
+        // The context should still be lost. It will not get restored until the
         // webglrestorecontext event is fired.
         shouldBeTrue("gl.isContextLost()");
         shouldBe("gl.getError()", "gl.NO_ERROR");
         // gl methods should still be no-ops
         shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)");
       }, 0);
     });
     canvas.addEventListener("webglcontextrestored", function() {
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3059,16 +3059,24 @@ void HTMLMediaElement::UpdateReadyStateF
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
+  // Section 2.4.3.1 of the Media Source Extensions spec requires
+  // changing to HAVE_METADATA when seeking into an unbuffered
+  // range.
+  if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA) {
+    ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
+    return;
+  }
+
   if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
       mDownloadSuspendedByCache &&
       mDecoder &&
       !mDecoder->IsEnded()) {
     // The decoder has signalled that the download has been suspended by the
     // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
     // script waiting for a "canplaythrough" event; without this forced
     // transition, we will never fire the "canplaythrough" event if the
--- a/content/media/MediaDecoderOwner.h
+++ b/content/media/MediaDecoderOwner.h
@@ -114,16 +114,19 @@ public:
   enum NextFrameStatus {
     // The next frame of audio/video is available
     NEXT_FRAME_AVAILABLE,
     // The next frame of audio/video is unavailable because the decoder
     // is paused while it buffers up data
     NEXT_FRAME_UNAVAILABLE_BUFFERING,
     // The next frame of audio/video is unavailable for some other reasons
     NEXT_FRAME_UNAVAILABLE,
+    // The next frame is unavailable due to waiting for more Media Source
+    // Extensions data to become available.
+    NEXT_FRAME_WAIT_FOR_MSE_DATA,
     // Sentinel value
     NEXT_FRAME_UNINITIALIZED
   };
 
   // Called by the decoder when some data has been downloaded or
   // buffering/seeking has ended. aNextFrameAvailable is true when
   // the data for the next frame is available. This method will
   // decide whether to set the ready state to HAVE_CURRENT_DATA,
--- a/content/media/gtest/TestWebMWriter.cpp
+++ b/content/media/gtest/TestWebMWriter.cpp
@@ -147,17 +147,16 @@ TEST(WebMWriter, Cluster)
   writer.SetVP8Metadata(width, height, displayWidth,
                         displayHeight, aTrackRate);
 
   nsTArray<nsTArray<uint8_t> > encodedBuf;
   writer.GetContainerData(&encodedBuf, ContainerWriter::GET_HEADER);
   EXPECT_TRUE(encodedBuf.Length() > 0);
   encodedBuf.Clear();
 
-  uint64_t timestamp = 0;
   // write the first I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
   // No data because the cluster is not closed.
   EXPECT_FALSE(writer.HaveValidCluster());
 
   // The second I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
   // Should have data because the first cluster is closed.
@@ -186,17 +185,16 @@ TEST(WebMWriter, FLUSH_NEEDED)
   int32_t width = 176;
   int32_t height = 352;
   int32_t displayWidth = 176;
   int32_t displayHeight = 352;
   TrackRate aTrackRate = 100000;
   writer.SetVP8Metadata(width, height, displayWidth,
                         displayHeight, aTrackRate);
 
-  uint64_t timestamp = 0;
   // write the first I-Frame.
   writer.AppendDummyFrame(EncodedFrame::VP8_I_FRAME, FIXED_DURATION);
 
   // P-Frame
   writer.AppendDummyFrame(EncodedFrame::VP8_P_FRAME, FIXED_DURATION);
   // Have data because the metadata is finished.
   EXPECT_TRUE(writer.HaveValidCluster());
   // No data because the cluster is not closed and the metatdata had been
--- a/content/media/gtest/moz.build
+++ b/content/media/gtest/moz.build
@@ -21,8 +21,10 @@ EXPORT_LIBRARY = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/content/media/encoder',
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
+
+FAIL_ON_WARNINGS = True
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -294,16 +294,17 @@ MediaSource::Detach()
   SetReadyState(MediaSourceReadyState::Closed);
 }
 
 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
   , mDuration(UnspecifiedNaN<double>())
   , mDecoder(nullptr)
   , mReadyState(MediaSourceReadyState::Closed)
+  , mWaitForDataMonitor("MediaSource.WaitForData.Monitor")
 {
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
 
 #ifdef PR_LOGGING
   if (!gMediaSourceLog) {
     gMediaSourceLog = PR_NewLogModule("MediaSource");
   }
@@ -390,16 +391,30 @@ MediaSource::WrapObject(JSContext* aCx)
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   // Cycle through all SourceBuffers and tell them to evict data in
   // the given range.
   mSourceBuffers->Evict(aStart, aEnd);
 }
 
+void
+MediaSource::WaitForData()
+{
+  MonitorAutoLock lock(mWaitForDataMonitor);
+  lock.Wait();
+}
+
+void
+MediaSource::NotifyGotData()
+{
+  MonitorAutoLock lock(mWaitForDataMonitor);
+  lock.NotifyAll();
+}
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
                                    mSourceBuffers, mActiveSourceBuffers)
 
 NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaSource, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_MediaSource_h_
 
 #include "MediaSourceDecoder.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaSourceBinding.h"
+#include "mozilla/Monitor.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsID.h"
 #include "nsISupports.h"
 #include "nscore.h"
 
@@ -84,16 +85,22 @@ public:
     return mDecoder;
   }
 
   // Called by SourceBuffers to notify this MediaSource that data has
   // been evicted from the buffered data. The start and end times
   // that were evicted are provided.
   void NotifyEvicted(double aStart, double aEnd);
 
+  // Block thread waiting for data to be appended to a SourceBuffer.
+  void WaitForData();
+
+  // Unblock threads waiting for data to be appended to a SourceBuffer.
+  void NotifyGotData();
+
 private:
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   void DurationChange(double aNewDuration, ErrorResult& aRv);
@@ -101,16 +108,20 @@ private:
   double mDuration;
 
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
   nsRefPtr<MediaSourceDecoder> mDecoder;
 
   MediaSourceReadyState mReadyState;
+
+  // Monitor for waiting for when new data is appended to
+  // a Source Buffer.
+  Monitor mWaitForDataMonitor;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
 
 } // namespace mozilla
 #endif /* mozilla_dom_MediaSource_h_ */
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -125,16 +125,21 @@ public:
   nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                 int64_t aCurrentTime) MOZ_OVERRIDE;
   nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
   already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
                                                       MediaSourceDecoder* aParentDecoder);
 
   void InitializePendingDecoders();
 
+  bool IsShutdown() {
+    ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
+    return mDecoder->IsShutdown();
+  }
+
 private:
   bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     MOZ_ASSERT(mActiveVideoDecoder != -1);
 
     InitializePendingDecoders();
 
     for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
@@ -378,35 +383,70 @@ MediaSourceReader::CreateSubDecoder(cons
   if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
     MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
     return nullptr;
   }
   mDecoder->NotifyWaitingForResourcesStatusChanged();
   return decoder.forget();
 }
 
+namespace {
+class ChangeToHaveMetadata : public nsRunnable {
+public:
+  ChangeToHaveMetadata(AbstractMediaDecoder* aDecoder) :
+    mDecoder(aDecoder)
+  {
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+    auto owner = mDecoder->GetOwner();
+    if (owner) {
+      owner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<AbstractMediaDecoder> mDecoder;
+};
+}
+
 nsresult
 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                         int64_t aCurrentTime)
 {
-  ResetDecode();
+  if (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)) {
+    NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
+  }
+
+  // Loop until we have the requested time range in the source buffers.
+  // This is a workaround for our lack of async functionality in the
+  // MediaDecoderStateMachine. Bug 979104 implements what we need and
+  // we'll remove this for an async approach based on that in bug XXXXXXX.
+  while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)
+         && !IsShutdown()) {
+    mMediaSource->WaitForData();
+    MaybeSwitchVideoReaders(aTime);
+  }
 
-  dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
-  if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
-    if (GetAudioReader()) {
-      nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
+  if (IsShutdown()) {
+    return NS_OK;
+  }
+
+  ResetDecode();
+  if (GetAudioReader()) {
+    nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+    if (NS_FAILED(rv)) {
+      return rv;
     }
-    if (GetVideoReader()) {
-      nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
+  }
+  if (GetVideoReader()) {
+    nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+     if (NS_FAILED(rv)) {
+      return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult
 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
 {
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -488,16 +488,18 @@ SourceBuffer::AppendData(const uint8_t* 
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, start);
   }
   StopUpdating();
 
   // Schedule the state machine thread to ensure playback starts
   // if required when data is appended.
   mMediaSource->GetDecoder()->ScheduleStateMachineThread();
+
+  mMediaSource->NotifyGotData();
 }
 
 void
 SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
 {
   ErrorResult dummy;
   nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
   if (!ranges || ranges->Length() == 0) {
--- a/content/media/webm/WebMWriter.cpp
+++ b/content/media/webm/WebMWriter.cpp
@@ -9,16 +9,21 @@
 namespace mozilla {
 
 WebMWriter::WebMWriter(uint32_t aTrackTypes) : ContainerWriter()
 {
   mMetadataRequiredFlag = aTrackTypes;
   mEbmlComposer = new EbmlComposer();
 }
 
+WebMWriter::~WebMWriter()
+{
+  // Out-of-line dtor so mEbmlComposer nsAutoPtr can delete a complete type.
+}
+
 nsresult
 WebMWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
                               uint32_t aFlags)
 {
   for (uint32_t i = 0 ; i < aData.GetEncodedFrames().Length(); i++) {
     mEbmlComposer->WriteSimpleBlock(aData.GetEncodedFrames().ElementAt(i).get());
   }
   return NS_OK;
--- a/content/media/webm/WebMWriter.h
+++ b/content/media/webm/WebMWriter.h
@@ -42,16 +42,18 @@ public:
  * The ctor/dtor run in the MediaRecorder thread, others run in MediaEncoder thread.
  */
 class WebMWriter : public ContainerWriter
 {
 public:
   // aTrackTypes indicate this muxer should multiplex into Video only or A/V foramt.
   // Run in MediaRecorder thread
   WebMWriter(uint32_t aTrackTypes);
+  virtual ~WebMWriter();
+
   // WriteEncodedTrack inserts raw packets into WebM stream.
   nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
                              uint32_t aFlags = 0) MOZ_OVERRIDE;
 
   // GetContainerData outputs multiplexing data.
   // aFlags indicates the muxer should enter into finished stage and flush out
   // queue data.
   nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -2161,35 +2161,34 @@ nsFocusManager::SetCaretVisible(nsIPresS
 
   nsRefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
 
   if (docFrameSelection && caret &&
      (frameSelection == docFrameSelection || !aContent)) {
     nsISelection* domSelection = docFrameSelection->
       GetSelection(nsISelectionController::SELECTION_NORMAL);
     if (domSelection) {
+      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
+      if (!selCon) {
+        return NS_ERROR_FAILURE;
+      }
       // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
-      caret->SetCaretVisible(false);
+      selCon->SetCaretEnabled(false);
 
       // Caret must blink on non-editable elements
       caret->SetIgnoreUserModify(true);
       // Tell the caret which selection to use
       caret->SetCaretDOMSelection(domSelection);
 
       // In content, we need to set the caret. The only special case is edit
       // fields, which have a different frame selection from the document.
       // They will take care of making the caret visible themselves.
 
-      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
-      if (!selCon)
-        return NS_ERROR_FAILURE;
-
       selCon->SetCaretReadOnly(false);
       selCon->SetCaretEnabled(aVisible);
-      caret->SetCaretVisible(aVisible);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -565,17 +565,17 @@ nsTimeout::HasRefCntOne()
   return mRefCnt.get() == 1;
 }
 
 nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
-  mMayHaveMouseEnterLeaveEventListener(false),
+  mMayHaveTouchCaret(false), mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mAudioMuted(false), mAudioVolume(1.0),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -55,18 +55,18 @@ enum PopupControlState {
 enum UIStateChangeType
 {
   UIStateChangeType_NoChange,
   UIStateChangeType_Set,
   UIStateChangeType_Clear
 };
 
 #define NS_PIDOMWINDOW_IID \
-{ 0xf26953de, 0xa799, 0x4a92, \
-  { 0x87, 0x49, 0x7c, 0x37, 0xe5, 0x90, 0x3f, 0x37 } }
+{ 0x33403513, 0x6e4a, 0x4985, \
+  { 0x99, 0x8d, 0xfc, 0x02, 0x81, 0x6e, 0xb9, 0xf2 } }
 
 class nsPIDOMWindow : public nsIDOMWindowInternal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
@@ -463,16 +463,31 @@ public:
     MaybeUpdateTouchState();
   }
 
   bool HasTouchEventListeners()
   {
     return mMayHaveTouchEventListener;
   }
 
+   /**
+   * Will be called when touch caret visibility has changed. mMayHaveTouchCaret
+   * is set if that some node (this window, its document, or content in that
+   * document) has a visible touch caret.
+   */
+  void SetMayHaveTouchCaret(bool aSetValue)
+  {
+    mMayHaveTouchCaret = aSetValue;
+  }
+
+  bool MayHaveTouchCaret()
+  {
+    return mMayHaveTouchCaret;
+  }
+
   /**
    * Moves the top-level window into fullscreen mode if aIsFullScreen is true,
    * otherwise exits fullscreen. If aRequireTrust is true, this method only
    * changes window state in a context trusted for write.
    */
   virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust) = 0;
 
   /**
@@ -756,16 +771,17 @@ protected:
 
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
   bool                   mIsHandlingResizeEvent;
   bool                   mIsInnerWindow;
   bool                   mMayHavePaintEventListener;
   bool                   mMayHaveTouchEventListener;
+  bool                   mMayHaveTouchCaret;
   bool                   mMayHaveMouseEnterLeaveEventListener;
   bool                   mMayHavePointerEnterLeaveEventListener;
 
   // This variable is used on both inner and outer windows (and they
   // should match).
   bool                   mIsModalContentWindow;
 
   // Tracks activation state that's used for :-moz-window-inactive.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1400,17 +1400,17 @@ WrapNativeParent(JSContext* cx, T* p, ns
 
   JSObject* parent = WrapNativeParentHelper<T>::Wrap(cx, p, cache);
   if (!useXBLScope) {
     return parent;
   }
 
   // If useXBLScope is true, it means that the canonical reflector for this
   // native object should live in the XBL scope.
-  if (xpc::IsInXBLScope(parent)) {
+  if (xpc::IsInContentXBLScope(parent)) {
     return parent;
   }
   JS::Rooted<JSObject*> rootedParent(cx, parent);
   JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScope(cx, rootedParent));
   JSAutoCompartment ac(cx, xblScope);
   if (NS_WARN_IF(!JS_WrapObject(cx, &rootedParent))) {
     return nullptr;
   }
--- a/dom/bluetooth/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth/ipc/BluetoothParent.cpp
@@ -19,17 +19,17 @@
 
 using mozilla::unused;
 USING_BLUETOOTH_NAMESPACE
 
 /*******************************************************************************
  * BluetoothRequestParent::ReplyRunnable
  ******************************************************************************/
 
-class BluetoothRequestParent::ReplyRunnable : public BluetoothReplyRunnable
+class BluetoothRequestParent::ReplyRunnable MOZ_FINAL : public BluetoothReplyRunnable
 {
   BluetoothRequestParent* mRequest;
 
 public:
   ReplyRunnable(BluetoothRequestParent* aRequest)
   : BluetoothReplyRunnable(nullptr), mRequest(aRequest)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -54,25 +54,32 @@ public:
 
     ReleaseMembers();
     return NS_OK;
   }
 
   void
   Revoke()
   {
-    MOZ_ASSERT(NS_IsMainThread());
-    mRequest = nullptr;
+    ReleaseMembers();
   }
 
   virtual bool
   ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) MOZ_OVERRIDE
   {
     MOZ_CRASH("This should never be called!");
   }
+
+  virtual void
+  ReleaseMembers() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mRequest = nullptr;
+    BluetoothReplyRunnable::ReleaseMembers();
+  }
 };
 
 /*******************************************************************************
  * BluetoothParent
  ******************************************************************************/
 
 BluetoothParent::BluetoothParent()
 : mShutdownState(Running)
--- a/dom/identity/Identity.manifest
+++ b/dom/identity/Identity.manifest
@@ -1,9 +1,8 @@
 # nsDOMIdentity.js
 component {210853d9-2c97-4669-9761-b1ab9cbf57ef} nsDOMIdentity.js
-contract @mozilla.org/dom/identity;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
-category JavaScript-navigator-property mozId @mozilla.org/dom/identity;1
+contract @mozilla.org/identity/manager;1 {210853d9-2c97-4669-9761-b1ab9cbf57ef}
 
 # nsIDService.js (initialization on startup)
 component {4e0a0e98-b1d3-4745-a1eb-f815199dd06b} nsIDService.js
 contract @mozilla.org/dom/identity/service;1 {4e0a0e98-b1d3-4745-a1eb-f815199dd06b}
 category app-startup IDService @mozilla.org/dom/identity/service;1
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -43,39 +43,20 @@ const ERRORS = {
   "ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS":
     "Only privileged and certified apps may use Firefox Accounts",
   "ERROR_INVALID_ASSERTION_AUDIENCE":
     "Assertion audience may not differ from origin",
   "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
     "The request() method may only be invoked when handling user input",
 };
 
-function nsDOMIdentity(aIdentityInternal) {
-  this._identityInternal = aIdentityInternal;
+function nsDOMIdentity() {
 }
-nsDOMIdentity.prototype = {
-  __exposedProps__: {
-    // Relying Party (RP)
-    watch: 'r',
-    request: 'r',
-    logout: 'r',
-    get: 'r',
-    getVerifiedEmail: 'r',
 
-    // Provisioning
-    beginProvisioning: 'r',
-    genKeyPair: 'r',
-    registerCertificate: 'r',
-    raiseProvisioningFailure: 'r',
-
-    // Authentication
-    beginAuthentication: 'r',
-    completeAuthentication: 'r',
-    raiseAuthenticationFailure: 'r'
-  },
+nsDOMIdentity.prototype = {
 
   // require native events unless syntheticEventsOk is set
   get nativeEventsRequired() {
     if (Services.prefs.prefHasUserValue(PREF_SYNTHETIC_EVENTS_OK) &&
         (Services.prefs.getPrefType(PREF_SYNTHETIC_EVENTS_OK) ===
          Ci.nsIPrefBranch.PREF_BOOL)) {
       return !Services.prefs.getBoolPref(PREF_SYNTHETIC_EVENTS_OK);
     }
@@ -144,17 +125,17 @@ nsDOMIdentity.prototype = {
     this._rpWatcher.audience = message.audience;
 
     if (message.errors.length) {
       this.reportErrors(message);
       // We don't delete the rpWatcher object, because we don't want the
       // broken client to be able to call watch() any more.  It's broken.
       return;
     }
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message);
+    this._mm.sendAsyncMessage("Identity:RP:Watch", message);
   },
 
   request: function nsDOMIdentity_request(aOptions = {}) {
     this._log("request: " + JSON.stringify(aOptions));
 
     // Has the caller called watch() before this?
     if (!this._rpWatcher) {
       throw new Error("navigator.id.request called before navigator.id.watch");
@@ -214,17 +195,17 @@ nsDOMIdentity.prototype = {
         throw new Error("oncancel is not a function");
       } else {
         // Store optional cancel callback for later.
         this._onCancelRequestCallback = aOptions.oncancel;
       }
     }
 
     this._rpCalls++;
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Request", message);
+    this._mm.sendAsyncMessage("Identity:RP:Request", message);
   },
 
   logout: function nsDOMIdentity_logout() {
     if (!this._rpWatcher) {
       throw new Error("navigator.id.logout called before navigator.id.watch");
     }
     if (this._rpCalls > MAX_RP_CALLS) {
       throw new Error("navigator.id.logout called too many times");
@@ -234,17 +215,17 @@ nsDOMIdentity.prototype = {
     let message = this.DOMIdentityMessage();
 
     // Report and fail hard on any errors.
     if (message.errors.length) {
       this.reportErrors(message);
       return;
     }
 
-    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Logout", message);
+    this._mm.sendAsyncMessage("Identity:RP:Logout", message);
   },
 
   /*
    * Get an assertion.  This function is deprecated.  RPs are
    * encouraged to use the observer API instead (watch + request).
    */
   get: function nsDOMIdentity_get(aCallback, aOptions) {
     var opts = {};
@@ -317,65 +298,65 @@ nsDOMIdentity.prototype = {
     if (this._beginProvisioningCallback) {
       throw new Error("navigator.id.beginProvisioning already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginProvisioning callback is required.");
     }
 
     this._beginProvisioningCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
+    this._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
                                                 this.DOMIdentityMessage());
   },
 
   genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
     this._log("genKeyPair");
     if (!this._beginProvisioningCallback) {
       throw new Error("navigator.id.genKeyPair called outside of provisioning");
     }
     if (this._genKeyPairCallback) {
       throw new Error("navigator.id.genKeyPair already called.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("genKeyPair callback is required.");
     }
 
     this._genKeyPairCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
+    this._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
                                                 this.DOMIdentityMessage());
   },
 
   registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
     this._log("registerCertificate");
     if (!this._genKeyPairCallback) {
       throw new Error("navigator.id.registerCertificate called outside of provisioning");
     }
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.cert = aCertificate;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
+    this._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
   },
 
   raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
     this._log("raiseProvisioningFailure '" + aReason + "'");
     if (this._provisioningEnded) {
       throw new Error("Provisioning already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
     this._provisioningEnded = true;
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
+    this._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
   },
 
   /**
    *  Identity Provider (IDP) Authentication APIs
    */
 
   beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
     this._log("beginAuthentication");
@@ -385,64 +366,44 @@ nsDOMIdentity.prototype = {
     if (typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
     if (!aCallback || typeof(aCallback) !== "function") {
       throw new Error("beginAuthentication callback is required.");
     }
 
     this._beginAuthenticationCallback = aCallback;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication",
+    this._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication",
                                                 this.DOMIdentityMessage());
   },
 
   completeAuthentication: function nsDOMIdentity_completeAuthentication() {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!this._beginAuthenticationCallback) {
       throw new Error("navigator.id.completeAuthentication called outside of authentication");
     }
     this._authenticationEnded = true;
 
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication",
+    this._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication",
                                                 this.DOMIdentityMessage());
   },
 
   raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
     if (this._authenticationEnded) {
       throw new Error("Authentication already ended");
     }
     if (!aReason || typeof(aReason) != "string") {
       throw new Error("raiseProvisioningFailure reason is required");
     }
 
     let message = this.DOMIdentityMessage();
     message.reason = aReason;
-    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message);
-  },
-
-  // Private.
-  _init: function nsDOMIdentity__init(aWindow) {
-
-    this._initializeState();
-
-    // Store window and origin URI.
-    this._window = aWindow;
-    this._origin = aWindow.document.nodePrincipal.origin;
-    this._appStatus = aWindow.document.nodePrincipal.appStatus;
-    this._appId = aWindow.document.nodePrincipal.appId;
-
-    // Setup identifiers for current window.
-    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-
-    // We need to inherit the id from the internalIdentity service.
-    // See comments below in that service's init.
-    this._id = this._identityInternal._id;
+    this._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message);
   },
 
   /**
    * Called during init and shutdown.
    */
   _initializeState: function nsDOMIdentity__initializeState() {
     // Some state to prevent abuse
     // Limit the number of calls to .request
@@ -452,22 +413,28 @@ nsDOMIdentity.prototype = {
 
     this._rpWatcher = null;
     this._onCancelRequestCallback = null;
     this._beginProvisioningCallback = null;
     this._genKeyPairCallback = null;
     this._beginAuthenticationCallback = null;
   },
 
-  _receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
+  // nsIMessageListener
+  receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
+    // Is this message intended for this window?
+    if (msg.id != this._id) {
+      return;
+    }
+
     switch (aMessage.name) {
       case "Identity:ResetState":
-        if (!this._identityInternal._debug) {
+        if (!this._debug) {
           return;
         }
         this._initializeState();
         Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
         break;
       case "Identity:RP:Watch:OnLogin":
         // Do we have a watcher?
         if (!this._rpWatcher) {
@@ -533,20 +500,16 @@ nsDOMIdentity.prototype = {
         this._callGenKeyPairCallback(msg);
         break;
       case "Identity:IDP:CallBeginAuthenticationCallback":
         this._callBeginAuthenticationCallback(msg);
         break;
     }
   },
 
-  _log: function nsDOMIdentity__log(msg) {
-    this._identityInternal._log(msg);
-  },
-
   _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
     // create a pubkey object that works
     let chrome_pubkey = JSON.parse(message.publicKey);
 
     // bunch of stuff to create a proper object in window context
     function genPropDesc(value) {
       return {
         enumerable: true, configurable: true, writable: true, value: value
@@ -637,71 +600,49 @@ nsDOMIdentity.prototype = {
     // Replace any audience supplied by the RP with one that has been sanitised
     message.audience = _audience;
 
     this._log("DOMIdentityMessage: " + JSON.stringify(message));
 
     return message;
   },
 
-  uninit: function DOMIdentity_uninit() {
-    this._log("nsDOMIdentity uninit() " + this._id);
-    this._identityInternal._mm.sendAsyncMessage(
-      "Identity:RP:Unwatch",
-      { id: this._id }
-    );
-  }
-
-};
-
-/**
- * Internal functions that shouldn't be exposed to content.
- */
-function nsDOMIdentityInternal() {
-}
-nsDOMIdentityInternal.prototype = {
-
-  // nsIMessageListener
-  receiveMessage: function nsDOMIdentityInternal_receiveMessage(aMessage) {
-    let msg = aMessage.json;
-    // Is this message intended for this window?
-    if (msg.id != this._id) {
-      return;
-    }
-    this._identity._receiveMessage(aMessage);
-  },
+  /*
+   * Internal methods that are not exposed to content.
+   * See dom/webidl/Identity.webidl for the public interface.
+   */
 
   // nsIObserver
   observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
     let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (wId != this._innerWindowID) {
       return;
     }
 
-    this._identity.uninit();
+    this.uninit();
 
     Services.obs.removeObserver(this, "inner-window-destroyed");
-    this._identity._initializeState();
-    this._identity = null;
+    this._initializeState();
 
     // TODO: Also send message to DOMIdentity notifiying window is no longer valid
     // ie. in the case that the user closes the auth. window and we need to know.
 
     try {
       for (let msgName of this._messages) {
         this._mm.removeMessageListener(msgName, this);
       }
     } catch (ex) {
       // Avoid errors when removing more than once.
     }
 
     this._mm = null;
   },
 
-  // nsIDOMGlobalPropertyInitializer
+  //  Because we implement nsIDOMGlobalPropertyInitializer, our init() method
+  //  is invoked with content window as its single argument.
   init: function nsDOMIdentityInternal_init(aWindow) {
     if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
         || !Services.prefs.getBoolPref(PREF_ENABLED)) {
       return null;
     }
 
     this._debug =
       Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
@@ -715,18 +656,27 @@ nsDOMIdentityInternal.prototype = {
     //
     // XXX Bug 869182 - use a combination of child process id and
     // innerwindow id to construct the unique id.
     this._id = uuidgen.generateUUID().toString();
     this._innerWindowID = util.currentInnerWindowID;
 
     // nsDOMIdentity needs to know our _id, so this goes after
     // its creation.
-    this._identity = new nsDOMIdentity(this);
-    this._identity._init(aWindow);
+    this._initializeState();
+
+    // Store window and origin URI.
+    this._window = aWindow;
+    this._origin = aWindow.document.nodePrincipal.origin;
+    this._appStatus = aWindow.document.nodePrincipal.appStatus;
+    this._appId = aWindow.document.nodePrincipal.appId;
+
+    // Setup identifiers for current window.
+    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
 
     this._log("init was called from " + aWindow.document.location);
 
     this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
@@ -740,34 +690,42 @@ nsDOMIdentityInternal.prototype = {
       "Identity:IDP:CallBeginAuthenticationCallback"
     ];
     this._messages.forEach(function(msgName) {
       this._mm.addMessageListener(msgName, this);
     }, this);
 
     // Setup observers so we can remove message listeners.
     Services.obs.addObserver(this, "inner-window-destroyed", false);
+  },
 
-    return this._identity;
+  uninit: function DOMIdentity_uninit() {
+    this._log("nsDOMIdentity uninit() " + this._id);
+    this._mm.sendAsyncMessage(
+      "Identity:RP:Unwatch",
+      { id: this._id }
+    );
   },
 
   // Private.
   _log: function nsDOMIdentityInternal__log(msg) {
     if (!this._debug) {
       return;
     }
     dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
   },
 
   // Component setup.
   classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
 
-  QueryInterface: XPCOMUtils.generateQI(
-    [Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIMessageListener]
-  ),
+  QueryInterface: XPCOMUtils.generateQI([
+      Ci.nsIMessageListener,
+      Ci.nsIObserver,
+      Ci.nsIDOMGlobalPropertyInitializer
+  ]),
 
   classInfo: XPCOMUtils.generateCI({
     classID: Components.ID("{210853d9-2c97-4669-9761-b1ab9cbf57ef}"),
     contractID: "@mozilla.org/dom/identity;1",
     interfaces: [],
     classDescription: "Identity DOM Implementation"
   })
 };
@@ -799,9 +757,9 @@ function assertCorrectCallbacks(aOptions
 
   for (let cbName of optionalCallbacks) {
     if (aOptions[cbName] && typeof(aOptions[cbName]) != "function") {
       throw new Error(cbName + " must be a function");
     }
   }
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentityInternal]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentity]);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2004,17 +2004,18 @@ TabChild::RecvRealTouchEvent(const Widge
 
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
   nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
 
-  if (!innerWindow || !innerWindow->HasTouchEventListeners()) {
+  if (!innerWindow || (!innerWindow->HasTouchEventListeners() &&
+                       !innerWindow->MayHaveTouchCaret())) {
     SendContentReceivedTouch(aGuid, false);
     return true;
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -14,30 +14,33 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
 const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
 const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
 const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
 const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
 const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1";
 const PC_IDENTITY_CONTRACT = "@mozilla.org/dom/rtcidentityassertion;1";
+const PC_STATIC_CONTRACT = "@mozilla.org/dom/peerconnectionstatic;1";
 
 const PC_CID = Components.ID("{00e0e20d-1494-4776-8e0e-0f0acbea3c79}");
 const PC_OBS_CID = Components.ID("{d1748d4c-7f6a-4dc5-add6-d55b7678537e}");
 const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
 const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
 const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
 const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}");
 const PC_IDENTITY_CID = Components.ID("{1abc7499-3c54-43e0-bd60-686e2703f072}");
+const PC_STATIC_CID = Components.ID("{0fb47c47-a205-4583-a9fc-cbadf8c95880}");
 
 // Global list of PeerConnection objects, so they can be cleaned up when
 // a page is torn down. (Maps inner window ID to an array of PC objects).
 function GlobalPCList() {
   this._list = {};
   this._networkdown = false; // XXX Need to query current state somehow
+  this._lifecycleobservers = {};
   Services.obs.addObserver(this, "inner-window-destroyed", true);
   Services.obs.addObserver(this, "profile-change-net-teardown", true);
   Services.obs.addObserver(this, "network:offline-about-to-go-offline", true);
   Services.obs.addObserver(this, "network:offline-status-changed", true);
 }
 GlobalPCList.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
@@ -47,16 +50,22 @@ GlobalPCList.prototype = {
     createInstance: function(outer, iid) {
       if (outer) {
         throw Cr.NS_ERROR_NO_AGGREGATION;
       }
       return _globalPCList.QueryInterface(iid);
     }
   },
 
+  notifyLifecycleObservers: function(pc, type) {
+    for (var key of Object.keys(this._lifecycleobservers)) {
+      this._lifecycleobservers[key](pc, pc._winID, type);
+    }
+  },
+
   addPC: function(pc) {
     let winID = pc._winID;
     if (this._list[winID]) {
       this._list[winID].push(Cu.getWeakReference(pc));
     } else {
       this._list[winID] = [Cu.getWeakReference(pc)];
     }
     this.removeNullRefs(winID);
@@ -92,17 +101,22 @@ GlobalPCList.prototype = {
     let cleanupWinId = function(list, winID) {
       if (list.hasOwnProperty(winID)) {
         list[winID].forEach(cleanupPcRef);
         delete list[winID];
       }
     };
 
     if (topic == "inner-window-destroyed") {
-      cleanupWinId(this._list, subject.QueryInterface(Ci.nsISupportsPRUint64).data);
+      let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      cleanupWinId(this._list, winID);
+
+      if (this._lifecycleobservers.hasOwnProperty(winID)) {
+        delete this._lifecycleobservers[winID];
+      }
     } else if (topic == "profile-change-net-teardown" ||
                topic == "network:offline-about-to-go-offline") {
       // Delete all peerconnections on shutdown - mostly synchronously (we
       // need them to be done deleting transports and streams before we
       // return)! All socket operations must be queued to STS thread
       // before we return to here.
       // Also kill them if "Work Offline" is selected - more can be created
       // while offline, but attempts to connect them should fail.
@@ -116,16 +130,19 @@ GlobalPCList.prototype = {
         // this._list shold be empty here
         this._networkdown = true;
       } else if (data == "online") {
         this._networkdown = false;
       }
     }
   },
 
+  _registerPeerConnectionLifecycleCallback: function(winID, cb) {
+    this._lifecycleobservers[winID] = cb;
+  },
 };
 let _globalPCList = new GlobalPCList();
 
 function RTCIceCandidate() {
   this.candidate = this.sdpMid = this.sdpMLineIndex = null;
 }
 RTCIceCandidate.prototype = {
   classDescription: "mozRTCIceCandidate",
@@ -330,16 +347,17 @@ RTCPeerConnection.prototype = {
       wait: !this._trickleIce
     });
   },
 
   _initialize: function(rtcConfig) {
     this._impl.initialize(this._observer, this._win, rtcConfig,
                           Services.tm.currentThread);
     this._initIdp();
+    _globalPCList.notifyLifecycleObservers(this, "initialized");
   },
 
   get _impl() {
     if (!this._pc) {
       throw new this._win.DOMError("",
           "RTCPeerConnection is gone (did you enter Offline mode?)");
     }
     return this._pc;
@@ -872,16 +890,17 @@ RTCPeerConnection.prototype = {
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.mozRTCSessionDescription({ type: this._remoteType,
                                                     sdp: sdp });
   },
 
   get peerIdentity() { return this._peerIdentity; },
+  get id() { return this._impl.id; },
   get iceGatheringState()  { return this._iceGatheringState; },
   get iceConnectionState() { return this._iceConnectionState; },
 
   get signalingState() {
     // checking for our local pc closed indication
     // before invoking the pc methods.
     if (this._closed) {
       return "closed";
@@ -894,20 +913,22 @@ RTCPeerConnection.prototype = {
       "SignalingHaveLocalPranswer":  "have-local-pranswer",
       "SignalingHaveRemotePranswer": "have-remote-pranswer",
       "SignalingClosed":             "closed"
     }[this._impl.signalingState];
   },
 
   changeIceGatheringState: function(state) {
     this._iceGatheringState = state;
+    _globalPCList.notifyLifecycleObservers(this, "icegatheringstatechange");
   },
 
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
+    _globalPCList.notifyLifecycleObservers(this, "iceconnectionstatechange");
     this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
   },
 
   getStats: function(selector, onSuccess, onError) {
     this._queueOrRun({
       func: this._getStats,
       args: [selector, onSuccess, onError],
       wait: true
@@ -1279,17 +1300,38 @@ PeerConnectionObserver.prototype = {
                                                                 { channel: channel }));
   },
 
   getSupportedConstraints: function(dict) {
     return dict;
   },
 };
 
+function RTCPeerConnectionStatic() {
+}
+RTCPeerConnectionStatic.prototype = {
+  classDescription: "mozRTCPeerConnectionStatic",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+                                         Ci.nsIDOMGlobalPropertyInitializer]),
+
+  classID: PC_STATIC_CID,
+  contractID: PC_STATIC_CONTRACT,
+
+  init: function(win) {
+    this._winID = win.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+  },
+
+  registerPeerConnectionLifecycleCallback: function(cb) {
+    _globalPCList._registerPeerConnectionLifecycleCallback(this._winID, cb);
+  },
+};
+
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList,
    RTCIceCandidate,
    RTCSessionDescription,
    RTCPeerConnection,
+   RTCPeerConnectionStatic,
    RTCStatsReport,
    RTCIdentityAssertion,
    PeerConnectionObserver]
 );
--- a/dom/media/PeerConnection.manifest
+++ b/dom/media/PeerConnection.manifest
@@ -1,15 +1,17 @@
 component {00e0e20d-1494-4776-8e0e-0f0acbea3c79} PeerConnection.js
 component {d1748d4c-7f6a-4dc5-add6-d55b7678537e} PeerConnection.js
 component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
 component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
 component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
 component {7fe6e18b-0da3-4056-bf3b-440ef3809e06} PeerConnection.js
 component {1abc7499-3c54-43e0-bd60-686e2703f072} PeerConnection.js
+component {0fb47c47-a205-4583-a9fc-cbadf8c95880} PeerConnection.js
 
 contract @mozilla.org/dom/peerconnection;1 {00e0e20d-1494-4776-8e0e-0f0acbea3c79}
 contract @mozilla.org/dom/peerconnectionobserver;1 {d1748d4c-7f6a-4dc5-add6-d55b7678537e}
 contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
 contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
 contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
 contract @mozilla.org/dom/rtcstatsreport;1 {7fe6e18b-0da3-4056-bf3b-440ef3809e06}
 contract @mozilla.org/dom/rtcidentityassertion;1 {1abc7499-3c54-43e0-bd60-686e2703f072}
+contract @mozilla.org/dom/peerconnectionstatic;1 {0fb47c47-a205-4583-a9fc-cbadf8c95880}
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -14,16 +14,18 @@ skip-if = toolkit == 'gonk' #Bug 962984 
 [test_dataChannel_basicAudioVideo.html]
 # Disabled on OS X for bug 930481 timeouts
 skip-if = os == 'mac' || toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicAudioVideoCombined.html]
 # Disabled on OS X for bug 930481 timeouts
 skip-if = os == 'mac' || toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicDataOnly.html]
 [test_dataChannel_basicVideo.html]
+skip-if = toolkit=='gonk' # b2g emulator seems to bee too slow (Bug 1016498 and 1008080)
+[test_dataChannel_bug1013809.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_noOffer.html]
 [test_getUserMedia_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_getUserMedia_basicVideo.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure, turned an intermittent (bug 962579) into a permanant orange
@@ -55,16 +57,18 @@ skip-if = toolkit=='gonk' # b2g(Bug 9604
 [test_peerConnection_basicVideo.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
+skip-if = toolkit=='gonk' # b2g emulator seems to bee too slow (Bug 1016498 and 1008080)
+[test_peerConnection_bug1013809.html]
 [test_peerConnection_close.html]
 [test_peerConnection_errorCallbacks.html]
 [test_peerConnection_offerRequiresReceiveAudio.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit=='gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) b2g-debug(Bug 960442, video support for WebRTC is disabled on b2g)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -476,18 +476,16 @@ function PeerConnectionTest(options) {
   else
     this.pcLocal = null;
 
   if (options.is_remote)
     this.pcRemote = new PeerConnectionWrapper('pcRemote', options.config_remote || options.config_local);
   else
     this.pcRemote = null;
 
-  this.connected = false;
-
   // Create command chain instance and assign default commands
   this.chain = new CommandChain(this, options.commands);
   if (!options.is_local) {
     this.chain.filterOut(/^PC_LOCAL/);
   }
   if (!options.is_remote) {
     this.chain.filterOut(/^PC_REMOTE/);
   }
@@ -505,17 +503,17 @@ function PeerConnectionTest(options) {
 
 /**
  * Closes the peer connection if it is active
  *
  * @param {Function} onSuccess
  *        Callback to execute when the peer connection has been closed successfully
  */
 PeerConnectionTest.prototype.close = function PCT_close(onSuccess) {
-  info("Closing peer connections. Connection state=" + this.connected);
+  info("Closing peer connections");
 
   var self = this;
   var closeTimeout = null;
   var waitingForLocal = false;
   var waitingForRemote = false;
   var everythingClosed = false;
 
   function verifyClosed() {
@@ -528,17 +526,16 @@ PeerConnectionTest.prototype.close = fun
       info("No closure pending");
       if (self.pcLocal) {
         is(self.pcLocal.signalingState, "closed", "pcLocal is in 'closed' state");
       }
       if (self.pcRemote) {
         is(self.pcRemote.signalingState, "closed", "pcRemote is in 'closed' state");
       }
       clearTimeout(closeTimeout);
-      self.connected = false;
       everythingClosed = true;
       onSuccess();
     }
   }
 
   function signalingstatechangeLocalClose(state) {
     info("'onsignalingstatechange' event '" + state + "' received");
     is(state, "closed", "onsignalingstatechange event is closed");
@@ -561,21 +558,24 @@ PeerConnectionTest.prototype.close = fun
       self.pcLocal.close();
     }
     if ((self.pcRemote) && (self.pcRemote.signalingState !== "closed")) {
       info("Closing pcRemote");
       self.pcRemote.onsignalingstatechange = signalingstatechangeRemoteClose;
       self.waitingForRemote = true;
       self.pcRemote.close();
     }
-    verifyClosed();
+    // give the signals handlers time to fire
+    setTimeout(verifyClosed, 1000);
   }
 
   closeTimeout = setTimeout(function() {
-    ok(false, "Closing PeerConnections timed out!");
+    var closed = ((self.pcLocal && (self.pcLocal.signalingState === "closed")) &&
+      (self.pcRemote && (self.pcRemote.signalingState === "closed")));
+    ok(closed, "Closing PeerConnections timed out");
     // it is not a success, but the show must go on
     onSuccess();
   }, 60000);
 
   closeEverything();
 };
 
 /**
@@ -789,63 +789,183 @@ function DataChannelTest(options) {
 }
 
 DataChannelTest.prototype = Object.create(PeerConnectionTest.prototype, {
   close : {
     /**
      * Close the open data channels, followed by the underlying peer connection
      *
      * @param {Function} onSuccess
-     *        Callback to execute when the connection has been closed
+     *        Callback to execute when all connections have been closed
      */
     value : function DCT_close(onSuccess) {
       var self = this;
+      var pendingDcClose = []
+      var closeTimeout = null;
 
-      function _closeChannels() {
-        var length = self.pcLocal.dataChannels.length;
+      info("DataChannelTest.close() called");
 
-        if (length > 0) {
-          self.closeDataChannel(length - 1, function () {
-            _closeChannels();
-          });
+      function _closePeerConnection() {
+        info("DataChannelTest closing PeerConnection");
+        PeerConnectionTest.prototype.close.call(self, onSuccess);
+      }
+
+      function _closePeerConnectionCallback(index) {
+        info("_closePeerConnection called with index " + index);
+        var pos = pendingDcClose.indexOf(index);
+        if (pos != -1) {
+          pendingDcClose.splice(pos, 1);
         }
         else {
-          PeerConnectionTest.prototype.close.call(self, onSuccess);
+          info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose);
+        }
+        if (pendingDcClose.length === 0) {
+          clearTimeout(closeTimeout);
+          _closePeerConnection();
         }
       }
 
-      _closeChannels();
+      var myDataChannels = null;
+      if (self.pcLocal) {
+        myDataChannels = self.pcLocal.dataChannels;
+      }
+      else if (self.pcRemote) {
+        myDataChannels = self.pcRemote.dataChannels;
+      }
+      var length = myDataChannels.length;
+      for (var i = 0; i < length; i++) {
+        var dataChannel = myDataChannels[i];
+        if (dataChannel.readyState !== "closed") {
+          pendingDcClose.push(i);
+          self.closeDataChannels(i, _closePeerConnectionCallback);
+        }
+      }
+      if (pendingDcClose.length === 0) {
+        _closePeerConnection();
+      }
+      else {
+        closeTimeout = setTimeout(function() {
+          ok(false, "Failed to properly close data channels: " +
+            pendingDcClose);
+          _closePeerConnection();
+        }, 60000);
+      }
     }
   },
 
-  closeDataChannel : {
+  closeDataChannels : {
     /**
-     * Close the specified data channel
+     * Close the specified data channels
      *
      * @param {Number} index
-     *        Index of the data channel to close on both sides
+     *        Index of the data channels to close on both sides
      * @param {Function} onSuccess
-     *        Callback to execute when the data channel has been closed
+     *        Callback to execute when the data channels has been closed
      */
-    value : function DCT_closeDataChannel(index, onSuccess) {
-      var localChannel = this.pcLocal.dataChannels[index];
-      var remoteChannel = this.pcRemote.dataChannels[index];
+    value : function DCT_closeDataChannels(index, onSuccess) {
+      info("_closeDataChannels called with index: " + index);
+      var localChannel = null;
+      if (this.pcLocal) {
+        localChannel = this.pcLocal.dataChannels[index];
+      }
+      var remoteChannel = null;
+      if (this.pcRemote) {
+        remoteChannel = this.pcRemote.dataChannels[index];
+      }
 
       var self = this;
+      var wait = false;
+      var pollingMode = false;
+      var everythingClosed = false;
+      var verifyInterval = null;
+      var remoteCloseTimer = null;
 
-      // Register handler for remote channel, cause we have to wait until
-      // the current close operation has been finished.
-      remoteChannel.onclose = function () {
-        self.pcRemote.dataChannels.splice(index, 1);
+      function _allChannelsAreClosed() {
+        var ret = null;
+        if (localChannel) {
+          ret = (localChannel.readyState === "closed");
+        }
+        if (remoteChannel) {
+          if (ret !== null) {
+            ret = (ret && (remoteChannel.readyState === "closed"));
+          }
+          else {
+            ret = (remoteChannel.readyState === "closed");
+          }
+        }
+        return ret;
+      }
+
+      function verifyClosedChannels() {
+        if (everythingClosed) {
+          // safety protection against events firing late
+          return;
+        }
+        if (_allChannelsAreClosed) {
+          ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index);
+          if (remoteCloseTimer !== null) {
+            clearTimeout(remoteCloseTimer);
+          }
+          if (verifyInterval !== null) {
+            clearInterval(verifyInterval);
+          }
+          everythingClosed = true;
+          onSuccess(index);
+        }
+        else {
+          info("Still waiting for DataChannel closure");
+        }
+      }
 
-        onSuccess(remoteChannel);
-      };
+      if ((localChannel) && (localChannel.readyState !== "closed")) {
+        // in case of steeplechase there is no far end, so we can only poll
+        if (remoteChannel) {
+          remoteChannel.onclose = function () {
+            is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'");
+            verifyClosedChannels();
+          };
+        }
+        else {
+          pollingMode = true;
+          verifyInterval = setInterval(verifyClosedChannels, 1000);
+        }
+
+        localChannel.close();
+        wait = true;
+      }
+      if ((remoteChannel) && (remoteChannel.readyState !== "closed")) {
+        if (localChannel) {
+          localChannel.onclose = function () {
+            is(localChannel.readyState, "closed", "localChannel is in state 'closed'");
+            verifyClosedChannels();
+          };
 
-      localChannel.close();
-      this.pcLocal.dataChannels.splice(index, 1);
+          // Apparently we are running a local test which has both ends of the
+          // data channel locally available, so by default lets wait for the
+          // remoteChannel.onclose handler from above to confirm closure on both
+          // ends.
+          remoteCloseTimer = setTimeout(function() {
+            todo(false, "localChannel.close() did not resulted in close signal on remote side");
+            remoteChannel.close();
+            verifyClosedChannels();
+          }, 30000);
+        }
+        else {
+          pollingMode = true;
+          verifyTimer = setInterval(verifyClosedChannels, 1000);
+
+          remoteChannel.close();
+        }
+
+        wait = true;
+      }
+
+      if (!wait) {
+        onSuccess(index);
+      }
     }
   },
 
   createDataChannel : {
     /**
      * Create a data channel
      *
      * @param {Dict} options
@@ -855,17 +975,17 @@ DataChannelTest.prototype = Object.creat
      */
     value : function DCT_createDataChannel(options, onSuccess) {
       var localChannel = null;
       var remoteChannel = null;
       var self = this;
 
       // Method to synchronize all asynchronous events.
       function check_next_test() {
-        if (self.connected && localChannel && remoteChannel) {
+        if (localChannel && remoteChannel) {
           onSuccess(localChannel, remoteChannel);
         }
       }
 
       if (!options.negotiated) {
         // Register handlers for the remote peer
         this.pcRemote.registerDataChannelOpenEvents(function (channel) {
           remoteChannel = channel;
@@ -920,91 +1040,72 @@ DataChannelTest.prototype = Object.creat
 
       source.send(data);
     }
   },
 
   setLocalDescription : {
     /**
      * Sets the local description for the specified peer connection instance
-     * and automatically handles the failure case. In case for the final call
-     * it will setup the requested datachannel.
+     * and automatically handles the failure case.
      *
      * @param {PeerConnectionWrapper} peer
               The peer connection wrapper to run the command on
      * @param {mozRTCSessionDescription} desc
      *        Session description for the local description request
      * @param {function} onSuccess
      *        Callback to execute if the local description was set successfully
      */
     value : function DCT_setLocalDescription(peer, desc, state, onSuccess) {
-      // If the peer has a remote offer we are in the final call, and have
-      // to wait for the datachannel connection to be open. It will also set
-      // the local description internally.
-      if (peer.signalingState === 'have-remote-offer') {
-        this.waitForInitialDataChannel(peer, desc, state, onSuccess);
-      }
-      else {
-        PeerConnectionTest.prototype.setLocalDescription.call(this, peer,
+      PeerConnectionTest.prototype.setLocalDescription.call(this, peer,
                                                               desc, state, onSuccess);
-      }
 
     }
   },
 
   waitForInitialDataChannel : {
     /**
-     * Create an initial data channel before the peer connection has been connected
+     * Wait for the initial data channel to get into the open state
      *
      * @param {PeerConnectionWrapper} peer
-              The peer connection wrapper to run the command on
-     * @param {mozRTCSessionDescription} desc
-     *        Session description for the local description request
+     *        The peer connection wrapper to run the command on
      * @param {Function} onSuccess
      *        Callback when the creation was successful
      */
-    value : function DCT_waitForInitialDataChannel(peer, desc, state, onSuccess) {
-      var self = this;
-
-      var targetPeer = peer;
-      var targetChannel = null;
+    value : function DCT_waitForInitialDataChannel(peer, onSuccess, onFailure) {
+      var dcConnectionTimeout = null;
 
-      var sourcePeer = (peer == this.pcLocal) ? this.pcRemote : this.pcLocal;
-      var sourceChannel = null;
+      function dataChannelConnected(channel) {
+        clearTimeout(dcConnectionTimeout);
+        is(channel.readyState, "open", peer + " dataChannels[0] is in state: 'open'");
+        onSuccess();
+      }
 
-      // Method to synchronize all asynchronous events which current happen
-      // due to a non-predictable flow. With bug 875346 fixed we will be able
-      // to simplify this code.
-      function check_next_test() {
-        if (self.connected && sourceChannel && targetChannel) {
-          onSuccess(sourceChannel, targetChannel);
-        }
+      if ((peer.dataChannels.length >= 1) &&
+          (peer.dataChannels[0].readyState === "open")) {
+        is(peer.dataChannels[0].readyState, "open", peer + " dataChannels[0] is in state: 'open'");
+        onSuccess();
+        return;
       }
 
-      // Register 'onopen' handler for the first local data channel
-      sourcePeer.dataChannels[0].onopen = function (channel) {
-        sourceChannel = channel;
-        check_next_test();
-      };
+      // TODO: drno: convert dataChannels into an object and make
+      //             registerDataChannelOPenEvent a generic function
+      if (peer == this.pcLocal) {
+        peer.dataChannels[0].onopen = dataChannelConnected;
+      } else {
+        peer.registerDataChannelOpenEvents(dataChannelConnected);
+      }
 
-      // Register handlers for the target peer
-      targetPeer.registerDataChannelOpenEvents(function (channel) {
-        targetChannel = channel;
-        check_next_test();
-      });
-
-      PeerConnectionTest.prototype.setLocalDescription.call(this, targetPeer, desc,
-        state,
-        function () {
-          self.connected = true;
-          check_next_test();
-        }
-      );
+      dcConnectionTimeout = setTimeout(function () {
+        info(peer + " timed out while waiting for dataChannels[0] to connect");
+        onFailure();
+      }, 60000);
     }
   }
+
 });
 
 /**
  * This class acts as a wrapper around a DataChannel instance.
  *
  * @param dataChannel
  * @param peerConnectionWrapper
  * @constructor
@@ -1961,20 +2062,17 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {Function} onDataChannelOpened
    *        Callback to execute when the data channel has been opened
    */
   registerDataChannelOpenEvents : function (onDataChannelOpened) {
     info(this + ": Register callbacks for 'ondatachannel' and 'onopen'");
 
     this.ondatachannel = function (targetChannel) {
-      targetChannel.onopen = function (targetChannel) {
-        onDataChannelOpened(targetChannel);
-      };
-
+      targetChannel.onopen = onDataChannelOpened;
       this.dataChannels.push(targetChannel);
     };
   },
 
   /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -130,16 +130,26 @@ var commandsPeerConnection = [
           send_message({"answer": test.pcRemote._last_answer,
                         "media_constraints": test.pcRemote.constraints});
         }
         test.next();
       });
     }
   ],
   [
+    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE, function () {
+        is(test.pcRemote.signalingState, STABLE,
+           "signalingState after remote setLocalDescription is 'stable'");
+        test.next();
+      });
+    }
+  ],
+  [
     'PC_LOCAL_GET_ANSWER',
     function (test) {
       if (test.pcRemote) {
         test._remote_answer = test.pcRemote._last_answer;
         test._remote_constraints = test.pcRemote.constraints;
         test.next();
       } else {
         wait_for_message().then(function(message) {
@@ -157,26 +167,16 @@ var commandsPeerConnection = [
       test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE, function () {
         is(test.pcLocal.signalingState, STABLE,
            "signalingState after local setRemoteDescription is 'stable'");
         test.next();
       });
     }
   ],
   [
-    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
-    function (test) {
-      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE, function () {
-        is(test.pcRemote.signalingState, STABLE,
-           "signalingState after remote setLocalDescription is 'stable'");
-        test.next();
-      });
-    }
-  ],
-  [
     'PC_LOCAL_WAIT_FOR_ICE_CONNECTED',
     function (test) {
       var myTest = test;
       var myPc = myTest.pcLocal;
 
       function onIceConnectedSuccess () {
         ok(true, "pc_local: ICE switched to 'connected' state");
         myTest.next();
@@ -370,39 +370,82 @@ var commandsDataChannel = [
       test.createAnswer(test.pcRemote, function () {
         is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
            "Remote create offer does not change signaling state");
         test.next();
       });
     }
   ],
   [
+    'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcLocal, function () {
+        ok(true, test.pcLocal + " dataChannels[0] switched to 'open'");
+      }, function () {
+        ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout')
+      });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcRemote, function () {
+        ok(true, test.pcRemote + " dataChannels[0] switched to 'open'");
+      }, function () {
+        ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout');
+      });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE,
+        function () {
+          is(test.pcRemote.signalingState, STABLE,
+             "signalingState after remote setLocalDescription is 'stable'");
+          test.next();
+        }
+      );
+    }
+  ],
+  [
     'PC_LOCAL_SET_REMOTE_DESCRIPTION',
     function (test) {
       test.setRemoteDescription(test.pcLocal, test.pcRemote._last_answer, STABLE,
         function () {
         is(test.pcLocal.signalingState, STABLE,
            "signalingState after local setRemoteDescription is 'stable'");
         test.next();
       });
     }
   ],
   [
-    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE',
     function (test) {
-      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, STABLE,
-        function (sourceChannel, targetChannel) {
-          is(sourceChannel.readyState, "open", test.pcLocal + " is in state: 'open'");
-          is(targetChannel.readyState, "open", test.pcRemote + " is in state: 'open'");
-
-          is(test.pcRemote.signalingState, STABLE,
-             "signalingState after remote setLocalDescription is 'stable'");
-          test.next();
-        }
-      );
+      test.waitForInitialDataChannel(test.pcLocal, function() {
+        test.next();
+      }, function() {
+        ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout')
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE',
+    function (test) {
+      test.waitForInitialDataChannel(test.pcRemote, function() {
+        test.next();
+      }, function() {
+        ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'");
+        unexpectedEventAndFinish(this, 'timeout');
+      });
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA_TRACKS',
     function (test) {
       test.pcLocal.checkMediaTracks(test.pcRemote.constraints, function () {
         test.next();
       });
@@ -503,33 +546,51 @@ var commandsDataChannel = [
         is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used");
         is(data, message, "Received message has the correct content.");
 
         test.next();
       }, options);
     }
   ],
   [
+    'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL',
+    function (test) {
+      var message = "Return a message also through 1st channel";
+      var options = {
+        sourceChannel: test.pcRemote.dataChannels[0],
+        targetChannel: test.pcLocal.dataChannels[0]
+      };
+
+      test.send(message, function (channel, data) {
+        is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used");
+        is(data, message, "Return message has the correct content.");
+
+        test.next();
+      }, options);
+    }
+  ],
+  [
     'CREATE_NEGOTIATED_DATA_CHANNEL',
     function (test) {
       var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false,
-		     maxRetransmits:500};
+        maxRetransmits:500};
       test.createDataChannel(options, function (sourceChannel2, targetChannel2) {
         is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
         is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
         is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
         is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
         if (options.id != undefined) {
           is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
-	} else {
-	  options.id = sourceChannel2.id;
-	}
-	var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
+        }
+        else {
+          options.id = sourceChannel2.id;
+        }
+        var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
         is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
         is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
 /*
   These aren't exposed by IDL yet
         is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered);
         is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" +
 	   sourceChannel2.maxRetransmits);
         is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" +
@@ -560,34 +621,10 @@ var commandsDataChannel = [
 
       test.send(message, function (channel, data) {
         is(channels.indexOf(channel), channels.length - 1, "Last channel used");
         is(data, message, "Received message has the correct content.");
 
         test.next();
       });
     }
-  ],
-  [
-    'CLOSE_LAST_OPENED_DATA_CHANNEL2',
-    function (test) {
-      var channels = test.pcRemote.dataChannels;
-
-      test.closeDataChannel(channels.length - 1, function (channel) {
-        is(channel.readyState, "closed", "Channel is in state: 'closed'");
-
-        test.next();
-      });
-    }
-  ],
-  [
-    'CLOSE_LAST_OPENED_DATA_CHANNEL',
-    function (test) {
-      var channels = test.pcRemote.dataChannels;
-
-      test.closeDataChannel(channels.length - 1, function (channel) {
-        is(channel.readyState, "closed", "Channel is in state: 'closed'");
-
-        test.next();
-      });
-    }
   ]
 ];
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "796895",
+    title: "Basic data channel audio connection"
+  });
+
+  var test;
+  runTest(function () {
+    test = new DataChannelTest();
+    var sld = test.chain.remove("PC_REMOTE_SET_LOCAL_DESCRIPTION");
+    test.chain.insertAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION", sld);
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.run();
+  }, true);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_bug1013809.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1013809",
+    title: "Audio-only peer connection with swapped setLocal and setRemote steps"
+  });
+
+  var test;
+  runTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var sld = test.chain.remove("PC_REMOTE_SET_LOCAL_DESCRIPTION");
+    test.chain.insertAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION", sld);
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Identity.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+callback IdentityOnReadyCallback = void();
+callback IdentityOnLoginCallback = void(DOMString identityAssertion);
+callback IdentityOnLogoutCallback = void();
+callback IdentityOnCancelCallback = void(DOMString? error);
+callback IdentityOnErrorCallback = void(DOMString error);
+
+dictionary IdentityWatchOptions {
+  // Required callback
+  IdentityOnLoginCallback onlogin;
+
+  // Optional parameters
+  DOMString wantIssuer;
+  DOMString loggedInUser;
+
+  // Optional callbacks
+  IdentityOnReadyCallback onready;
+  IdentityOnLogoutCallback onlogout;
+  IdentityOnErrorCallback onerror;
+
+  // Certified apps can specify this
+  DOMString audience;
+};
+
+dictionary IdentityRequestOptions {
+  // Optional parameters
+  long refreshAuthentication;
+  DOMString termsOfService;
+  DOMString privacyPolicy;
+  DOMString backgroundColor;
+  DOMString siteLogo;
+  DOMString siteName;
+  DOMString returnTo;
+
+  IdentityOnCancelCallback oncancel;
+
+  // Certified apps can specify this
+  DOMString origin;
+};
+
+dictionary IdentityGetOptions {
+  DOMString privacyPolicy;
+  DOMString termsOfService;
+  DOMString privacyURL;
+  DOMString tosURL;
+  DOMString siteName;
+  DOMString siteLogo;
+};
+
+[JSImplementation="@mozilla.org/identity/manager;1",
+ NoInterfaceObject,
+ NavigatorProperty="mozId",
+ Pref="dom.identity.enabled"]
+interface IdentityManager {
+  void watch(optional IdentityWatchOptions options);
+  void request(optional IdentityRequestOptions options);
+  void logout();
+
+  [Pref="dom.identity.exposeLegacyGetAPI"]
+  void get(IdentityOnLoginCallback callback, optional IdentityGetOptions options);
+
+  [Pref="dom.identity.exposeLegacyGetVerifiedEmailAPI"]
+  void getVerifiedEmail(IdentityOnLoginCallback callback);
+};
+
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -65,16 +65,17 @@ interface PeerConnectionImpl  {
   readonly attribute DOMString fingerprint;
   readonly attribute DOMString localDescription;
   readonly attribute DOMString remoteDescription;
 
   readonly attribute PCImplIceConnectionState iceConnectionState;
   readonly attribute PCImplIceGatheringState iceGatheringState;
   readonly attribute PCImplSignalingState signalingState;
   readonly attribute PCImplSipccState sipccState;
+  readonly attribute DOMString id;
 
   attribute DOMString peerIdentity;
   readonly attribute boolean privacyRequested;
 
   /* Data channels */
   [Throws]
   DataChannel createDataChannel(DOMString label, DOMString protocol,
     unsigned short type, boolean outOfOrderAllowed,
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -113,16 +113,19 @@ interface mozRTCPeerConnection : EventTa
   void addIceCandidate (mozRTCIceCandidate candidate,
                         optional VoidFunction successCallback,
                         optional RTCPeerConnectionErrorCallback failureCallback);
   readonly attribute RTCIceGatheringState iceGatheringState;
   readonly attribute RTCIceConnectionState iceConnectionState;
   [Pref="media.peerconnection.identity.enabled"]
   readonly attribute RTCIdentityAssertion? peerIdentity;
 
+  [ChromeOnly]
+  readonly attribute DOMString id;
+
   sequence<MediaStream> getLocalStreams ();
   sequence<MediaStream> getRemoteStreams ();
   MediaStream? getStreamById (DOMString streamId);
   void addStream (MediaStream stream, optional MediaConstraints constraints);
   void removeStream (MediaStream stream);
   void close ();
   attribute EventHandler onnegotiationneeded;
   attribute EventHandler onicecandidate;
@@ -143,8 +146,9 @@ interface mozRTCPeerConnection : EventTa
   attribute EventHandler onidentityresult;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onpeeridentity;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onidpassertionerror;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onidpvalidationerror;
 };
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/RTCPeerConnectionStatic.webidl
@@ -0,0 +1,39 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+  Right now, it is not possible to add static functions to a JS implemented
+  interface (see bug 863952), so we need to create a simple interface with a
+  trivial constructor and no data to hold these functions that really ought to
+  be static in mozRTCPeerConnection.
+  TODO(bcampen@mozilla.com) Merge this code into RTCPeerConnection once this
+  limitation is gone. (Bug 1017082)
+*/
+
+enum RTCLifecycleEvent {
+    "initialized",
+    "icegatheringstatechange",
+    "iceconnectionstatechange"
+};
+
+callback PeerConnectionLifecycleCallback = void (mozRTCPeerConnection pc,
+                                                 unsigned long long windowId,
+                                                 RTCLifecycleEvent eventType);
+
+[ChromeOnly,
+ Pref="media.peerconnection.enabled",
+ JSImplementation="@mozilla.org/dom/peerconnectionstatic;1",
+ Constructor()]
+interface mozRTCPeerConnectionStatic {
+
+  /* One slot per window (the window in which the register call is made),
+     automatically unregistered when window goes away.
+     Fires when a PC is created, and whenever the ICE connection state or
+     gathering state changes. */
+  void registerPeerConnectionLifecycleCallback(
+    PeerConnectionLifecycleCallback cb);
+};
+
--- a/dom/webidl/WebrtcGlobalInformation.webidl
+++ b/dom/webidl/WebrtcGlobalInformation.webidl
@@ -10,16 +10,17 @@ dictionary WebrtcGlobalStatisticsReport 
 
 callback WebrtcGlobalStatisticsCallback = void (WebrtcGlobalStatisticsReport reports);
 callback WebrtcGlobalLoggingCallback = void (sequence<DOMString> logMessages);
 
 [ChromeOnly]
 interface WebrtcGlobalInformation {
 
   [Throws]
-  static void getAllStats(WebrtcGlobalStatisticsCallback callback);
+  static void getAllStats(WebrtcGlobalStatisticsCallback callback,
+                          optional DOMString pcIdFilter);
 
   [Throws]
   static void getLogging(DOMString pattern,
                          WebrtcGlobalLoggingCallback callback);
 };
 
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -207,16 +207,17 @@ WEBIDL_FILES = [
     'IDBFileHandle.webidl',
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
+    'Identity.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
     'InstallPhaseEvent.webidl',
     'InterAppConnection.webidl',
@@ -293,16 +294,17 @@ WEBIDL_FILES = [
     'PushManager.webidl',
     'Range.webidl',
     'Rect.webidl',
     'RGBColor.webidl',
     'RTCConfiguration.webidl',
     'RTCIceCandidate.webidl',
     'RTCIdentityAssertion.webidl',
     'RTCPeerConnection.webidl',
+    'RTCPeerConnectionStatic.webidl',
     'RTCSessionDescription.webidl',
     'RTCStatsReport.webidl',
     'Screen.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'Selection.webidl',
     'ServiceWorker.webidl',
     'ServiceWorkerContainer.webidl',
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -99,28 +99,28 @@ static const JSClass gPrototypeJSClass =
     nullptr, nullptr, nullptr, nullptr
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false)
-  , mUsingXBLScope(false)
+  , mUsingContentXBLScope(false)
   , mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
 // Constructor used by web components.
 nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false),
-    mUsingXBLScope(false),
+    mUsingContentXBLScope(false),
     mPrototypeBinding(aBinding),
     mContent(aShadowRoot)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
@@ -273,17 +273,17 @@ nsXBLBinding::SetBoundElement(nsIContent
   // Compute whether we're using an XBL scope.
   //
   // We disable XBL scopes for remote XUL, where we care about compat more
   // than security. So we need to know whether we're using an XBL scope so that
   // we can decide what to do about untrusted events when "allowuntrusted"
   // is not given in the handler declaration.
   nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject();
   NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject());
-  mUsingXBLScope = xpc::UseXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
+  mUsingContentXBLScope = xpc::UseContentXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
 }
 
 bool
 nsXBLBinding::HasStyleSheets() const
 {
   // Find out if we need to re-resolve style.  We'll need to do this
   // if we have additional stylesheets in our binding document.
   if (mPrototypeBinding->HasStyleSheets())
@@ -507,33 +507,33 @@ nsXBLBinding::InstallEventHandlers()
           if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
                                   NS_HANDLER_TYPE_SYSTEM)) &&
               (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
             flags.mInSystemGroup = true;
           }
 
           bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
           if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
-              (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingXBLScope)) {
+              (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingContentXBLScope)) {
             flags.mAllowUntrustedEvents = true;
           }
 
           manager->AddEventListenerByType(handler,
                                           nsDependentAtomString(eventAtom),
                                           flags);
         }
       }
 
       const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
         mPrototypeBinding->GetKeyEventHandlers();
       int32_t i;
       for (i = 0; i < keyHandlers->Count(); ++i) {
         nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
         handler->SetIsBoundToChrome(isChromeDoc);
-        handler->SetUsingXBLScope(mUsingXBLScope);
+        handler->SetUsingContentXBLScope(mUsingContentXBLScope);
 
         nsAutoString type;
         handler->GetEventName(type);
 
         // If this is a command, add it in the system event group, otherwise
         // add it to the standard event group.
 
         // Figure out if we're using capturing or not.
@@ -915,18 +915,18 @@ GetOrCreateMapEntryForPrototype(JSContex
   // node with grand-proto HTMLDivElement and a bound anonymous node whose
   // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
   // Since we have to wrap the WeakMap keys into its scope, this distinction
   // would be lost if we don't do something about it.
   //
   // So we define two maps - one class objects that live in content (prototyped
   // to content prototypes), and the other for class objects that live in the
   // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
-  const char* name = xpc::IsInXBLScope(proto) ? "__ContentClassObjectMap__"
-                                              : "__XBLClassObjectMap__";
+  const char* name = xpc::IsInContentXBLScope(proto) ? "__ContentClassObjectMap__"
+                                                     : "__XBLClassObjectMap__";
 
   // Now, enter the XBL scope, since that's where we need to operate, and wrap
   // the proto accordingly.
   JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
   JS::Rooted<JSObject*> wrappedProto(cx, proto);
   JSAutoCompartment ac(cx, scope);
   if (!JS_WrapObject(cx, &wrappedProto)) {
     return nullptr;
@@ -1111,17 +1111,17 @@ nsXBLBinding::LookupMember(JSContext* aC
   //
   // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
   // into content. So for NAC reflectors that live in the XBL scope, we should
   // never get here. But on the off-chance that someone adds new callsites to
   // LookupMember, we do a release-mode assertion as belt-and-braces.
   // We do a release-mode assertion here to be extra safe.
   JS::Rooted<JSObject*> boundScope(aCx,
     js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper()));
-  MOZ_RELEASE_ASSERT(!xpc::IsInXBLScope(boundScope));
+  MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope));
   JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope));
   NS_ENSURE_TRUE(xblScope, false);
   MOZ_ASSERT(boundScope != xblScope);
 
   // Enter the xbl scope and invoke the internal version.
   {
     JSAutoCompartment ac(aCx, xblScope);
     JS::Rooted<jsid> id(aCx, aId);
--- a/dom/xbl/nsXBLBinding.h
+++ b/dom/xbl/nsXBLBinding.h
@@ -155,17 +155,17 @@ public:
   // Returns a live node list that iterates over the anonymous nodes generated
   // by this binding.
   nsAnonymousContentList* GetAnonymousNodeList();
 
 // MEMBER VARIABLES
 protected:
 
   bool mMarkedForDeath;
-  bool mUsingXBLScope;
+  bool mUsingContentXBLScope;
 
   nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
   nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
   nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
 
   nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
 
   // The <xbl:children> elements that we found in our <xbl:content> when we
--- a/dom/xbl/nsXBLEventHandler.cpp
+++ b/dom/xbl/nsXBLEventHandler.cpp
@@ -66,17 +66,17 @@ nsXBLMouseEventHandler::EventMatched(nsI
 }
 
 nsXBLKeyEventHandler::nsXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase,
                                            uint8_t aType)
   : mEventType(aEventType),
     mPhase(aPhase),
     mType(aType),
     mIsBoundToChrome(false),
-    mUsingXBLScope(false)
+    mUsingContentXBLScope(false)
 {
 }
 
 nsXBLKeyEventHandler::~nsXBLKeyEventHandler()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsXBLKeyEventHandler, nsIDOMEventListener)
@@ -92,17 +92,17 @@ nsXBLKeyEventHandler::ExecuteMatchedHand
   nsCOMPtr<EventTarget> target = aKeyEvent->InternalDOMEvent()->GetCurrentTarget();
 
   bool executed = false;
   for (uint32_t i = 0; i < mProtoHandlers.Length(); ++i) {
     nsXBLPrototypeHandler* handler = mProtoHandlers[i];
     bool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr();
     if ((trustedEvent ||
         (hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) ||
-        (!hasAllowUntrustedAttr && !mIsBoundToChrome && !mUsingXBLScope)) &&
+        (!hasAllowUntrustedAttr && !mIsBoundToChrome && !mUsingContentXBLScope)) &&
         handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreShiftKey)) {
       handler->ExecuteHandler(target, aKeyEvent);
       executed = true;
     }
   }
   return executed;
 }
 
--- a/dom/xbl/nsXBLEventHandler.h
+++ b/dom/xbl/nsXBLEventHandler.h
@@ -81,32 +81,32 @@ public:
     return mType;
   }
 
   void SetIsBoundToChrome(bool aIsBoundToChrome)
   {
     mIsBoundToChrome = aIsBoundToChrome;
   }
 
-  void SetUsingXBLScope(bool aUsingXBLScope)
+  void SetUsingContentXBLScope(bool aUsingContentXBLScope)
   {
-    mUsingXBLScope = aUsingXBLScope;
+    mUsingContentXBLScope = aUsingContentXBLScope;
   }
 
 private:
   nsXBLKeyEventHandler();
   bool ExecuteMatchedHandlers(nsIDOMKeyEvent* aEvent, uint32_t aCharCode,
                                 bool aIgnoreShiftKey);
 
   nsTArray<nsXBLPrototypeHandler*> mProtoHandlers;
   nsCOMPtr<nsIAtom> mEventType;
   uint8_t mPhase;
   uint8_t mType;
   bool mIsBoundToChrome;
-  bool mUsingXBLScope;
+  bool mUsingContentXBLScope;
 };
 
 nsresult
 NS_NewXBLEventHandler(nsXBLPrototypeHandler* aHandler,
                       nsIAtom* aEventType,
                       nsXBLEventHandler** aResult);
 
 nsresult
--- a/dom/xbl/nsXBLProtoImplMethod.cpp
+++ b/dom/xbl/nsXBLProtoImplMethod.cpp
@@ -99,17 +99,17 @@ nsresult
 nsXBLProtoImplMethod::InstallMember(JSContext* aCx,
                                     JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(IsCompiled(),
                   "Should not be installing an uncompiled method");
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
 
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
-  MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
+  MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
              globalObject == xpc::GetXBLScope(aCx, globalObject));
 
   JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
   if (jsMethodObject) {
     nsDependentString name(mName);
 
     JS::Rooted<JSObject*> method(aCx, JS_CloneFunctionObject(aCx, jsMethodObject, globalObject));
     NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -124,17 +124,17 @@ nsresult
 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
                                       JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(mIsCompiled,
                   "Should not be installing an uncompiled property");
   MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
-  MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
+  MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
              globalObject == xpc::GetXBLScope(aCx, globalObject));
 
   JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
   JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
   if (getter || setter) {
     if (getter) {
       if (!(getter = ::JS_CloneFunctionObject(aCx, getter, globalObject)))
         return NS_ERROR_OUT_OF_MEMORY;
--- a/editor/composer/src/moz.build
+++ b/editor/composer/src/moz.build
@@ -34,9 +34,12 @@ RESOURCE_FILES += [
     'res/table-add-row-before-hover.gif',
     'res/table-add-row-before.gif',
     'res/table-remove-column-active.gif',
     'res/table-remove-column-hover.gif',
     'res/table-remove-column.gif',
     'res/table-remove-row-active.gif',
     'res/table-remove-row-hover.gif',
     'res/table-remove-row.gif',
+    'res/text_selection_handle.png',
+    'res/text_selection_handle@1.5.png',
+    'res/text_selection_handle@2.png',
 ]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..656854decc593d3ccd8767654f1743fb2523b64e
GIT binary patch
literal 1580
zc$|Gy4NMbf7`{?1D%Saj!@qGJ8^SW$-j)7JZ3`_w&azsV!ZaDzyKAY?-g!N2AqXO|
zj?5q8R%L27j8hSv6VVu%N<bM9LAU78;S33OnrsflE+~+~t|)9-)Gv4UeczksdEWPX
zzb98=Oi$s@STq9w0DfAk&cx31*f(MtFZkX6<6G>skkMx`8Ki}Ap)>(#anekHX?D~~
zm<SXv+|*7a008%0TV@uMWmv7mNV@<HVgznG1q4Te+D)NY9>IWS!fJD<p#C2kA<%}a
zpmh?1$Ur3#xwh0In#d?h&%}!IFa-{&SAq#{B`aVj7!-8d^Bqp5TLq2lD%o`~EQG*u
z2$QFRUKEvOFoH=WO@I=CI1UrRFesG@;CP8t3V#41A{Y^h*hd-%$15dLB_ab~4v5W0
z;})e!m;5ppyHi2A3_~e}LYK=WaES#ZZ56@_g(9edAaN`r&ROVSP<Nce88xAxBb*p*
zqZk|M0E2tfOcpRI2%0#8otl(&IA6AjZJ5xFQbJfD68;BhFuWORw@;#-jES(k8STt0
zqzIvjaFPWy#$KEyDoCc3Ni=~nB%Mi;`4e3<=8_EQ%q1x>DMJdz7*NdS2nJ%u83uzg
z&EaHF2S%jnR1g~x*lf5`s!LSJ^{_;N=o4XBFIH%El0-x<k%<*>q88CkaCIbBU?&{R
z1Q-98D}60DXoeSTg0^iY@MM~_gX4!*+FntMR)$DL$qFs2Cx3M<FSz*YYl(ktEkd>!
z;s36BBE<S9xSTXDyP32;;b8qvv&J4cFOCI(kgwBp+Dv!1IsVG9mlG*J+1XiB`n>F}
ze*N}!n)73IZC8I?J!Pi3>z-@HKE2Ub*S&4&A7J^wiPpS`*%ab`>J^EQvuCfzdP9ct
z@r69v+to1d=hYh$8o-qYYw(?>9`n-T(^zwO`un%KHqtS9448@34cu$!Trl0x8`UkU
z)YjI^VxN2>KZW6FxMod93l@{{+gabxIq5t39vEtk{t1l!ZkKhZ!soriW@_)Hn;Ii~
zc9$qz(BsXu{Z8<i+H+llep~O~C_27(SX<01lWd~0{Rg@a-QG~33-y8XuWAG#tM1l)
z*dE96@RxJv0J(=}9<S|M(9$<`uZY{^;c)kbM%=B}lyzuAa&T<_gL>+5D3aaeDZ8}X
zyDqZ*TGNSU4KHWT{8{_Rs#CL&#Xe-#+BFq9Jem^~CBATFVX8W8p4{v7=ORX+>S7^(
z>--gMB56)hueG`@-m<I;?HDT=8%?gr9xEMv&bbj)9sm4M;9$y^5rcpJ;vWH>8c$%j
z#QzjtwPX;zc`TqAmbIPf|EMUhD=;$nJ691MD0V+;KKb4)0p}}qmG;KS$iV&5oT8zd
zM@S2Q=f-3+fB4yhk^L#1Qw!96_b<)#l~_EM+l}As?%UF`?EB`H6UUD1IMhQe`rF@n
z!TQAkWzXqP&xN&ZnSQObN_&)dCUq!`ez-%mxL=IGTX>6VcpY;#$S)82M(?NW8d|C;
z-yCwWx@}~)X$q%4JS^s5NL@l$V|6XQ*1KX)-T7soSAI54yj*stvA^d^pm^}fKx<(A
zp37DDN`aa_!E80hT*(<b<F$fh`6@mNF5sGt<|!Ym!2xwO#p#t9$;+XNxfc_zhD*WD
sCC2jX!$+^HYaaF+ha(%Jw*p+Ca&A|{+oOLS3;w^;^y#|Oi8<T;0Y<7^EC2ui
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d99b3c93b6c9471deb388c0693250dcbc77fc0dc
GIT binary patch
literal 1899
zc$|Gz4NwzD6b^zIsiD@N3YFUPl42B-aKQvP2sSYxh7uYBEi@`}B!@&uE?f>0pjZWe
zKvUETRvE1Si0CM&v?wYTG`3_^{0WK*Q9!M<Xj`N+5NLZ*v7OG~&hG8LH{bX6?b~nX
ziWD(Yn%8??6bgkVTPRkN^ZVprP(8_S<|AkYIr$Qj1R@qoAxsF4QiK{T83klIWI3ut
z5lwc+PiQ!W;?B^<B@hYnXjqNu*oX_mHtP%&R|}tSHX!OWlmL>^<yyS}d{*B80$Pm#
zjOWP%<%R`ls&-)xj>hK1#Hn-A)S()1egqJ1hRFarlt2KpE?sYg%>wX6T$o(D+#C>i
z0U^=^;LD^E<O*N`hNA$F9mG-xLJ+{`v!QuBJ|CJ3a04MOCy?xX7Bmm$@nLQ-@T!62
zZn!1|R*EBE?L}GwFqI$-Fo$C@nb@WvHij?fK%t?bt{7Y{i$t)D*?Iyov-C##NCYuz
zRO4C$p~duo%SMv1OhN#HBS+8~UWe5iUzLe07{`nlI1oFK^B<sG{${97_Zn>^lxWJE
z(Z;xJ1IkgNMl2IolNXmlcaaV70vtsM43ERG^pPqmQZWKEreX$QK`b9&$`Q3z?{dt3
z!H~;gnchesdNnE&3qaDt)@n5{Un~j@k&yM_N<<JO2?`a8c_MBIFE}U^5(&A&5v~|h
zXX;QrF~ZgS%N35wb&2668^yI*s3sD}bij*4!`jhv2_8L{mt4*0xrB_$<&ep6{&&?Q
zE>b_P<!j-R#%t-LdQ$H=DQwQ$Ur5OJ(k>GV<IHz1e3QL!O2(8+k2{u{ZY|yPsZGa|
zhw&%f+2HM!&-8--V%ogFiW#%-`X9TzVZga>f8{oi$!uo8w72%m-B{h!+~{qwt1w$y
zd*-pWC&L<l&l|Oi*3TH)J}`WH$JLdNXTu%21&inGysbEL@5r{H2@sX~#Az?L4xCwg
zwAtjJ(0Jfn!PkSU8cQlGME#dC%6w(gud17_$M__+m4Kz=XR(io&VT!*-@7$;s`0JD
zDwRypd$Uc2itysVOS4tePCx<Ds!P=Ia}L$fTLo*lj74XzSbS<(Plk5RR(42+=$n0V
zPnoxY{0Qf>;E5-#+`fFop3+_|bSo_QnvvOjatwtz_(f5auDCtpw@Ds5l7CcqM4wA2
z*}Vg<r~b)-Z(~0xhOcJ~d~)Tm)w0|(zjOG*aox(kvwwWPq3*(TcejgHxPAr^!AVTB
zscw13KHas4nX3FY&q0%RPF&Yzxp`tjQCI!r^{iUF=pilI-Tm~mRYdbfRlw94JHmXD
zHccukiY<;=8D3MFj4YebiLK?GDq*Jw@#E-Jp4u-jZ;9~9^A11Jne~(PrL2lZT75Ob
zEobnm7=8AipZDfuv<|w{c0%!o3#V@l-VRQTs}}iwP`c$*e0eQ(=0WSzh|}v7Rh^Kp
zg?}NvNOgjxQUp{gA;?NZ`!~s=CfB6*dJ*NhyP0l3Kbl*(3P*p?%!9c(ZdI0i<(R)N
zY6|&LG0a2P%C<<yGG#0J{i9QwrZ!XKN<5fjWgpSQO7@qCJm}0TS($a+>+r-)AN!qk
zGuda=-&1^NyT<57Jo<wZ;r`H;-d{IQ?m2XLpfF)f39at5-_tS->s|vVmPX`ntRLrf
zfF{ez3eQVmPHS;=s2oX`^Sr-r@3eGR&Cjd5p#DB@v-=&@_AjCPS+&&R8cP59)T6dA
z!=$}Oazj=>?Wyu|)|A&9?y|iZ7E?y<;@^9ZR|K>M`Pq&)b+)!{Th}A-mEQmEGlT9K
zSbVe9{;)S7^&H;XT(i=lSoPc4Sj-mVcs#h|_ZbzH%aWbHZc!8!yw$U8GX0xlyWZBJ
z{!{vW4rQMoc+hX_DW@F-B0C!IU6HbGVQOnZZD8a(Gs|i!hG)h08>d~S*PgDv&nzxb
z$J_DVdzT-jJ&sCxINf6HIG@xG;9<?BTLLXDSw1xFVphx2=;X*UOG{Uep&STlxcZB;
z?fDIzbA>bW@4+-_+g48M#-y8ltGj+VOuJZ+H)UTWjXm>>tqIwkD7}13Qtp_wfB#lT
z@I1dE)ow>O=Rse=1N)7=E7y0~&Don98a$m1cQH3l3T}_GiucA#T)#h=Bu0Exl$8Gu
DQ5go@
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a7fc3901ae69ac20299ea421aab904807801831e
GIT binary patch
literal 2382
zc$|G!dpMM7A09bo$RxWt8^aQ7%)!hs!-Sck#K^~pB#fCC6Z6i|GC73JM8wx&m(;4r
zDhee{j!_96bk3#~wjxQ$iir6}rSH4G>uR6tec$K#{jU4|J@@l_?mymaKi>`7nueMX
z2t?a!Bh6niTPa?&hMMA=a$aMdVlohW1d9WBQQ`!q5P-O|d659zi_44#`~fCgBIp8K
zAP{9s4kK6`O!pzNcwBqt62?A}%ZDrt7uQ5SlNAey;gLWz2P7lL8!sc^95xvdgr}qF
zd@2yb*|<{(1nl%>uy)3>h-`%Gdbmp>Nl}0ch?($2ZX75gC6W=J>XH=er8Eiw{{#`o
zk`bRp1=IcDRGtuk<Lw=gEHnlKClKs0I6Q%Xv4dmL7%U2{cnC-gj)W(Wu#WIA1EJVW
z$c`fU(>%ZIrHIIg7_pd7LZK2866_Nk?0Ldy6oyD7F4e$dkqQJ-Bmu?DL?kG(Sgt?=
zL@XhPFXr$-_)^S_<i(502*mOcxcskWLD82sDH?`KWb#oMdo=2QfOPslL%H0qXpz_-
zi27%=h#}zvD1Si2ix;vK7Z+u*MCOyILVzje2^l<I+;SKFVt8VnD2B&}Qv(R_b#x|+
z11=?OJ~8NYk{2ivGeH*MMI$2=8G8<gO(M`JL?;gnjfnN2U@#sIM0XmVf_1_>IuJ1w
zcdYv|m&Rkoa{*Ai%w_+V>+wzQk{Ld;0U_rnfbA*dap9j1P2zl83+3BdK6BaM)<XRz
z7o`w``oF7QPAUAewESvZMex=70I2Z0P+{z+xnK$eqT1?3b7v&>UOSu;7Z9k|I{$V)
zrl)SZBcZqMe0rq;E3OqrQPa0@=-ilPg)KEN(st!5zp&VWpl0aOahm$TwXQ|K)%#Z;
zMXXu_!>x+e-zrvq-*#Q)z0$Uh$Kcr9Wbw@7qyq1ei>Yxb*7cI{zLu7mQQ~lMQnyWM
zR%oHyL8#H5lMyYU1qMcZQ=b)jS?AajQ?ZRxwcR+fM)g*lPIxEnT?EWrFJs_=j(kq7
z@?No?)=-X3S<;$8<Fz)%SrZ!suX80Ku+nD}uC}H5NpaIt6F%7|N~<s>NZ@tx&#GEC
zi@Ma0ruKS=e#B|tXIr|u=%QDhdbs0}k9QYYhf!hot8&M&jPex?T0KYME4V91!i!VY
zt^l+Bu!F_Zzeg<|a4M<e54bs1!kYPWBBNZ`z}YmNzzYAU6u)yw-EgS7)>)^(ch~Rd
z>*sAY9QABF<gZ09%AwzoR^VZISMQMfY7Xb*+%c1b(g(=xg3b#gV+MnTdb>68!?dxG
zGRaW)@s?|oYSh8l4TET@raC44#>W#?@Szj8u&4GN$7?SjZ(cZe;XdR1)vP}FIw#of
z()naf&~Y1sQ|r3NFS#@CjQ_BH_@80FU6dcW6@l;YfwUJ9#y1tURLuquwoI{4Ha5SQ
zeiq*shvIY|n;|!e+ssQiE><c=+ADum{$-@0O!CX!AiKR?Yq}ov%VYw2w|#zHf=XDD
zavO$4HM#Q;nbc$mI%e;+fmL&lm!xKMpf2%|P69hKqs&cD>QR9*yXqYk$Nyc^d#U8j
z6t|24i@L7-JmPgp@x`GhRIQtbzYDNSE@95Uu^29fyBA7X3y216>AU>M_4k5LW#vKE
zn{z&NUgqvGK^AO?5v}Om?WH=pFM~K8@1d7Dn8}`coN#e&yfW?nQC-hhh?pyh9)~g=
z3pL~GfBb92c-39^Lym<#Hf-1_t!Bx5aovUL)oAnhnP}+1`^hwlReBEiT{A_=Yc8_Z
zER|tiT0>gU-BKB_MwFLvPB3X8%xEh-js2eMJ*ED9(9@Y}UfXKfM!~{XTyI<Ovv$<~
zTR<LCy7pnKbaMX<+XI%9xI$FxUQk8_(Spt)OZQ#kYP~&UvX^S2_-C)C^FJPFlnqO7
z{iHWv(E?i68?+jgJN?N&9p-Ca=yH)1QvJuqEl~b}p%GhojjyorjWC^9wfzHBFkr5W
zmNk|?cpDd6vA<)~W3pTJvRq)2*4m_^MR#hvJUgr3VAC7gJ&MFHJnC@f7mfXnm$pjJ
zmbb*3w9L&sIW^bRGy2vxN&e&CsZO3X*DVdT+A;IfCe@7AyZ6kH=%<;r)@RD|>Piw=
z`#vP+cGspz4wsJ;LK^yd+w<+BA&>w=85oZvG}j0(zYz4N7i=`3{&7fmG$?nrSLwa?
zS~b<5Il<)UMM|*5AwbvkOtY-0r#iK#?`hvk)ck<{$xZc1t(FbBMou%LwdI@j8PP*_
zg>kp*cY<}YrlzR;souaB6YF~W+t&{vj2G$n2E!<|UGlOA&UgE(y1IUYssOjxZcj$#
zF8!gGb{rdx>d@8#+7~^7UMe-FoIExFHKNKxFNEwOg;e_9@rQOf_+NQsirXF(^d#w}
zNm=!l?7Tr!&DW-D@_G;vg17q?&%32C7|oWgC-QHHu?gJXdMqJn60xQ9k$SD1=p1rn
zyqNf5+pu+G?D2y-5w%;-)js9pCj2xFWT&tEwO94<<{YKf8k?-}=+g}A7p`RQf+Gd?
zg#icZFR73%G+b2vl{iLl_R*L#zO8NY*epL(Iv;Kp#?^>~<h$QUo^hu?lty}ORadvw
z#XZbwpW2kHv(ouw?DMvpsVdHRrP}8r^|}R>K50Ys1}RkKhsSUO+cSZtSNBItCZXH)
zZ>6VX886gXX5POvB|oayZmdepI}=W&S!OHYLuX$ZRGNKn8w_`IYpZ!^tZn6=7FHq0
fo5S|YRrDbvd#9*bW<Mk^{o#0d_|mE<5gGph#aQOF
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -726,16 +726,20 @@ nsEditorEventListener::CleanupDragDropCa
   if (mCaret)
   {
     mCaret->EraseCaret();
     mCaret->SetCaretVisible(false);    // hide it, so that it turns off its timer
 
     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     if (presShell)
     {
+      nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
+      if (selCon) {
+        selCon->SetCaretEnabled(false);
+      }
       presShell->RestoreCaret();
     }
 
     mCaret->Terminate();
     mCaret = nullptr;
   }
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -2695,29 +2695,16 @@ public:
 
     // Shared code for GL extensions and GLX extensions.
     static bool ListHasExtension(const GLubyte *extensions,
                                  const char *extension);
 
     GLint GetMaxTextureImageSize() { return mMaxTextureImageSize; }
 
 public:
-    /**
-     * Context reset constants.
-     * These are used to determine who is guilty when a context reset
-     * happens.
-     */
-    enum ContextResetARB {
-        CONTEXT_NO_ERROR = 0,
-        CONTEXT_GUILTY_CONTEXT_RESET_ARB = 0x8253,
-        CONTEXT_INNOCENT_CONTEXT_RESET_ARB = 0x8254,
-        CONTEXT_UNKNOWN_CONTEXT_RESET_ARB = 0x8255
-    };
-
-public:
     std::map<GLuint, SharedSurface_GL*> mFBOMapping;
 
     enum {
         DebugEnabled = 1 << 0,
         DebugTrace = 1 << 1,
         DebugAbortOnError = 1 << 2
     };
 
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -753,16 +753,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mCompositionBounds);
     WriteParam(aMsg, aParam.mRootCompositionSize);
     WriteParam(aMsg, aParam.mScrollId);
     WriteParam(aMsg, aParam.mResolution);
     WriteParam(aMsg, aParam.mCumulativeResolution);
     WriteParam(aMsg, aParam.mZoom);
     WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
     WriteParam(aMsg, aParam.mMayHaveTouchListeners);
+    WriteParam(aMsg, aParam.mMayHaveTouchCaret);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mIsRoot);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
     WriteParam(aMsg, aParam.mScrollGeneration);
     aMsg->WriteBytes(aParam.mContentDescription,
                      sizeof(aParam.mContentDescription));
     WriteParam(aMsg, aParam.mTransformScale);
@@ -780,16 +781,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
             ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
             ReadParam(aMsg, aIter, &aResult->mScrollId) &&
             ReadParam(aMsg, aIter, &aResult->mResolution) &&
             ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
             ReadParam(aMsg, aIter, &aResult->mZoom) &&
             ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
             ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners) &&
+            ReadParam(aMsg, aIter, &aResult->mMayHaveTouchCaret) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mIsRoot) &&
             ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
             ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
             aMsg->ReadBytes(aIter,
                             reinterpret_cast<const char**>(&aResult->mContentDescription),
                             sizeof(aResult->mContentDescription)) &&
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -76,16 +76,17 @@ public:
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mViewport(0, 0, 0, 0)
     , mScrollableRect(0, 0, 0, 0)
     , mResolution(1)
     , mCumulativeResolution(1)
     , mTransformScale(1)
     , mDevPixelsPerCSSPixel(1)
     , mMayHaveTouchListeners(false)
+    , mMayHaveTouchCaret(false)
     , mIsRoot(false)
     , mHasScrollgrab(false)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollOffset(0, 0)
     , mZoom(1)
     , mUpdateScrollOffset(false)
     , mScrollGeneration(0)
     , mContentDescription()
@@ -108,16 +109,17 @@ public:
            mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
            mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
            mViewport.IsEqualEdges(aOther.mViewport) &&
            mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
            mResolution == aOther.mResolution &&
            mCumulativeResolution == aOther.mCumulativeResolution &&
            mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
            mMayHaveTouchListeners == aOther.mMayHaveTouchListeners &&
+           mMayHaveTouchCaret == aOther.mMayHaveTouchCaret &&
            mPresShellId == aOther.mPresShellId &&
            mIsRoot == aOther.mIsRoot &&
            mScrollId == aOther.mScrollId &&
            mScrollOffset == aOther.mScrollOffset &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset;
   }
   bool operator!=(const FrameMetrics& aOther) const
@@ -339,16 +341,19 @@ public:
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
   CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
 
   // Whether or not this frame may have touch listeners.
   bool mMayHaveTouchListeners;
 
+  // Whether or not this frame may have touch caret.
+  bool mMayHaveTouchCaret;
+
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
   // Whether or not this frame is for an element marked 'scrollgrab'.
   bool mHasScrollgrab;
 
 public:
   void SetScrollOffset(const CSSPoint& aScrollOffset)
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -642,17 +642,18 @@ nsEventStatus AsyncPanZoomController::Re
 
   // If we may have touch listeners and touch action property is enabled, we
   // enable the machinery that allows touch listeners to preventDefault any touch inputs
   // and also waits for the allowed touch behavior values to be received from the outside.
   // This should not happen unless there are actually touch listeners and touch-action property
   // enable as it introduces potentially unbounded lag because it causes a round-trip through
   // content.  Usually, if content is responding in a timely fashion, this only introduces a
   // nearly constant few hundred ms of lag.
-  if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
+  if ((mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) &&
+      aEvent.mInputType == MULTITOUCH_INPUT &&
       (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
     if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
       SetState(WAITING_CONTENT_RESPONSE);
     }
   }
 
   if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
@@ -2029,16 +2030,17 @@ gfx3DMatrix AsyncPanZoomController::GetT
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
   bool isDefault = mFrameMetrics.IsDefault();
 
   mLastContentPaintMetrics = aLayerMetrics;
   UpdateTransformScale();
 
   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
+  mFrameMetrics.mMayHaveTouchCaret = aLayerMetrics.mMayHaveTouchCaret;
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
     aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
   if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
     LogRendertraceRect(GetGuid(), "painted critical displayport", "darkgreen",
       aLayerMetrics.mCriticalDisplayPort + aLayerMetrics.GetScrollOffset());
@@ -2238,17 +2240,18 @@ void AsyncPanZoomController::ContentRece
   mTouchBlockState.mPreventDefaultSet = true;
   mTouchBlockState.mPreventDefault = aPreventDefault;
   CheckContentResponse();
 }
 
 void AsyncPanZoomController::CheckContentResponse() {
   bool canProceedToTouchState = true;
 
-  if (mFrameMetrics.mMayHaveTouchListeners) {
+  if (mFrameMetrics.mMayHaveTouchListeners ||
+      mFrameMetrics.mMayHaveTouchCaret) {
     canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
   }
 
   if (mTouchActionPropertyEnabled) {
     canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
   }
 
   if (!canProceedToTouchState) {
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -78,58 +78,23 @@ ClientTiledThebesLayer::BeginPaint()
     return;
   }
 
   mPaintData.mLowPrecisionPaintCount = 0;
   mPaintData.mPaintFinished = false;
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mCriticalDisplayPort.SetEmpty();
 
-  if (!GetBaseTransform().Is2DIntegerTranslation()) {
-    // Give up if the layer is transformed. The code below assumes that there
-    // is no transform set, and not making that assumption would cause huge
-    // complication to handle a quite rare case.
-    //
-    // FIXME The intention is to bail out of this function when there's a CSS
-    //       transform set on the layer, but unfortunately there's no way to
-    //       distinguish transforms due to scrolling from transforms due to
-    //       CSS transforms.
-    //
-    //       Because of this, there may be unintended behaviour when setting
-    //       2d CSS translations on the children of scrollable displayport
-    //       layers.
+  if (!GetBaseTransform().Is2D()) {
+    // Give up if there is a complex CSS transform on the layer. We might
+    // eventually support these but for now it's too complicated to handle
+    // given that it's a pretty rare scenario.
     return;
   }
 
-#ifdef MOZ_WIDGET_ANDROID
-  // Subframes on Fennec are not async scrollable because they have no displayport.
-  // However, the code in RenderLayer() picks up a displayport from the nearest
-  // scrollable ancestor container layer anyway, which is incorrect for Fennec. This
-  // behaviour results in the subframe getting clipped improperly and perma-blank areas
-  // while scrolling the subframe. To work around this, we detect if this layer is
-  // the primary scrollable layer and disable the tiling behaviour if it is not.
-  bool isPrimaryScrollableThebesLayer = false;
-  if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) {
-    if (GetParent() == scrollable) {
-      for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) {
-        if (child->GetType() == Layer::TYPE_THEBES) {
-          if (child == this) {
-            // |this| is the first thebes layer child of the GetPrimaryScrollableLayer()
-            isPrimaryScrollableThebesLayer = true;
-          }
-          break;
-        }
-      }
-    }
-  }
-  if (!isPrimaryScrollableThebesLayer) {
-    return;
-  }
-#endif
-
   // Get the metrics of the nearest scrollable layer and the nearest layer
   // with a displayport.
   ContainerLayer* scrollAncestor = nullptr;
   ContainerLayer* displayPortAncestor = nullptr;
   for (ContainerLayer* ancestor = GetParent(); ancestor; ancestor = ancestor->GetParent()) {
     const FrameMetrics& metrics = ancestor->GetFrameMetrics();
     if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
       scrollAncestor = ancestor;
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -512,16 +512,19 @@ ContentClientDoubleBuffered::FinalizeFra
   }
 }
 
 void
 ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
 {
   if (!mTextureClient && mFrontClient) {
     CreateBackBuffer(mFrontBufferRect);
+
+    mBufferRect = mFrontBufferRect;
+    mBufferRotation = mFrontBufferRotation;
   }
 }
 
 void
 ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
                                                    const nsIntRegion& aUpdateRegion)
 {
   DrawIterator iter;
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -150,17 +150,17 @@ void
 gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
 {
     if (!csurf)
         return;
     cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
 }
 
 already_AddRefed<gfxASurface>
-gfxASurface::Wrap (cairo_surface_t *csurf)
+gfxASurface::Wrap (cairo_surface_t *csurf, const gfxIntSize& aSize)
 {
     nsRefPtr<gfxASurface> result;
 
     /* Do we already have a wrapper for this surface? */
     result = GetSurfaceWrapper(csurf);
     if (result) {
         // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
         return result.forget();
@@ -185,29 +185,29 @@ gfxASurface::Wrap (cairo_surface_t *csur
 #endif
 #ifdef MOZ_X11
     else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
         result = new gfxXlibSurface(csurf);
     }
 #endif
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
-        result = new gfxQuartzSurface(csurf);
+        result = new gfxQuartzSurface(csurf, aSize);
     }
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
         result = new gfxQuartzImageSurface(csurf);
     }
 #endif
 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
     else if (stype == CAIRO_SURFACE_TYPE_QT) {
         result = new gfxQPainterSurface(csurf);
     }
 #endif
     else {
-        result = new gfxUnknownSurface(csurf);
+        result = new gfxUnknownSurface(csurf, aSize);
     }
 
     // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
 
     return result.forget();
 }
 
 void
@@ -332,17 +332,17 @@ gfxASurface::CreateSimilarSurface(gfxCon
     cairo_surface_t *surface =
         cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
                                      aSize.width, aSize.height);
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 already_AddRefed<gfxImageSurface>
 gfxASurface::GetAsReadableARGB32ImageSurface()
 {
     nsRefPtr<gfxImageSurface> imgSurface = GetAsImageSurface();
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -45,17 +45,17 @@ public:
     virtual nsrefcnt Release(void);
 #endif
 
 public:
 
     /** Wrap the given cairo surface and return a gfxASurface for it.
      * This adds a reference to csurf (owned by the returned gfxASurface).
      */
-    static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf);
+    static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf, const gfxIntSize& aSize = gfxIntSize(-1, -1));
 
     /*** this DOES NOT addref the surface */
     cairo_surface_t *CairoSurface() {
         return mSurface;
     }
 
     gfxSurfaceType GetType() const;
 
@@ -269,23 +269,22 @@ protected:
     bool mAllowUseAsSource;
 };
 
 /**
  * An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
  */
 class gfxUnknownSurface : public gfxASurface {
 public:
-    gfxUnknownSurface(cairo_surface_t *surf)
-        : mSize(-1, -1)
+    gfxUnknownSurface(cairo_surface_t *surf, const gfxIntSize& aSize)
+        : mSize(aSize)
     {
         Init(surf, true);
     }
 
     virtual ~gfxUnknownSurface() { }
     virtual const nsIntSize GetSize() const { return mSize; }
-    void SetSize(const nsIntSize& aSize) { mSize = aSize; }
 
 private:
     nsIntSize mSize;
 };
 
 #endif /* GFX_ASURFACE_H */
--- a/gfx/thebes/gfxQuartzSurface.cpp
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -84,18 +84,19 @@ gfxQuartzSurface::gfxQuartzSurface(CGCon
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(cairo_surface_t *csurf,
+                                   const gfxIntSize& aSize,
                                    bool aForPrinting) :
-    mSize(-1.0, -1.0), mForPrinting(aForPrinting)
+    mSize(aSize), mForPrinting(aForPrinting)
 {
     mCGContext = cairo_quartz_surface_get_cg_context (csurf);
     CGContextRetain (mCGContext);
 
     Init(csurf, true);
 }
 
 gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
@@ -156,17 +157,17 @@ gfxQuartzSurface::CreateSimilarSurface(g
     cairo_surface_t *surface =
         cairo_quartz_surface_create_cg_layer(mSurface, (cairo_content_t)aType,
                                              aSize.width, aSize.height);
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 CGContextRef
 gfxQuartzSurface::GetCGContextWithClip(gfxContext *ctx)
 {
 	return cairo_quartz_get_cg_context_with_clip(ctx->GetCairo());
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -15,17 +15,17 @@
 class gfxContext;
 class gfxImageSurface;
 
 class gfxQuartzSurface : public gfxASurface {
 public:
     gfxQuartzSurface(const gfxSize& size, gfxImageFormat format, bool aForPrinting = false);
     gfxQuartzSurface(CGContextRef context, const gfxSize& size, bool aForPrinting = false);
     gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
-    gfxQuartzSurface(cairo_surface_t *csurf, bool aForPrinting = false);
+    gfxQuartzSurface(cairo_surface_t *csurf, const gfxIntSize& aSize, bool aForPrinting = false);
     gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
     gfxQuartzSurface(unsigned char *data, const gfxIntSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
 
     virtual ~gfxQuartzSurface();
 
     virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
                                                                const gfxIntSize& aSize);
 
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -143,22 +143,17 @@ gfxWindowsSurface::CreateSimilarSurface(
                                        aSize.width, aSize.height);
     }
 
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
         return nullptr;
     }
 
-    nsRefPtr<gfxASurface> result = Wrap(surface);
-    if (mForPrinting) {
-      MOZ_ASSERT(result->GetType() == gfxSurfaceType::Recording);
-      gfxUnknownSurface *unknown = static_cast<gfxUnknownSurface*>(result.get());
-      unknown->SetSize(aSize);
-    }
+    nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
     cairo_surface_destroy(surface);
     return result.forget();
 }
 
 gfxWindowsSurface::~gfxWindowsSurface()
 {
     if (mOwnsDC) {
         if (mWnd)
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -369,25 +369,34 @@ extern JS_FRIEND_API(void)
 ShrinkGCBuffers(JSRuntime *rt);
 
 /*
  * Assert if a GC occurs while this class is live. This class does not disable
  * the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertOnGC)
 {
+#ifdef DEBUG
     JSRuntime *runtime;
     size_t gcNumber;
 
   public:
     AutoAssertOnGC();
     explicit AutoAssertOnGC(JSRuntime *rt);
     ~AutoAssertOnGC();
 
     static void VerifyIsSafeToGC(JSRuntime *rt);
+#else
+  public:
+    AutoAssertOnGC() {}
+    explicit AutoAssertOnGC(JSRuntime *rt) {}
+    ~AutoAssertOnGC() {}
+
+    static void VerifyIsSafeToGC(JSRuntime *rt) {}
+#endif
 };
 
 /*
  * Disable the static rooting hazard analysis in the live region, but assert if
  * any GC occurs while this guard object is live. This is most useful to help
  * the exact rooting hazard analysis in complex regions, since it cannot
  * understand dataflow.
  *
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -14,20 +14,20 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsexn.h"
 #include "jsnum.h"
-#include "jsworkers.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "js/CharacterEncoding.h"
+#include "vm/HelperThreads.h"
 #include "vm/Keywords.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::unicode;
 
 using mozilla::Maybe;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -3,19 +3,19 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_GCInternals_h
 #define gc_GCInternals_h
 
 #include "jscntxt.h"
-#include "jsworkers.h"
 
 #include "gc/Zone.h"
+#include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 
 namespace js {
 namespace gc {
 
 void
 MarkPersistentRootedChains(JSTracer *trc);
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -532,23 +532,25 @@ class GCRuntime
     size_t                systemPageSize;
 
     /* The OS allocation granularity may not match the page size. */
     size_t                systemAllocGranularity;
 
     /* Strong references on scripts held for PCCount profiling API. */
     js::ScriptAndCountsVector *scriptAndCountsVector;
 
+#ifdef DEBUG
     /*
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
+#endif
 
   private:
     /* Always preserve JIT code during GCs, for testing. */
     bool                  alwaysPreserveCode;
 
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1018620.js
@@ -0,0 +1,5 @@
+
+String.prototype.search = evalcx('').String.prototype.search;
+''.search(/()/);
+gcPreserveCode();
+gc(this);
rename from js/src/jit-test/tests/basic/latin1.js
rename to js/src/jit-test/tests/latin1/basic.js
rename from js/src/jit-test/tests/basic/latin1-indexOf.js
rename to js/src/jit-test/tests/latin1/indexOf.js
rename from js/src/jit-test/tests/basic/latin1-indexing.js
rename to js/src/jit-test/tests/latin1/indexing.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/startsWith-endsWith.js
@@ -0,0 +1,65 @@
+function testStartsWith() {
+    var s1 = toLatin1("abc\u0099def");
+    var s2 = toLatin1("abc\u0099d");
+    var s3 = toLatin1("abc\u0098d");
+    var s4 = toLatin1("bc\u0099");
+
+    // Latin1 + Latin1
+    assertEq(s1.startsWith(s2), true);
+    assertEq(s1.startsWith(s3), false);
+    assertEq(s1.startsWith(s4), false);
+    assertEq(s1.startsWith(s4, 1), true);
+    assertEq(s1.startsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.startsWith("abc\u0099\u1200".slice(0, -1)), true);
+    assertEq(s1.startsWith("abc\u0099e\u1200".slice(0, -1)), false);
+    assertEq(s1.startsWith("bc\u0099\u1200".slice(0, -1), 1), true);
+    assertEq(s1.startsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "abc\u0099de\u1200";
+    assertEq(s5.startsWith(s1), false);
+    assertEq(s5.startsWith(s2), true);
+    assertEq(s5.startsWith(s4), false);
+    assertEq(s5.startsWith(s4, 1), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.startsWith(s5), true);
+    assertEq(s5.startsWith("\u1200"), false);
+    assertEq(s5.startsWith("\u1200", 6), true);
+}
+testStartsWith();
+
+function testEndsWith() {
+    var s1 = toLatin1("zabc\u0099defg");
+    var s2 = toLatin1("\u0099defg");
+    var s3 = toLatin1("\u0098defg");
+    var s4 = toLatin1("zabc\u0099def");
+
+    // Latin1 + Latin1
+    assertEq(s1.endsWith(s2), true);
+    assertEq(s1.endsWith(s3), false);
+    assertEq(s1.endsWith(s4), false);
+    assertEq(s1.endsWith(s4, 8), true);
+    assertEq(s1.endsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.endsWith("abc\u0099defg\u1200".slice(0, -1)), true);
+    assertEq(s1.endsWith("\u1100efg\u1200".slice(0, -1)), false);
+    assertEq(s1.endsWith("bc\u0099\u1200".slice(0, -1), 5), true);
+    assertEq(s1.endsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "\u1200zabc\u0099defg";
+    assertEq(s5.endsWith(s1), true);
+    assertEq(s5.endsWith(s2), true);
+    assertEq(s5.endsWith(s4), false);
+    assertEq(s5.endsWith(s4, 9), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.endsWith(s5), true);
+    assertEq(s5.endsWith("\u1200"), false);
+    assertEq(s5.endsWith("\u1200za", 3), true);
+}
+testEndsWith();
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -9,31 +9,31 @@
 #include "mozilla/Move.h"
 
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
 #include "jsmath.h"
 #include "jsprf.h"
-#include "jsworkers.h"
 #include "prmjtime.h"
 
 #include "assembler/assembler/MacroAssembler.h"
 #include "frontend/Parser.h"
 #include "jit/AsmJSLink.h"
 #include "jit/AsmJSModule.h"
 #include "jit/AsmJSSignalHandlers.h"
 #include "jit/CodeGenerator.h"
 #include "jit/CompileWrappers.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8652,20 +8652,20 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall
                 FloatRegister fr = ToFloatRegister(a);
                 int srcId = fr.code() * 2;
                 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId+1));
             }
         }
     }
 #endif
 
-   if (mir->spIncrement())
+    if (mir->spIncrement())
         masm.freeStack(mir->spIncrement());
 
-   JS_ASSERT((AlignmentAtPrologue +  masm.framePushed()) % StackAlignment == 0);
+    JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
 
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
     masm.assumeUnreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -6,17 +6,16 @@
 
 #include "jit/Ion.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsprf.h"
-#include "jsworkers.h"
 
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
@@ -36,16 +35,17 @@
 #include "jit/Lowering.h"
 #include "jit/ParallelSafetyAnalysis.h"
 #include "jit/PerfSpewer.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/StupidAllocator.h"
 #include "jit/UnreachableCodeElimination.h"
 #include "jit/ValueNumbering.h"
 #include "vm/ForkJoin.h"
+#include "vm/HelperThreads.h"
 #include "vm/TraceLogging.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "jit/ExecutionMode-inl.h"
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -3,19 +3,18 @@
  * 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/. */
 
 #ifdef DEBUG
 
 #include "jit/IonSpewer.h"
 
-#include "jsworkers.h"
-
 #include "jit/Ion.h"
+#include "vm/HelperThreads.h"
 
 #ifndef ION_SPEW_DIR
 # if defined(_WIN32)
 #  define ION_SPEW_DIR ""
 # elif defined(__ANDROID__)
 #  define ION_SPEW_DIR "/data/local/tmp/"
 # else
 #  define ION_SPEW_DIR "/tmp/"
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1549,17 +1549,17 @@ class LIRGraph
     }
     // Return the localSlotCount() value rounded up so that it satisfies the
     // platform stack alignment requirement, and so that it's a multiple of
     // the number of slots per Value.
     uint32_t paddedLocalSlotCount() const {
         // Round to StackAlignment, but also round to at least sizeof(Value) in
         // case that's greater, because StackOffsetOfPassedArg rounds argument
         // slots to 8-byte boundaries.
-        size_t Alignment = Max(sizeof(StackAlignment), sizeof(Value));
+        size_t Alignment = Max(size_t(StackAlignment), sizeof(Value));
         return AlignBytes(localSlotCount(), Alignment);
     }
     size_t paddedLocalSlotsSize() const {
         return paddedLocalSlotCount();
     }
     void setArgumentSlotCount(uint32_t argumentSlotCount) {
         argumentSlotCount_ = argumentSlotCount;
     }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -113,24 +113,27 @@ class MIRGenerator
         maxAsmJSStackArgBytes_ = n;
     }
     void setPerformsCall() {
         performsCall_ = true;
     }
     bool performsCall() const {
         return performsCall_;
     }
+    void setNeedsInitialStackAlignment() {
+        needsInitialStackAlignment_ = true;
+    }
+    bool needsInitialStackAlignment() const {
+        JS_ASSERT(compilingAsmJS());
+        return needsInitialStackAlignment_;
+    }
     void setPerformsAsmJSCall() {
         JS_ASSERT(compilingAsmJS());
         setPerformsCall();
-        performsAsmJSCall_ = true;
-    }
-    bool performsAsmJSCall() const {
-        JS_ASSERT(compilingAsmJS());
-        return performsAsmJSCall_;
+        setNeedsInitialStackAlignment();
     }
     void noteMinAsmJSHeapLength(uint32_t len) {
         minAsmJSHeapLength_ = len;
     }
     uint32_t minAsmJSHeapLength() const {
         return minAsmJSHeapLength_;
     }
 
@@ -149,17 +152,17 @@ class MIRGenerator
     uint32_t nslots_;
     MIRGraph *graph_;
     AbortReason abortReason_;
     bool error_;
     mozilla::Atomic<bool, mozilla::Relaxed> cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
     bool performsCall_;
-    bool performsAsmJSCall_;
+    bool needsInitialStackAlignment_;
     uint32_t minAsmJSHeapLength_;
 
     // Keep track of whether frame arguments are modified during execution.
     // RegAlloc needs to know this as spilling values back to their register
     // slots is not compatible with that.
     bool modifiesFrameArguments_;
 
 #if defined(JS_ION_PERF)
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -24,17 +24,17 @@ MIRGenerator::MIRGenerator(CompileCompar
     optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     abortReason_(AbortReason_NoAbort),
     error_(false),
     cancelBuild_(false),
     maxAsmJSStackArgBytes_(0),
     performsCall_(false),
-    performsAsmJSCall_(false),
+    needsInitialStackAlignment_(false),
     minAsmJSHeapLength_(AsmJSAllocationGranularity),
     modifiesFrameArguments_(false),
     options(options)
 { }
 
 bool
 MIRGenerator::abortFmt(const char *message, va_list ap)
 {
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -6,22 +6,21 @@
 
 #ifndef jit_shared_Assembler_shared_h
 #define jit_shared_Assembler_shared_h
 
 #include "mozilla/PodOperations.h"
 
 #include <limits.h>
 
-#include "jsworkers.h"
-
 #include "jit/IonAllocPolicy.h"
 #include "jit/Label.h"
 #include "jit/Registers.h"
 #include "jit/RegisterSets.h"
+#include "vm/HelperThreads.h"
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
 // JS_SMALL_BRANCH means the range on a branch instruction
 // is smaller than the whole address space
 #    define JS_SMALL_BRANCH
 #endif
 namespace js {
 namespace jit {
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -47,17 +47,18 @@ CodeGeneratorShared::CodeGeneratorShared
     deoptTable_(nullptr),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
     sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
     osrEntryOffset_(0),
     skipArgCheckEntryOffset_(0),
-    frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize())
+    frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),
+    frameInitialAdjustment_(0)
 {
     if (!gen->compilingAsmJS())
         masm.setInstrumentation(&sps_);
 
     // Since asm.js uses the system ABI which does not necessarily use a
     // regular array where all slots are sizeof(Value), it maintains the max
     // argument stack depth separately.
     if (gen->compilingAsmJS()) {
@@ -67,20 +68,22 @@ CodeGeneratorShared::CodeGeneratorShared
         // An MAsmJSCall does not align the stack pointer at calls sites but instead
         // relies on the a priori stack adjustment (in the prologue) on platforms
         // (like x64) which require the stack to be aligned.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         bool forceAlign = true;
 #else
         bool forceAlign = false;
 #endif
-        if (gen->performsAsmJSCall() || forceAlign) {
+        if (gen->needsInitialStackAlignment() || forceAlign) {
             unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
-            if (unsigned rem = alignmentAtCall % StackAlignment)
-                frameDepth_ += StackAlignment - rem;
+            if (unsigned rem = alignmentAtCall % StackAlignment) {
+                frameInitialAdjustment_ = StackAlignment - rem;
+                frameDepth_ += frameInitialAdjustment_;
+            }
         }
 
         // FrameSizeClass is only used for bailing, which cannot happen in
         // asm.js code.
         frameClass_ = FrameSizeClass::None();
     } else {
         frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
     }
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -139,16 +139,21 @@ class CodeGeneratorShared : public LInst
     void dropArguments(unsigned argc);
 
   protected:
     // The initial size of the frame in bytes. These are bytes beyond the
     // constant header present for every Ion frame, used for pre-determined
     // spills.
     int32_t frameDepth_;
 
+    // In some cases, we force stack alignment to platform boundaries, see
+    // also CodeGeneratorShared constructor. This value records the adjustment
+    // we've done.
+    int32_t frameInitialAdjustment_;
+
     // Frame class this frame's size falls into (see IonFrame.h).
     FrameSizeClass frameClass_;
 
     // For arguments to the current function.
     inline int32_t ArgToStackOffset(int32_t slot) const {
         return masm.framePushed() +
                (gen->compilingAsmJS() ? NativeFrameSize : sizeof(IonJSFrameLayout)) +
                slot;
@@ -156,28 +161,28 @@ class CodeGeneratorShared : public LInst
 
     // For the callee of the current function.
     inline int32_t CalleeStackOffset() const {
         return masm.framePushed() + IonJSFrameLayout::offsetOfCalleeToken();
     }
 
     inline int32_t SlotToStackOffset(int32_t slot) const {
         JS_ASSERT(slot > 0 && slot <= int32_t(graph.localSlotCount()));
-        int32_t offset = masm.framePushed() - slot;
+        int32_t offset = masm.framePushed() - frameInitialAdjustment_ - slot;
         JS_ASSERT(offset >= 0);
         return offset;
     }
     inline int32_t StackOffsetToSlot(int32_t offset) const {
         // See: SlotToStackOffset. This is used to convert pushed arguments
         // to a slot index that safepoints can use.
         //
-        // offset = framePushed - slot
-        // offset + slot = framePushed
-        // slot = framePushed - offset
-        return masm.framePushed() - offset;
+        // offset = framePushed - frameInitialAdjustment - slot
+        // offset + slot = framePushed - frameInitialAdjustment
+        // slot = framePushed - frameInitialAdjustement - offset
+        return masm.framePushed() - frameInitialAdjustment_ - offset;
     }
 
     // For argument construction for calls. Argslots are Value-sized.
     inline int32_t StackOffsetOfPassedArg(int32_t slot) const {
         // A slot of 0 is permitted only to calculate %esp offset for calls.
         JS_ASSERT(slot >= 0 && slot <= int32_t(graph.argumentSlotCount()));
         int32_t offset = masm.framePushed() -
                        graph.paddedLocalSlotsSize() -
--- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp
+++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp
@@ -236,30 +236,27 @@ MoveEmitterX86::breakCycle(const MoveOpe
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
             masm.loadDouble(toAddress(to), ScratchFloatReg);
             masm.storeDouble(ScratchFloatReg, cycleSlot());
         } else {
             masm.storeDouble(to.floatReg(), cycleSlot());
         }
         break;
+      case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
-      case MoveOp::INT32:
         // x64 can't pop to a 32-bit destination, so don't push.
         if (to.isMemory()) {
             masm.load32(toAddress(to), ScratchReg);
             masm.store32(ScratchReg, cycleSlot());
         } else {
             masm.store32(to.reg(), cycleSlot());
         }
         break;
 #endif
-#ifndef JS_CODEGEN_X64
-      case MoveOp::INT32:
-#endif
       case MoveOp::GENERAL:
         masm.Push(toOperand(to));
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected move type");
     }
 }
 
@@ -288,32 +285,29 @@ MoveEmitterX86::completeCycle(const Move
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(double));
         if (to.isMemory()) {
             masm.loadDouble(cycleSlot(), ScratchFloatReg);
             masm.storeDouble(ScratchFloatReg, toAddress(to));
         } else {
             masm.loadDouble(cycleSlot(), to.floatReg());
         }
         break;
+      case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
-      case MoveOp::INT32:
         JS_ASSERT(pushedAtCycle_ != -1);
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(int32_t));
         // x64 can't pop to a 32-bit destination.
         if (to.isMemory()) {
             masm.load32(cycleSlot(), ScratchReg);
             masm.store32(ScratchReg, toAddress(to));
         } else {
             masm.load32(cycleSlot(), to.reg());
         }
         break;
 #endif
-#ifndef JS_CODEGEN_X64
-      case MoveOp::INT32:
-#endif
       case MoveOp::GENERAL:
         JS_ASSERT(masm.framePushed() - pushedAtStart_ >= sizeof(intptr_t));
         masm.Pop(toPopOperand(to));
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected move type");
     }
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -35,19 +35,16 @@
 #include "jsprf.h"
 #include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
-#ifdef JS_THREADSAFE
-#include "jsworkers.h"
-#endif
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
 #ifdef ENABLE_BINARYDATA
@@ -65,16 +62,17 @@
 #include "js/StructuredClone.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/ErrorObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -32,24 +32,24 @@
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswatchpoint.h"
-#include "jsworkers.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/Ion.h"
 #endif
 #include "js/CharacterEncoding.h"
 #include "js/OldDebugAPI.h"
+#include "vm/HelperThreads.h"
 #include "vm/Shape.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -6,21 +6,21 @@
 
 #ifndef jscntxtinlines_h
 #define jscntxtinlines_h
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "jsiter.h"
-#include "jsworkers.h"
 
 #include "builtin/Object.h"
 #include "jit/IonFrames.h"
 #include "vm/ForkJoin.h"
+#include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 #ifdef JS_CRASH_DIAGNOSTICS
 class CompartmentChecker
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1110,17 +1110,19 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
 #endif
     validate(true),
     fullCompartmentChecks(false),
     gcCallback(nullptr),
     sliceCallback(nullptr),
     mallocBytes(0),
     mallocGCTriggered(false),
     scriptAndCountsVector(nullptr),
+#ifdef DEBUG
     inUnsafeRegion(0),
+#endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
     helperState(rt)
 {
@@ -2432,39 +2434,33 @@ void
 GCHelperState::work()
 {
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
 
     JS_ASSERT(!thread);
     thread = PR_GetCurrentThread();
 
+    TraceLogger *logger = TraceLoggerForCurrentThread();
+
     switch (state()) {
 
       case IDLE:
         MOZ_ASSUME_UNREACHABLE("GC helper triggered on idle state");
         break;
 
       case SWEEPING: {
-#if JS_TRACE_LOGGING
-        AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
-                            TraceLogging::GC_SWEEPING_START,
-                            TraceLogging::GC_SWEEPING_STOP);
-#endif
+        AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
         doSweep();
         JS_ASSERT(state() == SWEEPING);
         break;
       }
 
       case ALLOCATING: {
-#if JS_TRACE_LOGGING
-        AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::GC_BACKGROUND),
-                            TraceLogging::GC_ALLOCATING_START,
-                            TraceLogging::GC_ALLOCATING_STOP);
-#endif
+        AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
         do {
             Chunk *chunk;
             {
                 AutoUnlockGC unlock(rt);
                 chunk = Chunk::allocate(rt);
             }
 
             /* OOM stops the background allocation. */
@@ -5615,16 +5611,17 @@ JS::GetGCNumber()
 {
     JSRuntime *rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     if (!rt)
         return 0;
     return rt->gc.number;
 }
 #endif
 
+#ifdef DEBUG
 JS::AutoAssertOnGC::AutoAssertOnGC()
   : runtime(nullptr), gcNumber(0)
 {
     js::PerThreadData *data = js::TlsPerThreadData.get();
     if (data) {
         /*
          * GC's from off-thread will always assert, so off-thread is implicitly
          * AutoAssertOnGC. We still need to allow AutoAssertOnGC to be used in
@@ -5660,8 +5657,9 @@ JS::AutoAssertOnGC::~AutoAssertOnGC()
 }
 
 /* static */ void
 JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
 {
     if (rt->gc.inUnsafeRegion > 0)
         MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
 }
+#endif
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -13,27 +13,27 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jshashutil.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
-#include "jsworkers.h"
 #include "prmjtime.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitCompartment.h"
 #endif
 #include "js/MemoryMetrics.h"
+#include "vm/HelperThreads.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1556,16 +1556,49 @@ str_lastIndexOf(JSContext *cx, unsigned 
         else
             res = LastIndexOfImpl(textChars, textLen, pat->twoByteChars(nogc), patLen, start);
     }
 
     args.rval().setInt32(res);
     return true;
 }
 
+static bool
+EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
+{
+    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
+        if (jschar(*s1) != *s2)
+            return false;
+    }
+    return true;
+}
+
+static bool
+HasSubstringAt(JSLinearString *text, JSLinearString *pat, size_t start)
+{
+    MOZ_ASSERT(start + pat->length() <= text->length());
+
+    size_t patLen = pat->length();
+
+    AutoCheckCannotGC nogc;
+    if (text->hasLatin1Chars()) {
+        const Latin1Char *textChars = text->latin1Chars(nogc) + start;
+        if (pat->hasLatin1Chars())
+            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
+
+        return EqualCharsLatin1TwoByte(textChars, pat->twoByteChars(nogc), patLen);
+    }
+
+    const jschar *textChars = text->twoByteChars(nogc) + start;
+    if (pat->hasTwoByteChars())
+        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
+
+    return EqualCharsLatin1TwoByte(pat->latin1Chars(nogc), textChars, patLen);
+}
+
 /* ES6 20131108 draft 21.1.3.18. */
 static bool
 str_startsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
@@ -1595,35 +1628,35 @@ str_startsWith(JSContext *cx, unsigned a
             if (!ToInteger(cx, args[1], &d))
                 return false;
             pos = uint32_t(Min(Max(d, 0.0), double(UINT32_MAX)));
         }
     }
 
     // Step 9
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Step 10
     uint32_t start = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 12
     if (searchLen + start < searchLen || searchLen + start > textLen) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Steps 13 and 14
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 /* ES6 20131108 draft 21.1.3.7. */
 static bool
 str_endsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1642,19 +1675,16 @@ str_endsWith(JSContext *cx, unsigned arg
 
     // Steps 5 and 6
     Rooted<JSLinearString *> searchStr(cx, ArgToRootedString(cx, args, 0));
     if (!searchStr)
         return false;
 
     // Step 7
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Steps 8 and 9
     uint32_t pos = textLen;
     if (args.hasDefined(1)) {
         if (args[1].isInt32()) {
             int i = args[1].toInt32();
             pos = (i < 0) ? 0U : uint32_t(i);
         } else {
@@ -1665,29 +1695,32 @@ str_endsWith(JSContext *cx, unsigned arg
         }
     }
 
     // Step 10
     uint32_t end = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 13 (reordered)
     if (searchLen > end) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Step 12
     uint32_t start = end - searchLen;
 
     // Steps 14 and 15
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 static bool
 js_TrimString(JSContext *cx, Value *vp, bool trimLeft, bool trimRight)
 {
     CallReceiver call = CallReceiverFromVp(vp);
     RootedString str(cx, ThisToStringForStringProto(cx, call));
@@ -4241,26 +4274,16 @@ js::ValueToSource(JSContext *cx, HandleV
 
 JSString *
 js::StringToSource(JSContext *cx, JSString *str)
 {
     return js_QuoteString(cx, str, '"');
 }
 
 static bool
-EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
-{
-    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (jschar(*s1) != *s2)
-            return false;
-    }
-    return true;
-}
-
-static bool
 EqualChars(JSLinearString *str1, JSLinearString *str2)
 {
     MOZ_ASSERT(str1->length() == str2->length());
 
     size_t len = str1->length();
 
     AutoCheckCannotGC nogc;
     if (str1->hasTwoByteChars()) {
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -562,18 +562,26 @@ CrossCompartmentWrapper::fun_toString(JS
     if (!cx->compartment()->wrap(cx, str.address()))
         return nullptr;
     return str;
 }
 
 bool
 CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g)
 {
-    AutoCompartment call(cx, wrappedObject(wrapper));
-    return Wrapper::regexp_toShared(cx, wrapper, g);
+    RegExpGuard wrapperGuard(cx);
+    {
+        AutoCompartment call(cx, wrappedObject(wrapper));
+        if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
+            return false;
+    }
+
+    // Get an equivalent RegExpShared associated with the current compartment.
+    RegExpShared *re = wrapperGuard.re();
+    return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
 }
 
 bool
 CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
                                       MutableHandleValue vp)
 {
     PIERCE(cx, wrapper,
            NOTHING,
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -145,31 +145,31 @@ UNIFIED_SOURCES += [
     'jsprf.cpp',
     'jspropertytree.cpp',
     'jsproxy.cpp',
     'jsreflect.cpp',
     'jsscript.cpp',
     'jsstr.cpp',
     'jswatchpoint.cpp',
     'jsweakmap.cpp',
-    'jsworkers.cpp',
     'jswrapper.cpp',
     'perf/jsperf.cpp',
     'prmjtime.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/ErrorObject.cpp',
     'vm/ForkJoin.cpp',
     'vm/GlobalObject.cpp',
+    'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Interpreter.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/Monitor.cpp',
     'vm/ObjectImpl.cpp',
     'vm/OldDebugAPI.cpp',
     'vm/PIC.cpp',
     'vm/Probes.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -46,30 +46,30 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
-#include "jsworkers.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Ion.h"
 #include "js/OldDebugAPI.h"
 #include "js/StructuredClone.h"
 #include "perf/jsperf.h"
 #include "shell/jsheaptools.h"
 #include "shell/jsoptparse.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/Monitor.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 
@@ -6015,22 +6015,16 @@ SetRuntimeOptions(JSRuntime *rt, const O
 
     jsCacheDir = op.getStringOption("js-cache");
     if (jsCacheDir) {
         if (op.getBoolOption("js-cache-per-process"))
             jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid());
         jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir);
     }
 
-#ifdef JS_THREADSAFE
-    int32_t threadCount = op.getIntOption("thread-count");
-    if (threadCount >= 0)
-        SetFakeCPUCount(threadCount);
-#endif /* JS_THREADSAFE */
-
 #ifdef DEBUG
     dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
 #endif
 
     return true;
 }
 
 static int
@@ -6272,16 +6266,24 @@ main(int argc, char **argv, char **envp)
     if (op.getBoolOption("no-sse4")) {
         JSC::MacroAssembler::SetSSE4Disabled();
         PropagateFlagToNestedShells("--no-sse4");
     }
 #endif
 
 #endif // DEBUG
 
+#ifdef JS_THREADSAFE
+    // The fake thread count must be set before initializing the Runtime,
+    // which spins up the thread pool.
+    int32_t threadCount = op.getIntOption("thread-count");
+    if (threadCount >= 0)
+        SetFakeCPUCount(threadCount);
+#endif /* JS_THREADSAFE */
+
     // Start the engine.
     if (!JS_Init())
         return 1;
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     rt = JS_NewRuntime(32L * 1024L * 1024L);
     if (!rt)
         return 1;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -9,27 +9,27 @@
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "json.h"
 #include "jsprototypes.h"
 #include "jsweakmap.h"
-#include "jsworkers.h"
 
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/PIC.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WeakMapObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
rename from js/src/jsworkers.cpp
rename to js/src/vm/HelperThreads.cpp
--- a/js/src/jsworkers.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jsworkers.h"
+#include "vm/HelperThreads.h"
 
 #ifdef JS_THREADSAFE
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnativestack.h"
 #include "prmjtime.h"
 
rename from js/src/jsworkers.h
rename to js/src/vm/HelperThreads.h
--- a/js/src/jsworkers.h
+++ b/js/src/vm/HelperThreads.h
@@ -5,18 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Definitions for managing off-main-thread work using a process wide list
  * of worklist items and pool of threads. Worklist items are engine internal,
  * and are distinct from e.g. web workers.
  */
 
-#ifndef jsworkers_h
-#define jsworkers_h
+#ifndef vm_HelperThreads_h
+#define vm_HelperThreads_h
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 #include "jslock.h"
 
 #include "frontend/TokenStream.h"
@@ -502,9 +502,9 @@ struct SourceCompressionTask
     bool complete();
     void abort() { abort_ = true; }
     bool active() const { return !!ss; }
     ScriptSource *source() { return ss; }
 };
 
 } /* namespace js */
 
-#endif /* jsworkers_h */
+#endif /* vm_HelperThreads_h */
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -497,24 +497,17 @@ class RegExpObject : public JSObject
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
 ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut);
 
-/*
- * Assuming ObjectClassIs(obj, ESClass_RegExp), return obj's RegExpShared.
- *
- * Beware: this RegExpShared can be owned by a compartment other than
- * cx->compartment. Normal RegExpGuard (which is necessary anyways)
- * will protect the object but it is important not to assign the return value
- * to be the private of any RegExpObject.
- */
+/* Assuming ObjectClassIs(obj, ESClass_RegExp), return a RegExpShared for obj. */
 inline bool
 RegExpToShared(JSContext *cx, HandleObject obj, RegExpGuard *g)
 {
     if (obj->is<RegExpObject>())
         return obj->as<RegExpObject>().getShared(cx, g);
     return Proxy::regexp_toShared(cx, obj, g);
 }
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -633,16 +633,17 @@ class JSLinearString : public JSString
     MOZ_ALWAYS_INLINE
     const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const;
 
     JS::TwoByteChars range() const {
         JS_ASSERT(JSString::isLinear());
         return JS::TwoByteChars(chars(), length());
     }
 
+    MOZ_ALWAYS_INLINE
     jschar latin1OrTwoByteChar(size_t index) const {
         MOZ_ASSERT(JSString::isLinear());
         MOZ_ASSERT(index < length());
         JS::AutoCheckCannotGC nogc;
         return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index];
     }
 
     /* Temporary, unsafe helper function for bug 998392. Don't use for anything else. */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -498,29 +498,29 @@ Scriptability::SetDocShellAllowsScript(b
 /* static */
 Scriptability&
 Scriptability::Get(JSObject *aScope)
 {
     return EnsureCompartmentPrivate(aScope)->scriptability;
 }
 
 bool
-IsXBLScope(JSCompartment *compartment)
+IsContentXBLScope(JSCompartment *compartment)
 {
     // We always eagerly create compartment privates for XBL scopes.
     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
     if (!priv || !priv->scope)
         return false;
-    return priv->scope->IsXBLScope();
+    return priv->scope->IsContentXBLScope();
 }
 
 bool
-IsInXBLScope(JSObject *obj)
+IsInContentXBLScope(JSObject *obj)
 {
-    return IsXBLScope(js::GetObjectCompartment(obj));
+    return IsContentXBLScope(js::GetObjectCompartment(obj));
 }
 
 bool
 IsUniversalXPConnectEnabled(JSCompartment *compartment)
 {
     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
     if (!priv)
         return false;
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -68,17 +68,17 @@ RemoteXULForbidsXBLScope(nsIPrincipal *a
 
 XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
                                              JS::HandleObject aGlobal)
       : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
-        mIsXBLScope(false)
+        mIsContentXBLScope(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
         DebugOnly<const js::Class*> clasp = js::GetObjectClass(aGlobal);
         MOZ_ASSERT(clasp->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
                                    JSCLASS_HAS_PRIVATE) ||
                    mozilla::dom::IsDOMClass(clasp));
@@ -96,28 +96,28 @@ XPCWrappedNativeScope::XPCWrappedNativeS
     // Attach ourselves to the compartment private.
     CompartmentPrivate *priv = EnsureCompartmentPrivate(aGlobal);
     priv->scope = this;
 
     // Determine whether we would allow an XBL scope in this situation.
     // In addition to being pref-controlled, we also disable XBL scopes for
     // remote XUL domains, _except_ if we have an additional pref override set.
     nsIPrincipal *principal = GetPrincipal();
-    mAllowXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
+    mAllowContentXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
 
     // Determine whether to use an XBL scope.
-    mUseXBLScope = mAllowXBLScope;
-    if (mUseXBLScope) {
+    mUseContentXBLScope = mAllowContentXBLScope;
+    if (mUseContentXBLScope) {
       const js::Class *clasp = js::GetObjectClass(mGlobalJSObject);
-      mUseXBLScope = !strcmp(clasp->name, "Window") ||
-                     !strcmp(clasp->name, "ChromeWindow") ||
-                     !strcmp(clasp->name, "ModalContentWindow");
+      mUseContentXBLScope = !strcmp(clasp->name, "Window") ||
+                            !strcmp(clasp->name, "ChromeWindow") ||
+                            !strcmp(clasp->name, "ModalContentWindow");
     }
-    if (mUseXBLScope) {
-      mUseXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
+    if (mUseContentXBLScope) {
+      mUseContentXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
     }
 }
 
 // static
 bool
 XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
 {
     for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
@@ -181,30 +181,30 @@ XPCWrappedNativeScope::AttachComponentsO
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
 
     RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
     return JS_DefinePropertyById(aCx, global, id, components,
                                  JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 JSObject*
-XPCWrappedNativeScope::EnsureXBLScope(JSContext *cx)
+XPCWrappedNativeScope::EnsureContentXBLScope(JSContext *cx)
 {
     JS::RootedObject global(cx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
-    MOZ_ASSERT(!mIsXBLScope);
+    MOZ_ASSERT(!mIsContentXBLScope);
     MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
                       "nsXBLPrototypeScript compilation scope"));
 
     // If we already have a special XBL scope object, we know what to use.
-    if (mXBLScope)
-        return mXBLScope;
+    if (mContentXBLScope)
+        return mContentXBLScope;
 
     // If this scope doesn't need an XBL scope, just return the global.
-    if (!mUseXBLScope)
+    if (!mUseContentXBLScope)
         return global;
 
     // Set up the sandbox options. Note that we use the DOM global as the
     // sandboxPrototype so that the XBL scope can access all the DOM objects
     // it's accustomed to accessing.
     //
     // NB: One would think that wantXrays wouldn't make a difference here.
     // However, wantXrays lives a secret double life, and one of its other
@@ -223,56 +223,58 @@ XPCWrappedNativeScope::EnsureXBLScope(JS
     nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
     principalAsArray.AppendElement(principal);
     ep = new nsExpandedPrincipal(principalAsArray);
 
     // Create the sandbox.
     RootedValue v(cx);
     nsresult rv = CreateSandboxObject(cx, &v, ep, options);
     NS_ENSURE_SUCCESS(rv, nullptr);
-    mXBLScope = &v.toObject();
+    mContentXBLScope = &v.toObject();
 
     // Tag it.
-    EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
+    EnsureCompartmentPrivate(js::UncheckedUnwrap(mContentXBLScope))->scope->mIsContentXBLScope = true;
 
     // Good to go!
-    return mXBLScope;
+    return mContentXBLScope;
 }
 
 bool
-XPCWrappedNativeScope::AllowXBLScope()
+XPCWrappedNativeScope::AllowContentXBLScope()
 {
     // We only disallow XBL scopes in remote XUL situations.
-    MOZ_ASSERT_IF(!mAllowXBLScope,
+    MOZ_ASSERT_IF(!mAllowContentXBLScope,
                   nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal()));
-    return mAllowXBLScope;
+    return mAllowContentXBLScope;
 }
 
 namespace xpc {
 JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
 {
     JS::RootedObject contentScope(cx, contentScopeArg);
     JSAutoCompartment ac(cx, contentScope);
-    JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx);
+    JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureContentXBLScope(cx);
     NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
     scope = js::UncheckedUnwrap(scope);
     JS::ExposeObjectToActiveJS(scope);
     return scope;
 }
 
-bool AllowXBLScope(JSCompartment *c)
+bool
+AllowContentXBLScope(JSCompartment *c)
 {
   XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
-  return scope && scope->AllowXBLScope();
+  return scope && scope->AllowContentXBLScope();
 }
 
-bool UseXBLScope(JSCompartment *c)
+bool
+UseContentXBLScope(JSCompartment *c)
 {
   XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
-  return scope && scope->UseXBLScope();
+  return scope && scope->UseContentXBLScope();
 }
 
 } /* namespace xpc */
 
 XPCWrappedNativeScope::~XPCWrappedNativeScope()
 {
     MOZ_COUNT_DTOR(XPCWrappedNativeScope);
 
@@ -296,17 +298,17 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // XXX we should assert that we are dead or that xpconnect has shutdown
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
     if (mXrayExpandos.initialized())
         mXrayExpandos.destroy();
 
     JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
-    mXBLScope.finalize(rt);
+    mContentXBLScope.finalize(rt);
     mGlobalJSObject.finalize(rt);
 }
 
 static PLDHashOperator
 WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
                              uint32_t number, void *arg)
 {
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1572,15 +1572,15 @@ IsChromeOrXBL(JSContext* cx, JSObject* /
 {
     MOZ_ASSERT(NS_IsMainThread());
     JSCompartment* c = js::GetContextCompartment(cx);
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, we just always claim to be XBL.
     //
     // Note that, for performance, we don't check AllowXULXBLForPrincipal here,
-    // and instead rely on the fact that AllowXBLScope() only returns false in
+    // and instead rely on the fact that AllowContentXBLScope() only returns false in
     // remote XUL situations.
-    return AccessCheck::isChrome(c) || IsXBLScope(c) || !AllowXBLScope(c);
+    return AccessCheck::isChrome(c) || IsContentXBLScope(c) || !AllowContentXBLScope(c);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1015,18 +1015,18 @@ public:
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
 
     void TraceSelf(JSTracer *trc) {
         MOZ_ASSERT(mGlobalJSObject);
         mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
-        if (mXBLScope)
-            mXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
+        if (mContentXBLScope)
+            mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
         if (mXrayExpandos.initialized())
             mXrayExpandos.trace(trc);
     }
 
     static void
     SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb);
 
     static void
@@ -1092,25 +1092,25 @@ public:
     void RemoveDOMExpandoObject(JSObject *expando) {
         if (mDOMExpandoSet)
             mDOMExpandoSet->remove(expando);
     }
 
     // Gets the appropriate scope object for XBL in this scope. The context
     // must be same-compartment with the global upon entering, and the scope
     // object is wrapped into the compartment of the global.
-    JSObject *EnsureXBLScope(JSContext *cx);
+    JSObject *EnsureContentXBLScope(JSContext *cx);
 
     XPCWrappedNativeScope(JSContext *cx, JS::HandleObject aGlobal);
 
     nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
 
-    bool IsXBLScope() { return mIsXBLScope; }
-    bool AllowXBLScope();
-    bool UseXBLScope() { return mUseXBLScope; }
+    bool IsContentXBLScope() { return mIsContentXBLScope; }
+    bool AllowContentXBLScope();
+    bool UseContentXBLScope() { return mUseContentXBLScope; }
 
 protected:
     virtual ~XPCWrappedNativeScope();
 
     static void KillDyingScopes();
 
     XPCWrappedNativeScope(); // not implemented
 
@@ -1125,39 +1125,39 @@ private:
     XPCWrappedNativeScope*           mNext;
     // The JS global object for this scope.  If non-null, this will be the
     // default parent for the XPCWrappedNatives that have us as the scope,
     // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
     // constructor).
     JS::ObjectPtr                    mGlobalJSObject;
 
     // XBL Scope. This is is a lazily-created sandbox for non-system scopes.
-    // EnsureXBLScope() decides whether it needs to be created or not.
+    // EnsureContentXBLScope() decides whether it needs to be created or not.
     // This reference is wrapped into the compartment of mGlobalJSObject.
-    JS::ObjectPtr                    mXBLScope;
+    JS::ObjectPtr                    mContentXBLScope;
 
     nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
 
     JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
 
-    bool mIsXBLScope;
+    bool mIsContentXBLScope;
 
     // For remote XUL domains, we run all XBL in the content scope for compat
     // reasons (though we sometimes pref this off for automation). We separately
-    // track the result of this decision (mAllowXBLScope), from the decision
-    // of whether to actually _use_ an XBL scope (mUseXBLScope), which depends
+    // track the result of this decision (mAllowContentXBLScope), from the decision
+    // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
     // on the type of global and whether the compartment is system principal
     // or not.
     //
     // This distinction is useful primarily because, if true, we know that we
     // have no way of distinguishing XBL script from content script for the
     // given scope. In these (unsupported) situations, we just always claim to
     // be XBL.
-    bool mAllowXBLScope;
-    bool mUseXBLScope;
+    bool mAllowContentXBLScope;
+    bool mUseContentXBLScope;
 };
 
 /***************************************************************************/
 // XPCNativeMember represents a single idl declared method, attribute or
 // constant.
 
 // Tight. No virtual methods. Can be bitwise copied (until any resolution done).
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -65,18 +65,18 @@ private:
     // Whether the new-style domain policy when this compartment was created
     // forbids script execution.
     bool mScriptBlockedByPolicy;
 };
 
 JSObject *
 TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);
 
-bool IsXBLScope(JSCompartment *compartment);
-bool IsInXBLScope(JSObject *obj);
+bool IsContentXBLScope(JSCompartment *compartment);
+bool IsInContentXBLScope(JSObject *obj);
 
 // Return a raw XBL scope object corresponding to contentScope, which must
 // be an object whose global is a DOM window.
 //
 // The return value is not wrapped into cx->compartment, so be sure to enter
 // its compartment before doing anything meaningful.
 //
 // Also note that XBL scopes are lazily created, so the return-value should be
@@ -85,33 +85,34 @@ bool IsInXBLScope(JSObject *obj);
 //
 // This function asserts if |contentScope| is itself in an XBL scope to catch
 // sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that
 // are in XBL scope (by just returning the global).
 JSObject *
 GetXBLScope(JSContext *cx, JSObject *contentScope);
 
 inline JSObject *
-GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj) {
-    if (IsInXBLScope(obj))
+GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj)
+{
+    if (IsInContentXBLScope(obj))
         return js::GetGlobalForObjectCrossCompartment(obj);
     return GetXBLScope(cx, obj);
 }
 
 // Returns whether XBL scopes have been explicitly disabled for code running
-// in this compartment. See the comment around mAllowXBLScope.
+// in this compartment. See the comment around mAllowContentXBLScope.
 bool
-AllowXBLScope(JSCompartment *c);
+AllowContentXBLScope(JSCompartment *c);
 
 // Returns whether we will use an XBL scope for this compartment. This is
 // semantically equivalent to comparing global != GetXBLScope(global), but it
 // does not have the side-effect of eagerly creating the XBL scope if it does
 // not already exist.
 bool
-UseXBLScope(JSCompartment *c);
+UseContentXBLScope(JSCompartment *c);
 
 bool
 IsSandboxPrototypeProxy(JSObject *obj);
 
 bool
 IsReflector(JSObject *obj);
 
 bool
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -152,19 +152,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     xrayProto.expando = 42;
     is(xray.expando, 42, "Xrayed instances see proto expandos");
     is(xray2.expando, 42, "Xrayed instances see proto expandos");
   }
 
   function testDate() {
     // toGMTString is handled oddly in the engine. We don't bother to support
     // it over Xrays.
-    //
-    // We don't yet support self-hosted functions on Xrays. See bug 972987.
-    let propsToSkip = ['toGMTString', 'toLocaleString',
-                       'toLocaleDateString', 'toLocaleTimeString'];
+    let propsToSkip = ['toGMTString'];
+
     testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
+
+    // Test the self-hosted toLocaleString.
+    var d = new iwin.Date();
+    isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
+    is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
+    is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
+    is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
   }
 
   ]]>
   </script>
   <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
 </window>
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -426,17 +426,17 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     // a privileged scope is wrapped with a CrossCompartmentWrapper, even though
     // the lack of a waiver _really_ should give it an opaque wrapper. This is
     // a bit too entrenched to change for content-chrome, but we can at least fix
     // it for XBL scopes.
     //
     // See bug 843829.
     else if (targetSubsumesOrigin && !originSubsumesTarget &&
              !waiveXrayFlag && xrayType == NotXray &&
-             IsXBLScope(target))
+             IsContentXBLScope(target))
     {
         wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton;
     }
 
     //
     // Now, handle the regular cases.
     //
     // These are wrappers we can compute using a rule-based approach. In order
@@ -457,20 +457,20 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
         bool wantXrays = !(sameOrigin && !targetdata->wantXrays);
 
         // If Xrays are warranted, the caller may waive them for non-security
         // wrappers.
         bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag;
 
         // We have slightly different behavior for the case when the object
         // being wrapped is in an XBL scope.
-        bool originIsXBLScope = IsXBLScope(origin);
+        bool originIsContentXBLScope = IsContentXBLScope(origin);
 
         wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
-                                originIsXBLScope);
+                                originIsContentXBLScope);
     }
 
     if (!targetSubsumesOrigin) {
         // Do a belt-and-suspenders check against exposing eval()/Function() to
         // non-subsuming content.
         JSFunction *fun = JS_GetObjectFunction(obj);
         if (fun) {
             if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -447,65 +447,87 @@ JSXrayTraits::resolveOwnProperty(JSConte
     // properties when we start supporting arrays.
     if (!JSID_IS_STRING(id))
         return true;
     Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id));
 
     // Scan through the functions.
     const JSFunctionSpec *fsMatch = nullptr;
     for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        if (fs->selfHostedName)
-            continue;
         if (JS_FlatStringEqualsAscii(str, fs->name)) {
             fsMatch = fs;
             break;
         }
     }
     if (fsMatch) {
         // Generate an Xrayed version of the method.
-        Rooted<JSFunction*> fun(cx, JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs,
-                                                       0, wrapper, id));
+        RootedFunction fun(cx);
+        if (fsMatch->selfHostedName) {
+            fun = JS::GetSelfHostedFunction(cx, fsMatch->selfHostedName, id, fsMatch->nargs);
+        } else {
+            fun = JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs,
+                                     0, wrapper, id);
+        }
         if (!fun)
             return false;
 
         // The generic Xray machinery only defines non-own properties on the holder.
         // This is broken, and will be fixed at some point, but for now we need to
         // cache the value explicitly. See the corresponding call to
         // JS_GetPropertyById at the top of this function.
         RootedObject funObj(cx, JS_GetFunctionObject(fun));
         return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     // Scan through the properties.
     const JSPropertySpec *psMatch = nullptr;
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
-        // We don't support self-hosted accessors yet (see bug 972987). And given
-        // the confusion outlined in bug 992977, we can't support JSPropertyOp-
-        // backed entries either (which in practice is fine).
-        if (!(ps->flags & JSPROP_NATIVE_ACCESSORS))
-            continue;
         if (JS_FlatStringEqualsAscii(str, ps->name)) {
             psMatch = ps;
             break;
         }
     }
     if (psMatch) {
+        desc.value().setUndefined();
+        // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS.
+        // See bug 992977.
+        RootedFunction getterObj(cx);
+        RootedFunction setterObj(cx);
+        unsigned flags = psMatch->flags;
+        if (flags & JSPROP_NATIVE_ACCESSORS) {
+            desc.setGetter(psMatch->getter.propertyOp.op);
+            desc.setSetter(psMatch->setter.propertyOp.op);
+        } else {
+            MOZ_ASSERT(flags & JSPROP_GETTER);
+            getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
+            if (!getterObj)
+                return false;
+            desc.setGetterObject(JS_GetFunctionObject(getterObj));
+            if (psMatch->setter.selfHosted.funname) {
+                MOZ_ASSERT(flags & JSPROP_SETTER);
+                setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
+                if (!setterObj)
+                    return false;
+                desc.setSetterObject(JS_GetFunctionObject(setterObj));
+            }
+        }
+        desc.setAttributes(flags);
+
         // The generic Xray machinery only defines non-own properties on the holder.
         // This is broken, and will be fixed at some point, but for now we need to
         // cache the value explicitly. See the corresponding call to
         // JS_GetPropertyById at the top of this function.
         //
         // Note also that the public-facing API here doesn't give us a way to
         // pass along JITInfo. It's probably ok though, since Xrays are already
         // pretty slow.
         return JS_DefinePropertyById(cx, holder, id,
-                                     UndefinedHandleValue, psMatch->flags,
-                                     psMatch->getter.propertyOp.op, psMatch->setter.propertyOp.op) &&
+                                     UndefinedHandleValue, desc.attributes(),
+                                     desc.getter(), desc.setter()) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
 JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
@@ -522,31 +544,30 @@ JSXrayTraits::enumerateNames(JSContext *
     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     RootedObject target(cx, getTargetObject(wrapper));
     const js::Class *clasp = js::GetObjectClass(target);
     MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder));
     MOZ_ASSERT(clasp->spec.defined());
 
     // Intern all the strings, and pass theme to the caller.
     for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        if (fs->selfHostedName)
-            continue;
         RootedString str(cx, JS_InternString(cx, fs->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
     }
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
-        // We don't support self-hosted functions yet. See bug 972987.
-        // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS.
-        // See bug 992977.
-        if (!(ps->flags & JSPROP_NATIVE_ACCESSORS))
-            continue;
+        // We have code to Xray self-hosted accessors. But at present, there don't appear
+        // to be any self-hosted accessors anywhere in SpiderMonkey, let alone in on an
+        // Xrayable class, so we can't test it. Assert against it to make sure that we get
+        // test coverage in test_XrayToJS.xul when the time comes.
+        MOZ_ASSERT(ps->flags & JSPROP_NATIVE_ACCESSORS,
+                   "Self-hosted accessor added to Xrayable class - ping the XPConnect "
+                   "module owner about adding test coverage");
         RootedString str(cx, JS_InternString(cx, ps->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
     }
 
     // Add the 'constructor' property.
@@ -1749,17 +1770,17 @@ static void
 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
 {
     // In general, we shouldn't have cross-compartment wrappers here, because
     // we should be running in an XBL scope, and the content prototype should
     // contain wrappers to functions defined in the XBL scope. But if the node
     // has been adopted into another compartment, those prototypes will now point
     // to a different XBL scope (which is ok).
     MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
-                  xpc::IsXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
+                  xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
     MOZ_ASSERT(JS_ObjectIsCallable(cx, obj));
 }
 
 static void
 DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc)
 {
     if (!desc->obj)
         return;
@@ -1904,17 +1925,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // While we have to do some sketchy walking through content land, we should
     // be protected by read-only/non-configurable properties, and any functions
     // we end up with should _always_ be living in an XBL scope (usually ours,
     // but could be another if the node has been adopted).
     //
     // Make sure to assert this.
     nsCOMPtr<nsIContent> content;
     if (!desc.object() &&
-        EnsureCompartmentPrivate(wrapper)->scope->IsXBLScope() &&
+        EnsureCompartmentPrivate(wrapper)->scope->IsContentXBLScope() &&
         (content = do_QueryInterfaceNative(cx, wrapper)))
     {
         if (!nsContentUtils::LookupBindingMember(cx, content, id, desc))
             return false;
         DEBUG_CheckXBLLookup(cx, desc.address());
     }
 
     // If we still have nothing, we're done.
new file mode 100644
--- /dev/null
+++ b/layout/base/TouchCaret.cpp
@@ -0,0 +1,820 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* 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 "TouchCaret.h"
+
+#include "nsCOMPtr.h"
+#include "nsFrameSelection.h"
+#include "nsIFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsIDOMNode.h"
+#include "nsISelection.h"
+#include "nsISelectionPrivate.h"
+#include "nsIContent.h"
+#include "nsIPresShell.h"
+#include "nsCanvasFrame.h"
+#include "nsRenderingContext.h"
+#include "nsPresContext.h"
+#include "nsBlockFrame.h"
+#include "nsISelectionController.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/BasicEvents.h"
+#include "nsIDOMWindow.h"
+#include "nsQueryContentEventResult.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsView.h"
+#include "nsDOMTokenList.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+#define TOUCHCARET_LOG(...)
+// #define TOUCHCARET_LOG(...) printf_stderr("TouchCaret: " __VA_ARGS__)
+
+// Click on the boundary of input/textarea will place the caret at the
+// front/end of the content. To advoid this, we need to deflate the content
+// boundary by 61 app units (1 pixel + 1 app unit).
+static const int32_t kBoundaryAppUnits = 61;
+// The auto scroll timer's interval in milliseconds.
+static const int32_t kAutoScrollTimerDelay = 30;
+
+NS_IMPL_ISUPPORTS(TouchCaret, nsISelectionListener)
+
+/*static*/ int32_t TouchCaret::sTouchCaretMaxDistance = 0;
+/*static*/ int32_t TouchCaret::sTouchCaretExpirationTime = 0;
+
+TouchCaret::TouchCaret(nsIPresShell* aPresShell)
+  : mState(TOUCHCARET_NONE),
+    mActiveTouchId(-1),
+    mCaretCenterToDownPointOffsetY(0),
+    mVisible(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  static bool addedTouchCaretPref = false;
+  if (!addedTouchCaretPref) {
+    Preferences::AddIntVarCache(&sTouchCaretMaxDistance,
+                                "touchcaret.distance.threshold");
+    Preferences::AddIntVarCache(&sTouchCaretExpirationTime,
+                                "touchcaret.expiration.time");
+    addedTouchCaretPref = true;
+  }
+
+  // The presshell owns us, so no addref.
+  mPresShell = do_GetWeakReference(aPresShell);
+  MOZ_ASSERT(mPresShell, "Hey, pres shell should support weak refs");
+}
+
+TouchCaret::~TouchCaret()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mTouchCaretExpirationTimer) {
+    mTouchCaretExpirationTimer->Cancel();
+    mTouchCaretExpirationTimer = nullptr;
+  }
+}
+
+nsIFrame*
+TouchCaret::GetCanvasFrame()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nullptr;
+  }
+  return presShell->GetCanvasFrame();
+}
+
+void
+TouchCaret::SetVisibility(bool aVisible)
+{
+  if (mVisible == aVisible) {
+    return;
+  }
+  mVisible = aVisible;
+
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return;
+  }
+
+  // Set touch caret visibility.
+  ErrorResult err;
+  touchCaretElement->ClassList()->Toggle(NS_LITERAL_STRING("hidden"),
+                                         dom::Optional<bool>(!mVisible),
+                                         err);
+  // Set touch caret expiration time.
+  mVisible ? LaunchExpirationTimer() : CancelExpirationTimer();
+
+  // We must call SetHasTouchCaret() in order to get APZC to wait until the
+  // event has been round-tripped and check whether it has been handled,
+  // otherwise B2G will end up panning the document when the user tries to drag
+  // touch caret.
+  presShell->SetMayHaveTouchCaret(mVisible);
+}
+
+nsRect
+TouchCaret::GetTouchFrameRect()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsRect();
+  }
+
+  dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return nsRect();
+  }
+
+  // Get touch caret position relative to canvas frame.
+  nsIFrame* touchCaretFrame = touchCaretElement->GetPrimaryFrame();
+  nsRect tcRect = touchCaretFrame->GetRectRelativeToSelf();
+  nsIFrame* canvasFrame = GetCanvasFrame();
+
+  nsLayoutUtils::TransformResult rv =
+    nsLayoutUtils::TransformRect(touchCaretFrame, canvasFrame, tcRect);
+  return rv == nsLayoutUtils::TRANSFORM_SUCCEEDED ? tcRect : nsRect();
+}
+
+nsRect
+TouchCaret::GetContentBoundary()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsRect();
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsIFrame* canvasFrame = GetCanvasFrame();
+
+  // Get the editing host to determine the touch caret dragable boundary.
+  dom::Element* editingHost = focusFrame->GetContent()->GetEditingHost();
+  if (!editingHost) {
+    return nsRect();
+  }
+
+  nsRect resultRect;
+  for (nsIFrame* frame = editingHost->GetPrimaryFrame(); frame;
+       frame = frame->GetNextContinuation()) {
+    nsRect rect = frame->GetContentRectRelativeToSelf();
+    nsLayoutUtils::TransformRect(frame, canvasFrame, rect);
+    resultRect = resultRect.Union(rect);
+
+    mozilla::layout::FrameChildListIterator lists(frame);
+    for (; !lists.IsDone(); lists.Next()) {
+      // Loop over all children to take the overflow rect in to consideration.
+      nsFrameList::Enumerator childFrames(lists.CurrentList());
+      for (; !childFrames.AtEnd(); childFrames.Next()) {
+        nsIFrame* kid = childFrames.get();
+        nsRect overflowRect = kid->GetScrollableOverflowRect();
+        nsLayoutUtils::TransformRect(kid, canvasFrame, overflowRect);
+        resultRect = resultRect.Union(overflowRect);
+      }
+    }
+  }
+  // Shrink rect to make sure we never hit the boundary.
+  resultRect.Deflate(kBoundaryAppUnits);
+
+  return resultRect;
+}
+
+nscoord
+TouchCaret::GetCaretYCenterPosition()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return 0;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsRect caretRect = focusFrame->GetRectRelativeToSelf();
+  nsIFrame *canvasFrame = GetCanvasFrame();
+  nsLayoutUtils::TransformRect(focusFrame, canvasFrame, caretRect);
+
+  return (caretRect.y + caretRect.height / 2);
+}
+
+void
+TouchCaret::SetTouchFramePos(const nsPoint& aOrigin)
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return;
+  }
+
+  // Convert aOrigin to CSS pixels.
+  nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
+  int32_t x = presContext->AppUnitsToIntCSSPixels(aOrigin.x);
+  int32_t y = presContext->AppUnitsToIntCSSPixels(aOrigin.y);
+
+  nsAutoString styleStr;
+  styleStr.AppendLiteral("left: ");
+  styleStr.AppendInt(x);
+  styleStr.AppendLiteral("px; top: ");
+  styleStr.AppendInt(y);
+  styleStr.AppendLiteral("px;");
+
+  touchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
+                             styleStr, true);
+}
+
+void
+TouchCaret::MoveCaret(const nsPoint& movePoint)
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  // Get scrollable frame.
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  nsIFrame* scrollable =
+    nsLayoutUtils::GetClosestFrameOfType(focusFrame, nsGkAtoms::scrollFrame);
+
+  // Convert touch/mouse position to frame coordinates.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  if (!canvasFrame) {
+    return;
+  }
+  nsPoint offsetToCanvasFrame = nsPoint(0,0);
+  nsLayoutUtils::TransformPoint(scrollable, canvasFrame, offsetToCanvasFrame);
+  nsPoint pt = movePoint - offsetToCanvasFrame;
+
+  // Evaluate offsets.
+  nsIFrame::ContentOffsets offsets =
+    scrollable->GetContentOffsetsFromPoint(pt, nsIFrame::SKIP_HIDDEN);
+
+  // Move caret position.
+  nsWeakFrame weakScrollable = scrollable;
+  nsRefPtr<nsFrameSelection> fs = scrollable->GetFrameSelection();
+  fs->HandleClick(offsets.content, offsets.StartOffset(),
+                  offsets.EndOffset(),
+                  false,
+                  false,
+                  offsets.associateWithNext);
+
+  if (!weakScrollable.IsAlive()) {
+    return;
+  }
+
+  // Scroll scrolled frame.
+  nsIScrollableFrame* saf = do_QueryFrame(scrollable);
+  nsIFrame* capturingFrame = saf->GetScrolledFrame();
+  offsetToCanvasFrame = nsPoint(0,0);
+  nsLayoutUtils::TransformPoint(capturingFrame, canvasFrame, offsetToCanvasFrame);
+  pt = movePoint - offsetToCanvasFrame;
+  fs->StartAutoScrollTimer(capturingFrame, pt, kAutoScrollTimerDelay);
+}
+
+bool
+TouchCaret::IsOnTouchCaret(const nsPoint& aPoint)
+{
+  // Return false if touch caret is not visible.
+  if (!mVisible) {
+    return false;
+  }
+
+  nsRect tcRect = GetTouchFrameRect();
+
+  // Check if the click was in the bounding box of the touch caret.
+  int32_t distance;
+  if (tcRect.Contains(aPoint.x, aPoint.y)) {
+    distance = 0;
+  } else {
+    // If click is outside the bounding box of the touch caret, check the
+    // distance to the center of the touch caret.
+    int32_t posX = (tcRect.x + (tcRect.width / 2));
+    int32_t posY = (tcRect.y + (tcRect.height / 2));
+    int32_t dx = Abs(aPoint.x - posX);
+    int32_t dy = Abs(aPoint.y - posY);
+    distance = dx + dy;
+  }
+  return (distance <= TouchCaretMaxDistance());
+}
+
+nsresult
+TouchCaret::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel,
+                                     int16_t aReason)
+{
+  // Hide touch caret while no caret exists.
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return NS_OK;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  if (!caret) {
+    SetVisibility(false);
+    return NS_OK;
+  }
+
+  // The same touch caret is shared amongst the document and any text widgets it
+  // may contain. This means that the touch caret could get notifications from
+  // multiple selections.
+  // If this notification is for a selection that is not the one the
+  // the caret is currently interested in , then there is nothing to do!
+  if (aSel != caret->GetCaretDOMSelection()) {
+    return NS_OK;
+  }
+
+  // Update touch caret position and visibility.
+  // Hide touch caret while key event causes selection change.
+  if ((aReason == nsISelectionListener::NO_REASON) ||
+      (aReason & nsISelectionListener::KEYPRESS_REASON)) {
+    UpdateTouchCaret(false);
+  } else {
+    UpdateTouchCaret(true);
+  }
+
+  return NS_OK;
+}
+
+void
+TouchCaret::UpdateTouchCaret(bool aVisible)
+{
+  // Hide touch caret while no caret exists.
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  if (!caret) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Hide touch caret while caret is not visible.
+  bool caretVisible = false;
+  caret->GetCaretVisible(&caretVisible);
+  if (!caretVisible) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Caret is visible and shown, update touch caret.
+  nsISelection* caretSelection = caret->GetCaretDOMSelection();
+  nsRect focusRect;
+  nsIFrame* focusFrame = caret->GetGeometry(caretSelection, &focusRect);
+  if (!focusFrame || focusRect.IsEmpty()) {
+    SetVisibility(false);
+    return;
+  }
+
+  // Position of the touch caret relative to focusFrame.
+  nsPoint pos = nsPoint(focusRect.x + (focusRect.width / 2),
+                        focusRect.y + focusRect.height);
+
+  // Transform the position to make it relative to canvas frame.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  if (!canvasFrame) {
+    return;
+  }
+  nsLayoutUtils::TransformPoint(focusFrame, canvasFrame, pos);
+
+  // Clamp the touch caret position to the scrollframe boundary.
+  nsIFrame* closestScrollFrame =
+    nsLayoutUtils::GetClosestFrameOfType(focusFrame, nsGkAtoms::scrollFrame);
+  while (closestScrollFrame) {
+    nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
+    nsRect visualRect = sf->GetScrollPortRect();
+    // Clamp the touch caret in the scroll port.
+    nsLayoutUtils::TransformRect(closestScrollFrame, canvasFrame, visualRect);
+    pos = visualRect.ClampPoint(pos);
+
+    // Get next ancestor scroll frame.
+    closestScrollFrame =
+      nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),
+                                           nsGkAtoms::scrollFrame);
+  }
+
+  SetTouchFramePos(pos);
+  SetVisibility(aVisible);
+}
+
+/* static */void
+TouchCaret::DisableTouchCaretCallback(nsITimer* aTimer, void* aTouchCaret)
+{
+  nsRefPtr<TouchCaret> self = static_cast<TouchCaret*>(aTouchCaret);
+  NS_PRECONDITION(aTimer == self->mTouchCaretExpirationTimer,
+                  "Unexpected timer");
+
+  self->SetVisibility(false);
+}
+
+void
+TouchCaret::LaunchExpirationTimer()
+{
+  if (TouchCaretExpirationTime() > 0) {
+    if (!mTouchCaretExpirationTimer) {
+      mTouchCaretExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
+    }
+
+    if (mTouchCaretExpirationTimer) {
+      mTouchCaretExpirationTimer->Cancel();
+      mTouchCaretExpirationTimer->InitWithFuncCallback(DisableTouchCaretCallback,
+                                                       this,
+                                                       TouchCaretExpirationTime(),
+                                                       nsITimer::TYPE_ONE_SHOT);
+    }
+  }
+}
+
+void
+TouchCaret::CancelExpirationTimer()
+{
+  if (mTouchCaretExpirationTimer) {
+    mTouchCaretExpirationTimer->Cancel();
+  }
+}
+
+nsEventStatus
+TouchCaret::HandleEvent(WidgetEvent* aEvent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return nsEventStatus_eIgnore;
+  }
+
+  mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
+  if (!touchCaretElement) {
+    return nsEventStatus_eIgnore;
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (aEvent->message) {
+    case NS_TOUCH_START:
+    case NS_TOUCH_ENTER:
+      status = HandleTouchDownEvent(aEvent->AsTouchEvent());
+      break;
+    case NS_MOUSE_BUTTON_DOWN:
+      status = HandleMouseDownEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_END:
+      status = HandleTouchUpEvent(aEvent->AsTouchEvent());
+      break;
+   case NS_MOUSE_BUTTON_UP:
+      status = HandleMouseUpEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_MOVE:
+      status = HandleTouchMoveEvent(aEvent->AsTouchEvent());
+      break;
+    case NS_MOUSE_MOVE:
+      status = HandleMouseMoveEvent(aEvent->AsMouseEvent());
+      break;
+    case NS_TOUCH_CANCEL:
+      mTouchesId.Clear();
+      SetState(TOUCHCARET_NONE);
+      LaunchExpirationTimer();
+      break;
+    case NS_KEY_UP:
+    case NS_KEY_DOWN:
+    case NS_KEY_PRESS:
+    case NS_WHEEL_EVENT_START:
+      // Disable touch caret while key/wheel event is received.
+      SetVisibility(false);
+      break;
+    default:
+      break;
+  }
+
+  return status;
+}
+
+nsPoint
+TouchCaret::GetEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier)
+{
+  for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+    if (aEvent->touches[i]->mIdentifier == aIdentifier) {
+      // Get event coordinate relative to canvas frame.
+      nsIFrame* canvasFrame = GetCanvasFrame();
+      nsIntPoint touchIntPoint = aEvent->touches[i]->mRefPoint;
+      return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+                                                          touchIntPoint,
+                                                          canvasFrame);
+    }
+  }
+  return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+}
+
+nsPoint
+TouchCaret::GetEventPosition(WidgetMouseEvent* aEvent)
+{
+  // Get event coordinate relative to canvas frame.
+  nsIFrame* canvasFrame = GetCanvasFrame();
+  nsIntPoint mouseIntPoint =
+    LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint);
+  return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+                                                      mouseIntPoint,
+                                                      canvasFrame);
+}
+
+nsEventStatus
+TouchCaret::HandleMouseMoveEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-move in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      {
+        nsPoint movePoint = GetEventPosition(aEvent);
+        movePoint.y += mCaretCenterToDownPointOffsetY;
+        nsRect contentBoundary = GetContentBoundary();
+        movePoint = contentBoundary.ClampPoint(movePoint);
+
+        MoveCaret(movePoint);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchMoveEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-move in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      // Consume touch event in mouse sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+      {
+        nsPoint movePoint = GetEventPosition(aEvent, mActiveTouchId);
+        movePoint.y += mCaretCenterToDownPointOffsetY;
+        nsRect contentBoundary = GetContentBoundary();
+        movePoint = contentBoundary.ClampPoint(movePoint);
+
+        MoveCaret(movePoint);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume NS_TOUCH_MOVE event in TOUCHCARET_TOUCHDRAG_INACTIVE state.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleMouseUpEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-up in state %d\n", this, mState);
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      if (aEvent->button == WidgetMouseEvent::eLeftButton) {
+        LaunchExpirationTimer();
+        SetState(TOUCHCARET_NONE);
+        status = nsEventStatus_eConsumeNoDefault;
+      }
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchUpEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-end in state %d\n", this, mState);
+  // Remove touches from cache if the stroke is gone in TOUCHDRAG states.
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE ||
+      mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+      nsTArray<int32_t>::index_type index =
+        mTouchesId.IndexOf(aEvent->touches[i]->mIdentifier);
+      MOZ_ASSERT(index != nsTArray<int32_t>::NoIndex);
+      mTouchesId.RemoveElementAt(index);
+    }
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      // Consume touch event in mouse sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+      if (mTouchesId.Length() == 0) {
+        // No more finger on the screen.
+        SetState(TOUCHCARET_NONE);
+        LaunchExpirationTimer();
+      } else {
+        // Still has finger touching on the screen.
+        if (aEvent->touches[0]->mIdentifier == mActiveTouchId) {
+          // Remove finger from the touch caret.
+          SetState(TOUCHCARET_TOUCHDRAG_INACTIVE);
+          LaunchExpirationTimer();
+        } else {
+          // If the finger removed is not the finger on touch caret, remain in
+          // TOUCHCARET_DRAG_ACTIVE state.
+        }
+      }
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      if (mTouchesId.Length() == 0) {
+        // No more finger on the screen.
+        SetState(TOUCHCARET_NONE);
+      }
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleMouseDownEvent(WidgetMouseEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a mouse-down in state %d\n", this, mState);
+  if (!GetVisibility()) {
+    // If touch caret is invisible, bypass event.
+    return nsEventStatus_eIgnore;
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      if (aEvent->button == WidgetMouseEvent::eLeftButton) {
+        nsPoint point = GetEventPosition(aEvent);
+        if (IsOnTouchCaret(point)) {
+          // Cache distence of the event point to the center of touch caret.
+          mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - point.y;
+          // Enter TOUCHCARET_MOUSEDRAG_ACTIVE state and cancel the timer.
+          SetState(TOUCHCARET_MOUSEDRAG_ACTIVE);
+          CancelExpirationTimer();
+          status = nsEventStatus_eConsumeNoDefault;
+        } else {
+          // Set touch caret invisible if HisTest fails. Bypass event.
+          SetVisibility(false);
+          status = nsEventStatus_eIgnore;
+        }
+      } else {
+        // Set touch caret invisible if not left button down event.
+        SetVisibility(false);
+        status = nsEventStatus_eIgnore;
+      }
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+      SetVisibility(false);
+      SetState(TOUCHCARET_NONE);
+      break;
+
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume mouse event in touch sequence.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  return status;
+}
+
+nsEventStatus
+TouchCaret::HandleTouchDownEvent(WidgetTouchEvent* aEvent)
+{
+  TOUCHCARET_LOG("%p got a touch-start in state %d\n", this, mState);
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  switch (mState) {
+    case TOUCHCARET_NONE:
+      if (!GetVisibility()) {
+        // If touch caret is invisible, bypass event.
+        status = nsEventStatus_eIgnore;
+      } else {
+        nsPoint point = GetEventPosition(aEvent, 0);
+        if (IsOnTouchCaret(point)) {
+          // Touch start position is contained in touch caret.
+          mActiveTouchId = aEvent->touches[0]->mIdentifier;
+          // Cache distance of the event point to the center of touch caret.
+          mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - point.y;
+          // Enter TOUCHCARET_TOUCHDRAG_ACTIVE state and cancel the timer.
+          SetState(TOUCHCARET_TOUCHDRAG_ACTIVE);
+          CancelExpirationTimer();
+          status = nsEventStatus_eConsumeNoDefault;
+        } else {
+          // Set touch caret invisible if HisTest fails. Bypass event.
+          SetVisibility(false);
+          status = nsEventStatus_eIgnore;
+        }
+      }
+      break;
+
+    case TOUCHCARET_MOUSEDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_ACTIVE:
+    case TOUCHCARET_TOUCHDRAG_INACTIVE:
+      // Consume NS_TOUCH_START event.
+      status = nsEventStatus_eConsumeNoDefault;
+      break;
+  }
+
+  // Cache active touch IDs in TOUCHDRAG states.
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE ||
+      mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    mTouchesId.Clear();
+    for (size_t i = 0; i < aEvent->touches.Length(); i++) {
+      mTouchesId.AppendElement(aEvent->touches[i]->mIdentifier);
+    }
+  }
+
+  return status;
+}
+
+void
+TouchCaret::SetState(TouchCaretState aState)
+{
+  TOUCHCARET_LOG("%p state changed from %d to %d\n", this, mState, aState);
+  if (mState == TOUCHCARET_NONE) {
+    MOZ_ASSERT(aState != TOUCHCARET_TOUCHDRAG_INACTIVE,
+               "mState: NONE => TOUCHDRAG_INACTIVE isn't allowed!");
+  }
+
+  if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE) {
+    MOZ_ASSERT(aState != TOUCHCARET_MOUSEDRAG_ACTIVE,
+               "mState: TOUCHDRAG_ACTIVE => MOUSEDRAG_ACTIVE isn't allowed!");
+  }
+
+  if (mState == TOUCHCARET_MOUSEDRAG_ACTIVE) {
+    MOZ_ASSERT(aState == TOUCHCARET_MOUSEDRAG_ACTIVE ||
+               aState == TOUCHCARET_NONE,
+               "MOUSEDRAG_ACTIVE allowed next state: NONE!");
+  }
+
+  if (mState == TOUCHCARET_TOUCHDRAG_INACTIVE) {
+    MOZ_ASSERT(aState == TOUCHCARET_TOUCHDRAG_INACTIVE ||
+               aState == TOUCHCARET_NONE,
+               "TOUCHDRAG_INACTIVE allowed next state: NONE!");
+  }
+
+  mState = aState;
+
+  if (mState == TOUCHCARET_NONE) {
+    mActiveTouchId = -1;
+    mCaretCenterToDownPointOffsetY = 0;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/TouchCaret.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TouchCaret_h__
+#define mozilla_TouchCaret_h__
+
+#include "nsISelectionListener.h"
+#include "nsIScrollObserver.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsFrameSelection.h"
+#include "nsITimer.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/TouchEvents.h"
+#include "Units.h"
+
+namespace mozilla {
+
+/**
+ * The TouchCaret places a touch caret according to caret postion when the
+ * caret is shown.
+ * TouchCaret is also responsible for touch caret visibility. Touch caret
+ * won't be shown when timer expires or while key event causes selection change.
+ */
+class TouchCaret MOZ_FINAL : public nsISelectionListener
+{
+public:
+  explicit TouchCaret(nsIPresShell* aPresShell);
+  ~TouchCaret();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISELECTIONLISTENER
+
+  void Terminate()
+  {
+    mPresShell = nullptr;
+  }
+
+  /**
+   * Handle mouse and touch event only.
+   * Depends on visibility and position of touch caret, HandleEvent may consume
+   * that input event and return nsEventStatus_eConsumeNoDefault to the caller.
+   * In that case, caller should stop bubble up that input event.
+   */
+  nsEventStatus HandleEvent(WidgetEvent* aEvent);
+
+  /**
+   * By calling this function, touch caret recalculate touch frame position and
+   * update accordingly.
+   */
+  void UpdateTouchCaret(bool aVisible);
+
+  /**
+   * SetVisibility will set the visibility of the touch caret.
+   * SetVisibility performs an attribute-changed notification which could, in
+   * theory, destroy frames.
+   */
+  void SetVisibility(bool aVisible);
+
+  /**
+   * GetVisibility will get the visibility of the touch caret.
+   */
+  bool GetVisibility() const
+  {
+    return mVisible;
+  }
+
+private:
+  // Hide default constructor.
+  TouchCaret() MOZ_DELETE;
+
+  /**
+   * Find the nsCanvasFrame which holds the touch caret.
+   */
+  nsIFrame* GetCanvasFrame();
+
+  /**
+   * Retrieve the bounding rectangle of the touch caret.
+   *
+   * @returns A nsRect representing the bounding rectangle of this touch caret.
+   *          The returned offset is relative to the canvas frame.
+   */
+  nsRect GetTouchFrameRect();
+
+  /**
+   * Retrieve the bounding rectangle where the caret can be positioned.
+   * If we're positioning a caret in an input field, make sure the touch caret
+   * stays within the bounds of the field.
+   *
+   * @returns A nsRect representing the bounding rectangle of this valid area.
+   *          The returned offset is relative to the canvas frame.
+   */
+  nsRect GetContentBoundary();
+
+  /**
+   * Retrieve the center y position of the caret.
+   * The returned point is relative to the canvas frame.
+   */
+  nscoord GetCaretYCenterPosition();
+
+  /**
+   * Set the position of the touch caret.
+   * Touch caret is an absolute positioned div.
+   */
+  void SetTouchFramePos(const nsPoint& aOrigin);
+
+  void LaunchExpirationTimer();
+  void CancelExpirationTimer();
+  static void DisableTouchCaretCallback(nsITimer* aTimer, void* aPresShell);
+
+  /**
+   * Move the caret to movePoint which is relative to the canvas frame.
+   * Caret will be scrolled into view.
+   *
+   * @param movePoint tap location relative to the canvas frame.
+   */
+  void MoveCaret(const nsPoint& movePoint);
+
+  /**
+   * Check if aPoint is inside the touch caret frame.
+   *
+   * @param aPoint tap location relative to the canvas frame.
+   */
+  bool IsOnTouchCaret(const nsPoint& aPoint);
+
+  /**
+   * These Handle* functions comprise input alphabet of the TouchCaret
+   * finite-state machine triggering state transitions.
+   */
+  nsEventStatus HandleMouseMoveEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleMouseUpEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleMouseDownEvent(WidgetMouseEvent* aEvent);
+  nsEventStatus HandleTouchMoveEvent(WidgetTouchEvent* aEvent);
+  nsEventStatus HandleTouchUpEvent(WidgetTouchEvent* aEvent);
+  nsEventStatus HandleTouchDownEvent(WidgetTouchEvent* aEvent);
+
+  /**
+   * Get the coordinates of a given touch event, relative to canvas frame.
+   * @param aEvent the event
+   * @param aIdentifier the mIdentifier of the touch which is to be converted.
+   * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
+   * for some reason the coordinates for the touch are not known (e.g.,
+   * the mIdentifier touch is not found).
+   */
+  nsPoint GetEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier);
+
+  /**
+   * Get the coordinates of a given mouse event, relative to canvas frame.
+   * @param aEvent the event
+   * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
+   * for some reason the coordinates for the mouse are not known.
+   */
+  nsPoint GetEventPosition(WidgetMouseEvent* aEvent);
+
+  /**
+   * States of TouchCaret finite-state machine.
+   */
+  enum TouchCaretState {
+    // In this state, either there is no touch/mouse event going on, or the
+    // first stroke does not hit the touch caret.
+    // Will enter TOUCHCARET_TOUCHDRAG_ACTIVE state if the first touch stroke
+    // hits the touch caret. Will enter TOUCHCARET_MOUSEDRAG_ACTIVE state if
+    // mouse (left button) down hits the touch caret.
+    // Allowed next state: TOUCHCARET_MOUSEDRAG_ACTIVE,
+    //                     TOUCHCARET_TOUCHDRAG_ACTIVE.
+    TOUCHCARET_NONE,
+    // The first (left button) mouse down hits on the touch caret and is
+    // alive. Will enter TOUCHCARET_NONE state if the left button is release.
+    // Allowed next states: TOUCHCARET_NONE.
+    TOUCHCARET_MOUSEDRAG_ACTIVE,
+    // The first touch start event hits on touch caret and is alive.
+    // Will enter TOUCHCARET_NONE state if the finger on touch caret is
+    // removed and there are no more fingers on the screen; will enter
+    // TOUCHCARET_TOUCHDRAG_INACTIVE state if the finger on touch caret is
+    // removed but still has fingers touching on the screen.
+    // Allowed next states: TOUCHCARET_NONE, TOUCHCARET_TOUCHDRAG_INACTIVE.
+    TOUCHCARET_TOUCHDRAG_ACTIVE,
+    // The first touch stroke, which hit on touch caret, is dead, but still has
+    // fingers touching on the screen.
+    // Will enter TOUCHCARET_NONE state if all the fingers are removed from the
+    // screen.
+    // Allowed next state: TOUCHCARET_NONE.
+    TOUCHCARET_TOUCHDRAG_INACTIVE,
+  };
+
+  /**
+   * Do actual state transition and reset substates.
+   */
+  void SetState(TouchCaretState aState);
+
+  /**
+   * Current state we're dealing with.
+   */
+  TouchCaretState mState;
+
+  /**
+   * Array containing all active touch IDs. When a touch happens, it gets added
+   * to this array, even if we choose not to handle it. When it ends, we remove
+   * it. We need to maintain this array in order to detect the end of the
+   * "multitouch" states because touch start events contain all current touches,
+   * but touch end events contain only those touches that have gone.
+   */
+  nsTArray<int32_t> mTouchesId;
+
+  /**
+   * The mIdentifier of the touch which is on the touch caret.
+   */
+  int32_t mActiveTouchId;
+
+  /**
+   * The offset between the tap location and the center of caret along y axis.
+   */
+  nscoord mCaretCenterToDownPointOffsetY;
+
+  static int32_t TouchCaretMaxDistance()
+  {
+    return sTouchCaretMaxDistance;
+  }
+
+  static int32_t TouchCaretExpirationTime()
+  {
+    return sTouchCaretExpirationTime;
+  }
+
+protected:
+  nsWeakPtr mPresShell;
+
+  // Touch caret visibility
+  bool mVisible;
+  // Touch caret timer
+  nsCOMPtr<nsITimer> mTouchCaretExpirationTimer;
+
+  // Preference
+  static int32_t sTouchCaretMaxDistance;
+  static int32_t sTouchCaretExpirationTime;
+};
+} //namespace mozilla
+#endif //mozilla_TouchCaret_h__
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -86,16 +86,17 @@ UNIFIED_SOURCES += [
     'nsQuoteList.cpp',
     'nsStyleChangeList.cpp',
     'nsStyleSheetService.cpp',
     'PaintTracker.cpp',
     'PositionedEventTargeting.cpp',
     'RestyleManager.cpp',
     'RestyleTracker.cpp',
     'StackArena.cpp',
+    'TouchCaret.cpp',
 ]
 
 # nsDocumentViewer.cpp and nsPresShell.cpp need to be built separately
 # because they force NSPR logging.
 # nsPresArena.cpp needs to be built separately because it uses plarena.h.
 # nsRefreshDriver.cpp needs to be built separately because of name clashes in the OS X headers
 SOURCES += [
     'nsDocumentViewer.cpp',
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -24,16 +24,17 @@
 #include "nsPresContext.h"
 #include "nsIDocument.h"
 #include "nsTableFrame.h"
 #include "nsTableColFrame.h"
 #include "nsTableRowFrame.h"
 #include "nsTableCellFrame.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsHTMLParts.h"
+#include "nsPresShell.h"
 #include "nsIPresShell.h"
 #include "nsUnicharUtils.h"
 #include "nsStyleSet.h"
 #include "nsViewManager.h"
 #include "nsStyleConsts.h"
 #include "nsIDOMXULElement.h"
 #include "nsContainerFrame.h"
 #include "nsNameSpaceManager.h"
@@ -2592,16 +2593,22 @@ nsCSSFrameConstructor::ConstructDocEleme
     contentFrame->SetInitialChildList(kPrincipalList, childItems);
   }
 
   // set the primary frame
   aDocElement->SetPrimaryFrame(contentFrame);
 
   SetInitialSingleChild(mDocElementContainingBlock, newFrame);
 
+  // Create touch caret frame if there is a canvas frame
+  if (mDocElementContainingBlock->GetType() == nsGkAtoms::canvasFrame) {
+    ConstructAnonymousContentForCanvas(state, mDocElementContainingBlock,
+                                       aDocElement);
+  }
+
   return newFrame;
 }
 
 
 nsIFrame*
 nsCSSFrameConstructor::ConstructRootFrame()
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
@@ -2847,16 +2854,39 @@ nsCSSFrameConstructor::SetUpDocElementCo
   if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     SetInitialSingleChild(viewportFrame, newFrame);
   } else {
     nsFrameList newFrameList(newFrame, newFrame);
     viewportFrame->AppendFrames(kPrincipalList, newFrameList);
   }
 }
 
+void
+nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
+                                                          nsIFrame* aFrame,
+                                                          nsIContent* aDocElement)
+{
+  NS_ASSERTION(aFrame->GetType() == nsGkAtoms::canvasFrame, "aFrame should be canvas frame!");
+
+  nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
+  GetAnonymousContent(aDocElement, aFrame, anonymousItems);
+  if (anonymousItems.IsEmpty()) {
+    // Touch caret is not enabled.
+    return;
+  }
+
+  FrameConstructionItemList itemsToConstruct;
+  nsContainerFrame* frameAsContainer = do_QueryFrame(aFrame);
+  AddFCItemsForAnonymousContent(aState, frameAsContainer, anonymousItems, itemsToConstruct);
+
+  nsFrameItems frameItems;
+  ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer, frameItems);
+  frameAsContainer->AppendFrames(kPrincipalList, frameItems);
+}
+
 nsContainerFrame*
 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell*  aPresShell,
                                           nsPresContext* aPresContext,
                                           nsContainerFrame* aParentFrame,
                                           nsIFrame*      aPrevPageFrame,
                                           nsContainerFrame*& aCanvasFrame)
 {
   nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1764,16 +1764,20 @@ private:
    * style contexts in the undisplayed content map must be non-pseudo
    * contexts and also handles unbinding undisplayed generated content
    * as needed.
    */
   static void SetAsUndisplayedContent(FrameConstructionItemList& aList,
                                       nsIContent* aContent,
                                       nsStyleContext* aStyleContext,
                                       bool aIsGeneratedContent);
+  // Create touch caret frame.
+  void ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
+                                          nsIFrame* aFrame,
+                                          nsIContent* aDocElement);
 
 public:
 
   friend class nsFrameConstructorState;
 
 private:
 
   nsIDocument*        mDocument;  // Weak ref
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -714,16 +714,17 @@ static void RecordFrameMetrics(nsIFrame*
     nsIDocument* document = nullptr;
     document = presShell->GetDocument();
     if (document) {
       nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
       if (innerWin) {
         metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners();
       }
     }
+    metrics.mMayHaveTouchCaret = presShell->MayHaveTouchCaret();
   }
 
   LayoutDeviceToParentLayerScale layoutToParentLayerScale =
     // The ScreenToParentLayerScale should be mTransformScale which is not calculated yet,
     // but we don't yet handle CSS transforms, so we assume it's 1 here.
     metrics.mCumulativeResolution * LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0);
 
   // Calculate the composition bounds as the size of the scroll frame and
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -48,18 +48,22 @@ class nsDocShell;
 class nsIDocument;
 class nsIFrame;
 class nsPresContext;
 class nsStyleSet;
 class nsViewManager;
 class nsView;
 class nsRenderingContext;
 class nsIPageSequenceFrame;
+class nsCanvasFrame;
 class nsAString;
 class nsCaret;
+namespace mozilla {
+class TouchCaret;
+} // namespace mozilla
 class nsFrameSelection;
 class nsFrameManager;
 class nsILayoutHistoryState;
 class nsIReflowCallback;
 class nsIDOMNode;
 class nsIntRegion;
 class nsIStyleSheet;
 class nsCSSFrameConstructor;
@@ -127,20 +131,20 @@ typedef struct CapturingContentInfo {
   // capture should only be allowed during a mousedown event
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   nsIContent* mContent;
 } CapturingContentInfo;
 
-//61e60df7-128a-4cdd-a684-5f0bd2ceb61f
+//a4e5ff3a-dc5c-4b3a-a625-d164a9e50619
 #define NS_IPRESSHELL_IID \
-{ 0x61e60df7, 0x128a, 0x4cdd, \
-  {0xa6, 0x84, 0x5f, 0x0b, 0xd2, 0xce, 0xb6, 0x1f}}
+{ 0xa4e5ff3a, 0xdc5c, 0x4b3a, \
+  {0xa6, 0x25, 0xd1, 0x64, 0xa9, 0xe5, 0x06, 0x19}}
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -463,16 +467,22 @@ public:
 
   /**
    * Returns the page sequence frame associated with the frame hierarchy.
    * Returns nullptr if not a paginated view.
    */
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const = 0;
 
   /**
+  * Returns the canvas frame associated with the frame hierarchy.
+  * Returns nullptr if is XUL document.
+  */
+  virtual nsCanvasFrame* GetCanvasFrame() const = 0;
+
+  /**
    * Gets the real primary frame associated with the content object.
    *
    * In the case of absolutely positioned elements and floated elements,
    * the real primary frame is the frame that is out of the flow and not the
    * placeholder frame.
    */
   virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const = 0;
 
@@ -731,16 +741,37 @@ public:
   /**
    * Notification sent by a frame informing the pres shell that it is about to
    * be destroyed.
    * This allows any outstanding references to the frame to be cleaned up
    */
   virtual void NotifyDestroyingFrame(nsIFrame* aFrame) = 0;
 
   /**
+   * Get the touch caret, if it exists. AddRefs it.
+   */
+  virtual already_AddRefed<mozilla::TouchCaret> GetTouchCaret() const = 0;
+
+  /**
+   * Returns the touch caret element of the presshell.
+   */
+  virtual mozilla::dom::Element* GetTouchCaretElement() const = 0;
+
+  /**
+   * Will be called when touch caret visibility has changed.
+   * Set the mMayHaveTouchCaret flag to aSet.
+   */
+  virtual void SetMayHaveTouchCaret(bool aSet) = 0;
+
+  /**
+   * Get the mMayHaveTouchCaret flag.
+   */
+  virtual bool MayHaveTouchCaret() = 0;
+
+  /**
    * Get the caret, if it exists. AddRefs it.
    */
   virtual already_AddRefed<nsCaret> GetCaret() const = 0;
 
   /**
    * Invalidate the caret's current position if it's outside of its frame's
    * boundaries. This function is useful if you're batching selection
    * notifications and might remove the caret's frame out from under it.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2225,16 +2225,76 @@ nsLayoutUtils::TransformPoints(nsIFrame*
     // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct
     // answer instead of some inaccuracy multiplying a number by its reciprocal.
     aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) /
         devPixelsPerCSSPixelToFrame;
   }
   return TRANSFORM_SUCCEEDED;
 }
 
+nsLayoutUtils::TransformResult
+nsLayoutUtils::TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                              nsPoint& aPoint)
+{
+  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
+  if (!nearestCommonAncestor) {
+    return NO_COMMON_ANCESTOR;
+  }
+  gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
+  if (downToDest.IsSingular()) {
+    return NONINVERTIBLE_TRANSFORM;
+  }
+  downToDest.Invert();
+  gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
+
+  float devPixelsPerAppUnitFromFrame =
+    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
+  float devPixelsPerAppUnitToFrame =
+    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxPoint toDevPixels = downToDest.ProjectPoint(
+      upToAncestor.ProjectPoint(
+        gfxPoint(aPoint.x * devPixelsPerAppUnitFromFrame,
+                 aPoint.y * devPixelsPerAppUnitFromFrame)));
+  aPoint.x = toDevPixels.x / devPixelsPerAppUnitToFrame;
+  aPoint.y = toDevPixels.y / devPixelsPerAppUnitToFrame;
+  return TRANSFORM_SUCCEEDED;
+}
+
+nsLayoutUtils::TransformResult
+nsLayoutUtils::TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                             nsRect& aRect)
+{
+  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
+  if (!nearestCommonAncestor) {
+    return NO_COMMON_ANCESTOR;
+  }
+  gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
+  if (downToDest.IsSingular()) {
+    return NONINVERTIBLE_TRANSFORM;
+  }
+  downToDest.Invert();
+  gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
+
+  float devPixelsPerAppUnitFromFrame =
+    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
+  float devPixelsPerAppUnitToFrame =
+    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxRect toDevPixels = downToDest.ProjectRectBounds(
+    upToAncestor.ProjectRectBounds(
+      gfxRect(aRect.x * devPixelsPerAppUnitFromFrame,
+              aRect.y * devPixelsPerAppUnitFromFrame,
+              aRect.width * devPixelsPerAppUnitFromFrame,
+              aRect.height * devPixelsPerAppUnitFromFrame)));
+  aRect.x = toDevPixels.x / devPixelsPerAppUnitToFrame;
+  aRect.y = toDevPixels.y / devPixelsPerAppUnitToFrame;
+  aRect.width = toDevPixels.width / devPixelsPerAppUnitToFrame;
+  aRect.height = toDevPixels.height / devPixelsPerAppUnitToFrame;
+  return TRANSFORM_SUCCEEDED;
+}
+
 bool
 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
                                          gfx3DMatrix* aTransform)
 {
   // FIXME/bug 796690: we can sometimes compute a transform in these
   // cases, it just increases complexity considerably.  Punt for now.
   if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) {
     return false;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -753,16 +753,31 @@ public:
     TRANSFORM_SUCCEEDED,
     NO_COMMON_ANCESTOR,
     NONINVERTIBLE_TRANSFORM
   };
   static TransformResult TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame,
                                          uint32_t aPointCount, CSSPoint* aPoints);
 
   /**
+   * Same as above function, but transform points in app units and
+   * handle 1 point per call.
+   */
+  static TransformResult TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                                        nsPoint& aPoint);
+
+  /**
+   * Transforms a rect from aFromFrame to aToFrame. In app units.
+   * Returns the bounds of the actual rect if the transform requires rotation
+   * or anything complex like that.
+   */
+  static TransformResult TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
+                                       nsRect& aRect);
+
+  /**
    * Return true if a "layer transform" could be computed for aFrame,
    * and optionally return the computed transform.  The returned
    * transform is what would be set on the layer currently if a layers
    * transaction were opened at the time this helper is called.
    */
   static bool GetLayerTransformForFrame(nsIFrame* aFrame,
                                         gfx3DMatrix* aTransform);
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -70,16 +70,17 @@
 #include "nsIDOMNodeList.h"
 #include "nsIDOMElement.h"
 #include "nsRange.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsReadableUtils.h"
 #include "nsIPageSequenceFrame.h"
 #include "nsCaret.h"
+#include "TouchCaret.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsFrameManager.h"
 #include "nsXPCOM.h"
 #include "nsILayoutHistoryState.h"
 #include "nsILineIterator.h" // for ScrollContentIntoView
 #include "pldhash.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/PointerEventBinding.h"
@@ -696,16 +697,28 @@ nsIPresShell::FrameSelection()
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
 static bool sPointerEventEnabled = true;
+static bool sTouchCaretEnabled = false;
+
+/* static */ bool
+PresShell::TouchCaretPrefEnabled()
+{
+  static bool initialized = false;
+  if (!initialized) {
+    Preferences::AddBoolVarCache(&sTouchCaretEnabled, "touchcaret.enabled");
+    initialized = true;
+  }
+  return sTouchCaretEnabled;
+}
 
 PresShell::PresShell()
   : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
 {
   mSelection = nullptr;
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = new ReflowCountMgr();
   mReflowCountMgr->SetPresContext(mPresContext);
@@ -846,16 +859,21 @@ PresShell::Init(nsIDocument* aDocument,
   // this MUST happen after we set up our style set but before we create any
   // frames.
   mPresContext->CompatibilityModeChanged();
 
   // setup the preference style rules (no forced reflow), and do it
   // before creating any frames.
   SetPreferenceStyleRules(false);
 
+  if (TouchCaretPrefEnabled()) {
+    // Create touch caret handle
+    mTouchCaret = new TouchCaret(this);
+  }
+
   NS_ADDREF(mSelection = new nsFrameSelection());
 
   mSelection->Init(this, nullptr);
 
   // Important: this has to happen after the selection has been set up
 #ifdef SHOW_CARET
   // make the caret
   mCaret = new nsCaret();
@@ -1102,16 +1120,21 @@ PresShell::Destroy()
     mCaret->Terminate();
     mCaret = nullptr;
   }
 
   if (mSelection) {
     mSelection->DisconnectFromPresShell();
   }
 
+  if (mTouchCaret) {
+    mTouchCaret->Terminate();
+    mTouchCaret = nullptr;
+  }
+
   // release our pref style sheet, if we have one still
   ClearPreferenceStyleRules();
 
   mIsDestroying = true;
 
   // We can't release all the event content in
   // mCurrentEventContentStack here since there might be code on the
   // stack that will release the event content too. Double release
@@ -2129,16 +2152,23 @@ PresShell::NotifyDestroyingFrame(nsIFram
 }
 
 already_AddRefed<nsCaret> PresShell::GetCaret() const
 {
   nsRefPtr<nsCaret> caret = mCaret;
   return caret.forget();
 }
 
+// TouchCaret
+already_AddRefed<TouchCaret> PresShell::GetTouchCaret() const
+{
+  nsRefPtr<TouchCaret> touchCaret = mTouchCaret;
+  return touchCaret.forget();
+}
+
 void PresShell::MaybeInvalidateCaretPosition()
 {
   if (mCaret) {
     mCaret->InvalidateOutsideCaret();
   }
 }
 
 void PresShell::SetCaret(nsCaret *aNewCaret)
@@ -2152,24 +2182,31 @@ void PresShell::RestoreCaret()
 }
 
 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable)
 {
   bool oldEnabled = mCaretEnabled;
 
   mCaretEnabled = aInEnable;
 
-  if (mCaret && (mCaretEnabled != oldEnabled))
+  if (mCaretEnabled != oldEnabled)
   {
 /*  Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
     nsCOMPtr<nsIDOMSelection> domSel;
     if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
       mCaret->SetCaretDOMSelection(domSel);
 */
-    mCaret->SetCaretVisible(mCaretEnabled);
+
+    MOZ_ASSERT(mCaret || mTouchCaret);
+    if (mCaret) {
+      mCaret->SetCaretVisible(mCaretEnabled);
+    }
+    if (mTouchCaret) {
+      mTouchCaret->UpdateTouchCaret(mCaretEnabled);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly)
 {
   if (mCaret)
@@ -2467,16 +2504,75 @@ nsIPresShell::GetRootScrollFrameAsScroll
 
 nsIPageSequenceFrame*
 PresShell::GetPageSequenceFrame() const
 {
   nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
   return do_QueryFrame(frame);
 }
 
+nsCanvasFrame*
+PresShell::GetCanvasFrame() const
+{
+  nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
+  return do_QueryFrame(frame);
+}
+
+Element*
+PresShell::GetTouchCaretElement() const
+{
+  return GetCanvasFrame() ? GetCanvasFrame()->GetTouchCaretElement() : nullptr;
+}
+
+void
+PresShell::SetMayHaveTouchCaret(bool aSet)
+{
+  if (!mPresContext) {
+    return;
+  }
+
+  if (!mPresContext->IsRoot()) {
+    nsIPresShell* rootPresShell = GetRootPresShell();
+    if (rootPresShell) {
+      rootPresShell->SetMayHaveTouchCaret(aSet);
+    }
+    return;
+  }
+
+  nsIDocument* document = GetDocument();
+  if (document) {
+    nsPIDOMWindow* innerWin = document->GetInnerWindow();
+    if (innerWin) {
+      innerWin->SetMayHaveTouchCaret(aSet);
+    }
+  }
+}
+
+bool
+PresShell::MayHaveTouchCaret()
+{
+  if (!mPresContext) {
+    return false;
+  }
+
+  if (!mPresContext->IsRoot()) {
+    nsIPresShell* rootPresShell = GetRootPresShell();
+    return rootPresShell ? rootPresShell->MayHaveTouchCaret() : false;
+  }
+
+  nsIDocument* document = GetDocument();
+  if (document) {
+    nsPIDOMWindow* innerWin = document->GetInnerWindow();
+    if (innerWin) {
+      return innerWin->MayHaveTouchCaret();
+    }
+  }
+  return false;
+}
+
 void
 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
 {
 #ifdef DEBUG
   mUpdateCount++;
 #endif
   mFrameConstructor->BeginUpdate();
 
@@ -6531,16 +6627,39 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
   if (mIsDestroying ||
       (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
        aEvent->HasMouseEventMessage())) {
     return NS_OK;
   }
 
   RecordMouseLocation(aEvent);
+
+  // Determine whether event need to be consumed by touch caret or not.
+  if (TouchCaretPrefEnabled()) {
+    // We have to target the focus window because regardless of where the
+    // touch goes, we want to access the touch caret when user is typing on an
+    // editable element.
+    nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
+    nsCOMPtr<nsIDocument> retargetEventDoc = window ? window->GetExtantDoc() : nullptr;
+    nsCOMPtr<nsIPresShell> presShell = retargetEventDoc ?
+                                       retargetEventDoc->GetShell() :
+                                       nullptr;
+    nsRefPtr<TouchCaret> touchCaret = presShell ? presShell->GetTouchCaret() : nullptr;
+    if (touchCaret) {
+      *aEventStatus = touchCaret->HandleEvent(aEvent);
+      if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
+        // If the event is consumed by the touch caret, cancel APZC panning by
+        // setting mMultipleActionsPrevented.
+        aEvent->mFlags.mMultipleActionsPrevented = true;
+        return NS_OK;
+      }
+    }
+  }
+
   if (sPointerEventEnabled) {
     UpdateActivePointerState(aEvent);
   }
 
   if (!nsContentUtils::IsSafeToRunScript())
     return NS_OK;
 
   nsIContent* capturingContent =
@@ -8105,17 +8224,17 @@ PresShell::Freeze()
 {
   mUpdateImageVisibilityEvent.Revoke();
 
   MaybeReleaseCapturingContent();
 
   mDocument->EnumerateFreezableElements(FreezeElement, nullptr);
 
   if (mCaret) {
-    mCaret->SetCaretVisible(false);
+    SetCaretEnabled(false);
   }
 
   mPaintingSuppressed = true;
 
   if (mDocument) {
     mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
   }
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -62,16 +62,19 @@ class PresShell : public nsIPresShell,
 public:
   PresShell();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
+  // Touch caret preference
+  static bool TouchCaretPrefEnabled();
+
   void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
             nsViewManager* aViewManager, nsStyleSet* aStyleSet,
             nsCompatibility aCompatMode);
   virtual void Destroy() MOZ_OVERRIDE;
   virtual void MakeZombie() MOZ_OVERRIDE;
 
   virtual nsresult SetPreferenceStyleRules(bool aForceReflow) MOZ_OVERRIDE;
 
@@ -85,16 +88,17 @@ public:
   NS_IMETHOD RepaintSelection(SelectionType aType) MOZ_OVERRIDE;
 
   virtual void BeginObservingDocument() MOZ_OVERRIDE;
   virtual void EndObservingDocument() MOZ_OVERRIDE;
   virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsresult ResizeReflowOverride(nscoord aWidth, nscoord aHeight) MOZ_OVERRIDE;
   virtual nsIPageSequenceFrame* GetPageSequenceFrame() const MOZ_OVERRIDE;
+  virtual nsCanvasFrame* GetCanvasFrame() const MOZ_OVERRIDE;
   virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const MOZ_OVERRIDE;
 
   virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const MOZ_OVERRIDE;
   virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
                                             nsFrameState aBitToAdd) MOZ_OVERRIDE;
   virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) MOZ_OVERRIDE;
   virtual void CancelAllPendingReflows() MOZ_OVERRIDE;
   virtual bool IsSafeToFlush() const MOZ_OVERRIDE;
@@ -202,16 +206,21 @@ public:
   virtual void WillPaintWindow() MOZ_OVERRIDE;
   virtual void DidPaintWindow() MOZ_OVERRIDE;
   virtual void ScheduleViewManagerFlush(PaintType aType = PAINT_DEFAULT) MOZ_OVERRIDE;
   virtual void DispatchSynthMouseMove(mozilla::WidgetGUIEvent* aEvent,
                                       bool aFlushOnHoverChange) MOZ_OVERRIDE;
   virtual void ClearMouseCaptureOnView(nsView* aView) MOZ_OVERRIDE;
   virtual bool IsVisible() MOZ_OVERRIDE;
 
+  // touch caret
+  virtual already_AddRefed<mozilla::TouchCaret> GetTouchCaret() const MOZ_OVERRIDE;
+  virtual mozilla::dom::Element* GetTouchCaretElement() const MOZ_OVERRIDE;
+  virtual void SetMayHaveTouchCaret(bool aSet) MOZ_OVERRIDE;
+  virtual bool MayHaveTouchCaret() MOZ_OVERRIDE;
   // caret handling
   virtual already_AddRefed<nsCaret> GetCaret() const MOZ_OVERRIDE;
   virtual void MaybeInvalidateCaretPosition() MOZ_OVERRIDE;
   NS_IMETHOD SetCaretEnabled(bool aInEnable) MOZ_OVERRIDE;
   NS_IMETHOD SetCaretReadOnly(bool aReadOnly) MOZ_OVERRIDE;
   NS_IMETHOD GetCaretEnabled(bool *aOutEnabled) MOZ_OVERRIDE;
   NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility) MOZ_OVERRIDE;
   NS_IMETHOD GetCaretVisible(bool *_retval) MOZ_OVERRIDE;
@@ -755,16 +764,19 @@ private:
 protected:
   nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
   nsCOMPtr<nsIContent>      mLastAnchorScrolledTo;
   nsRefPtr<nsCaret>         mCaret;
   nsRefPtr<nsCaret>         mOriginalCaret;
   nsCallbackEventRequest*   mFirstCallbackEventRequest;
   nsCallbackEventRequest*   mLastCallbackEventRequest;
 
+  // TouchCaret
+  nsRefPtr<mozilla::TouchCaret> mTouchCaret;
+
   // This timer controls painting suppression.  Until it fires
   // or all frames are constructed, we won't paint anything but
   // our <body> background and scrollbars.
   nsCOMPtr<nsITimer>        mPaintSuppressionTimer;
 
   // At least on Win32 and Mac after interupting a reflow we need to post
   // the resume reflow event off a timer to avoid event starvation because
   // posted messages are processed before other messages when the modal
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1015563-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="display: inline-flex;">
+<body style="margin: -3642924795px; flex-grow: 1;"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1015563-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <div style="display: flex">
+    <div style="margin: -3642924795px; flex-grow: 1;"></div>
+  </div>
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -526,9 +526,11 @@ load 943509-1.html
 asserts(3-6) load 944909-1.html
 test-pref(layout.css.sticky.enabled,true) load 949932.html
 load 973701-1.xhtml
 load 973701-2.xhtml
 load 986899.html
 load 1001233.html
 load 1001258-1.html
 pref(layout.css.grid.enabled,true) load 1015562.html
+asserts(2) load 1015563-1.html
+asserts(2) load 1015563-2.html
 load outline-on-frameset.xhtml
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -7,22 +7,27 @@
 
 #include "nsCanvasFrame.h"
 #include "nsContainerFrame.h"
 #include "nsCSSRendering.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
+#include "nsPresShell.h"
 #include "nsIPresShell.h"
 #include "nsDisplayList.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsFrameManager.h"
 #include "gfxPlatform.h"
-
+// for touchcaret
+#include "nsContentList.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsContentUtils.h"
+#include "nsStyleSet.h"
 // for focus
 #include "nsIScrollableFrame.h"
 #ifdef DEBUG_CANVAS_FOCUS
 #include "nsIDocShell.h"
 #endif
 
 //#define DEBUG_CANVAS_FOCUS
 
@@ -35,27 +40,73 @@ NS_NewCanvasFrame(nsIPresShell* aPresShe
 {
   return new (aPresShell) nsCanvasFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
 
 NS_QUERYFRAME_HEAD(nsCanvasFrame)
   NS_QUERYFRAME_ENTRY(nsCanvasFrame)
+  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
+nsresult
+nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
+{
+  // We won't create touch caret element if preference is not enabled.
+  if (!PresShell::TouchCaretPrefEnabled()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+
+  // Create and append touch caret frame.
+  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
+                                                 kNameSpaceID_XHTML,
+                                                 nsIDOMNode::ELEMENT_NODE);
+  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = NS_NewHTMLElement(getter_AddRefs(mTouchCaretElement), nodeInfo.forget(),
+                                mozilla::dom::NOT_FROM_PARSER);
+  NS_ENSURE_SUCCESS(rv, rv);
+  aElements.AppendElement(mTouchCaretElement);
+
+  // Add a _moz_anonclass attribute as touch caret selector.
+  ErrorResult er;
+  mTouchCaretElement->SetAttribute(NS_LITERAL_STRING("_moz_anonclass"),
+                                   NS_LITERAL_STRING("mozTouchCaret"), er);
+  NS_ENSURE_SUCCESS(er.ErrorCode(), er.ErrorCode());
+
+  // Set touch caret to visibility: hidden by default.
+  nsAutoString classValue;
+  classValue.AppendLiteral("moz-touchcaret hidden");
+  rv = mTouchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                                   classValue, true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+nsCanvasFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, uint32_t aFilter)
+{
+  aElements.MaybeAppendElement(mTouchCaretElement);
+}
+
 void
 nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   nsIScrollableFrame* sf =
     PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
   if (sf) {
     sf->RemoveScrollPositionListener(this);
   }
 
+  nsContentUtils::DestroyAnonymousContent(&mTouchCaretElement);
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 void
 nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
 {
   if (mDoPaintFocus) {
     mDoPaintFocus = false;
@@ -93,19 +144,24 @@ nsCanvasFrame::SetInitialChildList(Child
   nsContainerFrame::SetInitialChildList(aListID, aChildList);
 }
 
 void
 nsCanvasFrame::AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList)
 {
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
-  MOZ_ASSERT(mFrames.IsEmpty(), "already have a child frame");
-  MOZ_ASSERT(aFrameList.FirstChild() == aFrameList.LastChild(),
-             "Only one principal child frame allowed");
+  if (!mFrames.IsEmpty()) {
+    for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
+      // We only allow native anonymous child frame for touch caret,
+      // which its placeholder is added to the Principal child lists.
+      MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
+                 "invalid child list");
+    }
+  }
   nsFrame::VerifyDirtyBitSet(aFrameList);
   nsContainerFrame::AppendFrames(aListID, aFrameList);
 }
 
 void
 nsCanvasFrame::InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
                             nsFrameList&    aFrameList)
@@ -116,17 +172,16 @@ nsCanvasFrame::InsertFrames(ChildListID 
   AppendFrames(aListID, aFrameList);
 }
 
 void
 nsCanvasFrame::RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame)
 {
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
-  MOZ_ASSERT(aOldFrame == mFrames.FirstChild(), "unknown aOldFrame");
   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
 }
 #endif
 
 nsRect nsCanvasFrame::CanvasArea() const
 {
   // Not clear which overflow rect we want here, but it probably doesn't
   // matter.
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -8,29 +8,31 @@
 #ifndef nsCanvasFrame_h___
 #define nsCanvasFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "nsContainerFrame.h"
 #include "nsIScrollPositionListener.h"
 #include "nsDisplayList.h"
+#include "nsIAnonymousContentCreator.h"
 
 class nsPresContext;
 class nsRenderingContext;
 
 /**
  * Root frame class.
  *
  * The root frame is the parent frame for the document element's frame.
  * It only supports having a single child frame which must be an area
  * frame
  */
 class nsCanvasFrame : public nsContainerFrame,
-                      public nsIScrollPositionListener
+                      public nsIScrollPositionListener,
+                      public nsIAnonymousContentCreator
 {
 public:
   nsCanvasFrame(nsStyleContext* aContext)
   : nsContainerFrame(aContext),
     mDoPaintFocus(false),
     mAddedScrollPositionListener(false) {}
 
   NS_DECL_QUERYFRAME_TARGET(nsCanvasFrame)
@@ -59,16 +61,26 @@ public:
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
     return nsContainerFrame::IsFrameOfType(aFlags &
              ~(nsIFrame::eCanContainOverflowContainers));
   }
 
+  // nsIAnonymousContentCreator
+  virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE;
+  virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, uint32_t aFilter) MOZ_OVERRIDE;
+
+  // Touch caret handle function
+  mozilla::dom::Element* GetTouchCaretElement() const
+  {
+     return mTouchCaretElement;
+  }
+
   /** SetHasFocus tells the CanvasFrame to draw with focus ring
    *  @param aHasFocus true to show focus ring, false to hide it
    */
   NS_IMETHOD SetHasFocus(bool aHasFocus);
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
@@ -106,16 +118,18 @@ public:
                                       nsIContent** aContent) MOZ_OVERRIDE;
 
   nsRect CanvasArea() const;
 
 protected:
   // Data members
   bool                      mDoPaintFocus;
   bool                      mAddedScrollPositionListener;
+
+  nsCOMPtr<mozilla::dom::Element> mTouchCaretElement;
 };
 
 /**
  * Override nsDisplayBackground methods so that we pass aBGClipRect to
  * PaintBackground, covering the whole overflow area.
  * We can also paint an "extra background color" behind the normal
  * background.
  */
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1754,22 +1754,26 @@ FlexLine::ResolveFlexibleLengths(nscoord
         item->SetMainSize(item->GetFlexBaseSize());
       }
       availableFreeSpace -= item->GetMainSize();
     }
 
     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
            (" available free space = %d\n", availableFreeSpace));
 
-    MOZ_ASSERT((isUsingFlexGrow && availableFreeSpace >= 0) ||
-               (!isUsingFlexGrow && availableFreeSpace <= 0),
-               "The sign of our free space should never disagree with the "
-               "type of flexing (grow/shrink) that we're doing. Any potential "
-               "disagreement should've made us use the other type of flexing, "
-               "or should've been resolved in FreezeItemsEarly");
+
+    // The sign of our free space should agree with the type of flexing
+    // (grow/shrink) that we're doing (except if we've had integer overflow;
+    // then, all bets are off). Any disagreement should've made us use the
+    // other type of flexing, or should've been resolved in FreezeItemsEarly.
+    // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
+    // assertion to be fatal except in documents with enormous lengths.
+    NS_ASSERTION((isUsingFlexGrow && availableFreeSpace >= 0) ||
+                 (!isUsingFlexGrow && availableFreeSpace <= 0),
+                 "availableFreeSpace's sign should match isUsingFlexGrow");
 
     // If we have any free space available, give each flexible item a portion
     // of availableFreeSpace.
     if (availableFreeSpace != 0) {
       // The first time we do this, we initialize origAvailableFreeSpace.
       if (!isOrigAvailFreeSpaceInitialized) {
         origAvailableFreeSpace = availableFreeSpace;
         isOrigAvailFreeSpaceInitialized = true;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -48,16 +48,17 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsDOMClassInfoID.h"
 
 //included for desired x position;
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsCaret.h"
+#include "TouchCaret.h"
 
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsITimer.h"
 #include "nsFrameManager.h"
 // notifications
 #include "nsIDOMDocument.h"
@@ -688,16 +689,24 @@ void
 nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
 {
   mShell = aShell;
   mMouseDownState = false;
   mDesiredXSet = false;
   mLimiter = aLimiter;
   mCaretMovementStyle =
     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
+  // Set touch caret as selection listener
+  nsRefPtr<TouchCaret> touchCaret = mShell->GetTouchCaret();
+  if (touchCaret) {
+    int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    if (mDomSelections[index]) {
+      mDomSelections[index]->AddSelectionListener(touchCaret);
+    }
+  }
 }
 
 nsresult
 nsFrameSelection::MoveCaret(uint32_t          aKeycode,
                             bool              aContinueSelection,
                             nsSelectionAmount aAmount)
 {
   bool visualMovement =
@@ -3006,16 +3015,22 @@ nsFrameSelection::SetDelayedCaretData(Wi
   } else {
     mDelayedMouseEventValid = false;
   }
 }
 
 void
 nsFrameSelection::DisconnectFromPresShell()
 {
+  // Remove touch caret as selection listener
+  nsRefPtr<TouchCaret> touchCaret = mShell->GetTouchCaret();
+  if (touchCaret) {
+    int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    mDomSelections[index]->RemoveSelectionListener(touchCaret);
+  }
   StopAutoScrollTimer();
   for (int32_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
     mDomSelections[i]->Clear(nullptr);
   }
   mShell = nullptr;
 }
 
 //END nsISelection interface implementations
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1-ref.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1-ref.html
@@ -2,17 +2,17 @@
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       background: teal;
       border: 1px dashed black;
     }
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1a.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: row".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row-reverse;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1b.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: row-reverse".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: row-reverse;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c-wrap.html
@@ -6,17 +6,17 @@
      "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1c.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: column".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column;
       background: teal;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d-wrap.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d-wrap.html
@@ -6,17 +6,17 @@
      and "flex-wrap: wrap".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column-reverse;
       flex-wrap: wrap;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-1d.html
@@ -5,17 +5,17 @@
      child, with the flex container having "flex-direction: column-reverse".
 -->
 <html>
   <head>
     <style>
     .multicol {
       height: 10px;
       width: 100px;
-      -moz-column-width: 20px;
+      -moz-column-width: 30px;
       -moz-column-fill: auto;
       border: 2px solid orange;
       margin-bottom: 15px; /* (just for spacing between testcases) */
     }
     .flexContainer {
       display: flex;
       flex-direction: column-reverse;
       justify-content: flex-end;
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2-ref.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2-ref.html
@@ -19,17 +19,17 @@
       background: yellow;
       border: 1px dashed black;
     }
     .item {
       width: 40px;
       height: 15px;
       border: 1px dotted teal;
       margin: 1px;
-      font: 10px;
+      font: 10px serif;
       float: left;
     }
     </style>
   </head>
   <body>
     <!-- auto-height container: -->
     <div class="multicol">
       <div class="flexContainer" style="height: 38px">
--- a/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2.html
+++ b/layout/reftests/flexbox/pagination/flexbox-unbreakable-child-2.html
@@ -23,17 +23,17 @@
       background: yellow;
       border: 1px dashed black;
     }
     .item {
       width: 40px;
       height: 15px;
       border: 1px dotted teal;
       margin: 1px;
-      font: 10px;
+      font: 10px serif;
     }
     </style>
   </head>
   <body>
     <!-- auto-height container: -->
     <div class="multicol">
       <div class="flexContainer">
         <div class="item">1</div>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-collapsed-item-baseline-1-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-collapsed-item-baseline-1-ref.html
@@ -18,16 +18,17 @@
       width: 50px;
       background: yellow;
       border: 1px dotted black;
       margin: 5px;
       align-items: flex-start;
     }
     .hiddenItemForSizing {
       width: 0;
+      min-width: 0; /* disable default min-width:auto behavior */
       color: transparent;
       align-self: baseline;
     }
     .largeFont {
       font-size: 20px;
       background: lightblue;
       /* Our flex items get padding on opposite sides (top/bottom) so that they
          produce a large combined height when baseline-aligned: */
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-2.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-2.html
@@ -28,20 +28,22 @@
       justify-content: space-between;
       width: 70px;
       padding: 2px;
       margin-bottom: 2px;
     }
     .item1 {
       background: lightblue;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
     }
     .item2 {
       background: yellow;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
     }
   </style>
 </head>
 <body>
   <!-- This container has two flex items, the first of which has content
        sticking out & overlapping the second.  If they're painting atomically
        (and in the right order), the second item's background should cover the
        first item's overflowing content. -->
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-3.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-items-as-stacking-contexts-3.html
@@ -24,16 +24,17 @@
       width: 70px;
       height: 20px;
       padding: 2px;
       margin-bottom: 2px;
     }
     .item1 {
       background: lightblue;
       width: 30px;
+      min-width: 0; /* disable default min-width:auto behavior */
       padding: 2px;
     }
     .item2 {
       background: yellow;
       width: 30px;
       padding: 2px;
     }
     .grandchildA {
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -278,8 +278,46 @@ parsererror|sourcetext {
   white-space: pre;
   font-family: -moz-fixed;
   margin-top: 2em;
   margin-bottom: 1em;
   color: red;
   font-weight: bold;
   font-size: 12pt;
 }
+
+div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+  background-image: url("resource://gre/res/text_selection_handle.png");
+  position: absolute;
+  width: 19px;
+  height: 24px;
+  margin-left: -10px;
+  background-position: center center;
+  z-index: 2147483647;
+}
+
+@media (min-resolution: 1.5dppx) {
+  div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+    background-image: url("resource://gre/res/text_selection_handle@1.5.png");
+    position: absolute;
+    width: 29px;
+    height: 36px;
+    margin-left: -15px;
+    background-position: center center;
+    z-index: 2147483647;
+  }
+}
+
+@media (min-resolution: 2dppx) {
+  div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret {
+    background-image: url("resource://gre/res/text_selection_handle@2.png");
+    position: absolute;
+    width: 38px;
+    height: 48px;
+    margin-left: -19px;
+    background-position: center center;
+    z-index: 2147483647;
+  }
+}
+
+div[\_moz_anonclass="mozTouchCaret"].moz-touchcaret.hidden {
+  visibility: hidden;
+}
--- a/media/libstagefright/binding/Adts.cpp
+++ b/media/libstagefright/binding/Adts.cpp
@@ -36,17 +36,17 @@ bool
 Adts::ConvertEsdsToAdts(uint16_t aChannelCount, int8_t aFrequencyIndex,
                         int8_t aProfile, MP4Sample* aSample)
 {
   static const int kADTSHeaderSize = 7;
 
   size_t newSize = aSample->size + kADTSHeaderSize;
 
   // ADTS header uses 13 bits for packet size.
-  if (newSize >= (1 << 13) || aChannelCount < 0 || aChannelCount > 15 ||
+  if (newSize >= (1 << 13) || aChannelCount > 15 ||
       aFrequencyIndex < 0 || aProfile < 1 || aProfile > 4)
     return false;
 
   Array<uint8_t, kADTSHeaderSize> header;
   header[0] = 0xff;
   header[1] = 0xf1;
   header[2] =
     ((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2);
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -25,28 +25,16 @@ elif CONFIG['OS_TARGET'] in ('DragonFly'
         DEFINES['ENODATA'] = '-0x80000003'
     if CONFIG['OS_TARGET'] == 'OpenBSD':
         DEFINES['EBADMSG'] = '-0x80000006'
     DEFINES['HAVE_SYS_UIO_H'] = True
     DEFINES['off64_t'] = 'off_t'
     LOCAL_INCLUDES += [ 'ports/bsd/include' ]
 else:
     DEFINES['HAVE_SYS_UIO_H'] = True
-    if CONFIG['OS_TARGET'] == 'Linux':
-        CXXFLAGS += ['-Wno-deprecated',
-                     '-Wno-format',
-                     '-Wno-dynamic-class-memaccess',
-                     '-Wno-tautological-compare',
-                     '-Wno-tautological-constant-out-of-range-compare',
-                     '-Wno-unused-variable',
-                     '-Wno-unused-function',
-                     '-Wno-sign-compare',
-                     '-Wno-comment',
-                     '-Wno-incompatible-pointer-types-discards-qualifiers'
-        ]
 
 if CONFIG['OS_TARGET'] != 'Android':
     DEFINES['FAKE_LOG_DEVICE'] = True
     SOURCES += [
         'system/core/liblog/fake_log_device.c',
     ]
     UNIFIED_SOURCES += [
         'system/core/libcutils/strdup16to8.c',
@@ -100,8 +88,37 @@ LOCAL_INCLUDES += [
     'frameworks/av/media/libstagefright/',
     'stubs/empty',
     'stubs/include',
     'stubs/include/media/stagefright/foundation',
     'system/core/include',
 ]
 
 FINAL_LIBRARY = 'gklayout'
+
+# Suppress warnings in third-party code.
+if CONFIG['_MSC_VER']:
+    CFLAGS += [
+        '-wd4013', # 'function' undefined; assuming extern returning int
+        '-wd4101', # unreferenced local variable
+    ]
+    CXXFLAGS += [
+        '-wd4018', # '<' : signed/unsigned mismatch
+        '-wd4275', # non dll-interface class used as base for dll-interface class
+        '-wd4305', # '=' : truncation from 'double' to 'float'
+        '-wd4309', # '=' : truncation of constant value
+        '-wd4355', # 'this' : used in base member initializer list
+        '-wd4804', # '>' : unsafe use of type 'bool' in operation
+    ]
+elif CONFIG['GNU_CXX']:
+    CFLAGS += [
+        '-Wno-comment',
+        '-Wno-declaration-after-statement',
+        '-Wno-sign-compare'
+    ]
+    CXXFLAGS += [
+        '-Wno-format',
+        '-Wno-multichar',
+        '-Wno-sign-compare',
+        '-Wno-unused',
+    ]
+    if CONFIG['CLANG_CXX']:
+        CXXFLAGS += ['-Wno-tautological-constant-out-of-range-compare']
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -401,16 +401,27 @@ public:
 
     peerIdentity.SetIsVoid(true);
     return NS_OK;
   }
 
 #ifdef MOZILLA_INTERNAL_API
   const PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; }
   nsresult SetPeerIdentity(const nsAString& peerIdentity);
+
+  const std::string& GetIdAsAscii() const
+  {
+    return mName;
+  }
+
+  nsresult GetId(nsAString& id)
+  {
+    id = NS_ConvertASCIItoUTF16(mName.c_str());
+    return NS_OK;
+  }
 #endif
 
   // this method checks to see if we've made a promise to protect media.
   bool PrivacyRequested() const { return mPrivacyRequested; }
 
   NS_IMETHODIMP GetFingerprint(char** fingerprint);
   void GetFingerprint(nsAString& fingerprint)
   {
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -127,16 +127,17 @@ static void GetLogging_s(
                           NS_DISPATCH_NORMAL);
 }
 
 
 void
 WebrtcGlobalInformation::GetAllStats(
   const GlobalObject& aGlobal,
   WebrtcGlobalStatisticsCallback& aStatsCallback,
+  const Optional<nsAString>& pcIdFilter,
   ErrorResult& aRv)
 {
   if (!NS_IsMainThread()) {
     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
     return;
   }
 
   nsresult rv;
@@ -159,20 +160,23 @@ WebrtcGlobalInformation::GetAllStats(
   // the API consumer doesn't care why there are no PeerConnectionImpl.
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();
   if (ctx) {
     for (auto p = ctx->mPeerConnections.begin();
          p != ctx->mPeerConnections.end();
          ++p) {
       MOZ_ASSERT(p->second);
 
-      if (p->second->HasMedia()) {
-        queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
-        p->second->BuildStatsQuery_m(nullptr, // all tracks
-                                     queries->back());
+      if (!pcIdFilter.WasPassed() ||
+          pcIdFilter.Value().EqualsASCII(p->second->GetIdAsAscii().c_str())) {
+        if (p->second->HasMedia()) {
+          queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
+          p->second->BuildStatsQuery_m(nullptr, // all tracks
+                                       queries->back());
+        }
       }
     }
   }
 
   // CallbackObject does not support threadsafe refcounting, and must be
   // destroyed on main.
   nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle(
     new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback));
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
@@ -1,16 +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/. */
 
 #ifndef _WEBRTC_GLOBAL_INFORMATION_H_
 #define _WEBRTC_GLOBAL_INFORMATION_H_
 
 #include "nsString.h"
+#include "mozilla/dom/BindingDeclarations.h" // for Optional
 
 namespace sipcc {
 class PeerConnectionImpl;
 }
 
 namespace mozilla {
 class ErrorResult;
 
@@ -19,16 +20,17 @@ class GlobalObject;
 class WebrtcGlobalStatisticsCallback;
 class WebrtcGlobalLoggingCallback;
 
 class WebrtcGlobalInformation
 {
 public:
   static void GetAllStats(const GlobalObject& aGlobal,
                           WebrtcGlobalStatisticsCallback& aStatsCallback,
+                          const Optional<nsAString>& pcIdFilter,
                           ErrorResult& aRv);
 
   static void GetLogging(const GlobalObject& aGlobal,
                          const nsAString& aPattern,
                          WebrtcGlobalLoggingCallback& aLoggingCallback,
                          ErrorResult& aRv);
 
   static void StoreLongTermICEStatistics(sipcc::PeerConnectionImpl& aPc);
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -411,29 +411,16 @@ abstract public class BrowserApp extends
         final ContentValues values = new ContentValues();
         values.put(ReadingListItems.URL, message.optString("url"));
         values.put(ReadingListItems.TITLE, message.optString("title"));
         values.put(ReadingListItems.LENGTH, message.optInt("length"));
         values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
         return values;
     }
 
-    void handleReaderRemoved(final String url) {
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
-                showToast(R.string.page_removed, Toast.LENGTH_SHORT);
-
-                final int count = BrowserDB.getReadingListCount(getContentResolver());
-                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:ListCountUpdated", Integer.toString(count)));
-            }
-        });
-    }
-
     private void handleReaderFaviconRequest(final String url) {
         (new UiAsyncTask<Void, Void, String>(ThreadUtils.getBackgroundHandler()) {
             @Override
             public String doInBackground(Void... params) {
                 return Favicons.getFaviconURLForPageURL(url);
             }
 
             @Override
@@ -537,17 +524,16 @@ abstract public class BrowserApp extends
             "CharEncoding:Data",
             "CharEncoding:State",
             "Feedback:LastUrl",
             "Feedback:MaybeLater",
             "Feedback:OpenPlayStore",
             "Menu:Add",
             "Menu:Remove",
             "Reader:ListStatusRequest",
-            "Reader:Removed",
             "Reader:Share",
             "Settings:Show",
             "Telemetry:Gather",
             "Updater:Launch");
 
         Distribution.init(this);
         JavaAddonManager.getInstance().init(getApplicationContext());
         mSharedPreferencesHelper = new SharedPreferencesHelper(getApplicationContext());
@@ -895,17 +881,16 @@ abstract public class BrowserApp extends
             "CharEncoding:Data",
             "CharEncoding:State",
             "Feedback:LastUrl",
             "Feedback:MaybeLater",
             "Feedback:OpenPlayStore",
             "Menu:Add",
             "Menu:Remove",
             "Reader:ListStatusRequest",
-            "Reader:Removed",
             "Reader:Share",
             "Settings:Show",
             "Telemetry:Gather",
             "Updater:Launch");
 
         if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
             if (nfc != null) {
@@ -1226,20 +1211,16 @@ abstract public class BrowserApp extends
                 public void run() {
                     removeAddonMenuItem(id);
                 }
             });
 
         } else if ("Reader:ListStatusRequest".equals(event)) {
             handleReaderListStatusRequest(message.getString("url"));
 
-        } else if ("Reader:Removed".equals(event)) {
-            final String url = message.getString("url");
-            handleReaderRemoved(url);
-
         } else if ("Reader:Share".equals(event)) {
             final String title = message.getString("title");
             final String url = message.getString("url");
             GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title);
 
         } else if ("Settings:Show".equals(event)) {
             final String resource =
                     message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null);
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -494,20 +494,16 @@ public abstract class GeckoApp
 
         return super.onKeyDown(keyCode, event);
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
-        if (mToast != null) {
-            mToast.onSaveInstanceState(outState);
-        }
-
         outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
         outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
     }
 
     void handleClearHistory() {
         BrowserDB.clearHistory(getContentResolver());
     }
 
--- a/mobile/android/base/Telemetry.java
+++ b/mobile/android/base/Telemetry.java
@@ -149,18 +149,25 @@ public class Telemetry {
     private static String getSessionName(final Session session, final String sessionNameSuffix) {
         if (sessionNameSuffix != null) {
             return session.toString() + ":" + sessionNameSuffix;
         } else {
             return session.toString();
         }
     }
 
+    /**
+     * @param method A non-null method (if null is desired, consider using Method.NONE)
+     */
     private static void sendUIEvent(final String eventName, final Method method,
             final long timestamp, final String extras) {
+        if (method == null) {
+            throw new IllegalArgumentException("Expected non-null method - use Method.NONE?");
+        }
+
         Log.d(LOGTAG, "SendUIEvent: event = " + eventName + " method = " + method +
                 " timestamp = " + timestamp + " extras = " + extras);
         final GeckoEvent geckoEvent = GeckoEvent.createTelemetryUIEvent(
                 eventName, method.toString(), timestamp, extras);
         GeckoAppShell.sendEventToGecko(geckoEvent);
     }
 
     public static void sendUIEvent(final Event event, final Method method, final long timestamp,
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -21,16 +21,17 @@ import org.mozilla.gecko.GeckoApplicatio
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.LocaleManager;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.TelemetryContract.Method;
 import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
 import org.mozilla.gecko.home.HomePanelPicker;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.FloatingHintEditText;
@@ -849,19 +850,19 @@ OnSharedPreferenceChangeListener
 
                 if (TextUtils.isEmpty(newValue)) {
                     BrowserLocaleManager.getInstance().resetToSystemLocale(context);
                     Telemetry.sendUIEvent(TelemetryContract.Event.LOCALE_BROWSER_RESET);
                 } else {
                     if (null == localeManager.setSelectedLocale(context, newValue)) {
                         localeManager.updateConfiguration(context, Locale.getDefault());
                     }
-                    Telemetry.sendUIEvent(TelemetryContract.Event.LOCALE_BROWSER_UNSELECTED, null,
+                    Telemetry.sendUIEvent(TelemetryContract.Event.LOCALE_BROWSER_UNSELECTED, Method.NONE,
                                           currentLocale == null ? "unknown" : currentLocale);
-                    Telemetry.sendUIEvent(TelemetryContract.Event.LOCALE_BROWSER_SELECTED, null, newValue);
+                    Telemetry.sendUIEvent(TelemetryContract.Event.LOCALE_BROWSER_SELECTED, Method.NONE, newValue);
                 }
 
                 ThreadUtils.postToUiThread(new Runnable() {
                     @Override
                     public void run() {
                         onLocaleChanged(Locale.getDefault());
                     }
                 });
--- a/mobile/android/base/preferences/PanelsPreferenceCategory.java
+++ b/mobile/android/base/preferences/PanelsPreferenceCategory.java
@@ -1,16 +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/. */
 
 package org.mozilla.gecko.preferences;
 
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.TelemetryContract.Method;
 import org.mozilla.gecko.home.HomeConfig;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomeConfig.State;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -157,17 +158,17 @@ public class PanelsPreferenceCategory ex
         final String defaultPanelId = mConfigEditor.getDefaultPanelId();
         if (defaultPanelId != null && defaultPanelId.equals(id)) {
             return;
         }
 
         mConfigEditor.setDefault(id);
         mConfigEditor.apply();
 
-        Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_SET_DEFAULT, null, id);
+        Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_SET_DEFAULT, Method.NONE, id);
     }
 
     @Override
     protected void onPrepareForRemoval() {
         if (mLoadTask != null) {
             mLoadTask.cancel(true);
         }
     }
--- a/mobile/android/base/widget/ButtonToast.java
+++ b/mobile/android/base/widget/ButtonToast.java
@@ -33,23 +33,22 @@ public class ButtonToast {
     private final static String LOGTAG = "GeckoButtonToast";
 
     private final static int TOAST_DURATION = 5000;
 
     private final View mView;
     private final TextView mMessageView;
     private final Button mButton;
     private final Handler mHideHandler = new Handler();
-
-    private final LinkedList<Toast> mQueue = new LinkedList<Toast>();
     private Toast mCurrentToast;
 
     public enum ReasonHidden {
         CLICKED,
         TIMEOUT,
+        REPLACED,
         STARTUP
     }
 
     // State objects