Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Thu, 27 Sep 2018 12:17:59 +0300
changeset 438425 ba2b3ed1eb96065af623415366a7729cac877350
parent 438424 0b8b09f59546922daee91f342fbe38abbfdff56c (current diff)
parent 438408 12f5280f25c862decd0917eebbcf685853575678 (diff)
child 438426 11dd717f4a3ef27b0d64047c582c046bcd4db622
child 438550 c58aecd60cf303a396d8d7fea6225cbf50f64a44
push id108317
push userebalazs@mozilla.com
push dateThu, 27 Sep 2018 09:25:00 +0000
treeherdermozilla-inbound@11dd717f4a3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
ba2b3ed1eb96 / 64.0a1 / 20180927100044 / files
nightly linux64
ba2b3ed1eb96 / 64.0a1 / 20180927100044 / files
nightly mac
ba2b3ed1eb96 / 64.0a1 / 20180927100044 / files
nightly win32
ba2b3ed1eb96 / 64.0a1 / 20180927100044 / files
nightly win64
ba2b3ed1eb96 / 64.0a1 / 20180927100044 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -63,30 +63,31 @@
 
 .urlbarView-action-icon,
 .urlbarView-favicon {
   display: inline-block;
   vertical-align: middle;
   width: 16px;
   height: 16px;
   margin-inline-end: .6em;
+  background-repeat: no-repeat;
+  background-size: contain;
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 0.6;
 }
 
 .urlbarView-favicon {
   border-radius: 8px;
   background: currentcolor;
   opacity: 0.6;
 }
 
-.urlbarView-row[action=switch-to-tab] > .row-inner > .action-icon {
-  height: 8px;
-  border-top-left-radius: 3px;
-  border-top-right-radius: 3px;
-  background: currentcolor;
-  opacity: 0.5;
+.urlbarView-row[action=switch-to-tab] > .urlbarView-row-inner > .urlbarView-action-icon {
+  background-image: url(chrome://browser/skin/tab.svg);
 }
 
 .urlbarView-title {
   font-size: 1.05em;
 }
 
 .urlbarView-title::after {
   content: "\2014";
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
@@ -42,16 +42,17 @@ const val DISPLAY_HEIGHT = 640
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 @WithDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
 @WithDevToolsAPI
 class AccessibilityTest : BaseSessionTest() {
     lateinit var view: View
     val screenRect = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
     val provider: AccessibilityNodeProvider get() = view.accessibilityNodeProvider
+    private val nodeInfos = mutableListOf<AccessibilityNodeInfo>()
 
     // Given a child ID, return the virtual descendent ID.
     private fun getVirtualDescendantId(childId: Long): Int {
         try {
             val getVirtualDescendantIdMethod =
                 AccessibilityNodeInfo::class.java.getMethod("getVirtualDescendantId", Long::class.java)
             return getVirtualDescendantIdMethod.invoke(null, childId) as Int
         } catch (ex: Exception) {
@@ -65,16 +66,22 @@ class AccessibilityTest : BaseSessionTes
             val getSourceIdMethod =
                 AccessibilityRecord::class.java.getMethod("getSourceNodeId")
             return getVirtualDescendantId(getSourceIdMethod.invoke(event) as Long)
         } catch (ex: Exception) {
             return 0
         }
     }
 
+    private fun createNodeInfo(id: Int): AccessibilityNodeInfo {
+        val node = provider.createAccessibilityNodeInfo(id);
+        nodeInfos.add(node)
+        return node;
+    }
+
     // Get a child ID by index.
     private fun AccessibilityNodeInfo.getChildId(index: Int): Int =
             getVirtualDescendantId(
                     if (Build.VERSION.SDK_INT >= 21)
                         AccessibilityNodeInfo::class.java.getMethod(
                                 "getChildId", Int::class.java).invoke(this, index) as Long
                     else
                         (AccessibilityNodeInfo::class.java.getMethod("getChildNodeIds")
@@ -125,28 +132,29 @@ class AccessibilityTest : BaseSessionTes
             }
         }) },
         { (view.parent as View).setAccessibilityDelegate(null) },
         object : EventDelegate { })
     }
 
     @After fun teardown() {
         sessionRule.session.accessibility.view = null
+        nodeInfos.forEach { node -> node.recycle() }
     }
 
     private fun waitForInitialFocus() {
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onFocused(event: AccessibilityEvent) { }
         })
     }
 
     @Test fun testRootNode() {
         assertThat("provider is not null", provider, notNullValue())
-        val node = provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID)
+        val node = createNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID)
         assertThat("Root node should have WebView class name",
             node.className.toString(), equalTo("android.webkit.WebView"))
     }
 
     @Test fun testPageLoad() {
         sessionRule.session.loadTestPath(INPUTS_PATH)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
@@ -162,45 +170,45 @@ class AccessibilityTest : BaseSessionTes
 
         provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
             AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Text node should not be focusable", node.isFocusable, equalTo(false))
             }
         })
 
         provider.performAction(nodeId,
             AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Entry node should be focusable", node.isFocusable, equalTo(true))
             }
         })
     }
 
     @Test fun testTextEntryNode() {
         sessionRule.session.loadString("<input aria-label='Name' value='Tobias'>", "text/html")
         waitForInitialFocus()
 
         mainSession.evaluateJS("$('input').focus()")
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 val nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Focused EditBox", node.className.toString(),
                         equalTo("android.widget.EditText"))
                 if (Build.VERSION.SDK_INT >= 19) {
                     assertThat("Hint has field name",
                             node.extras.getString("AccessibilityNodeInfo.hint"),
                             equalTo("Name"))
                 }
             }
@@ -230,29 +238,29 @@ class AccessibilityTest : BaseSessionTes
         })
     }
 
     private fun waitUntilClick(checked: Boolean) {
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onClicked(event: AccessibilityEvent) {
                 var nodeId = getSourceId(event)
-                var node = provider.createAccessibilityNodeInfo(nodeId)
+                var node = createNodeInfo(nodeId)
                 assertThat("Event's checked state matches", event.isChecked, equalTo(checked))
                 assertThat("Checkbox node has correct checked state", node.isChecked, equalTo(checked))
             }
         })
     }
 
     private fun waitUntilSelect(selected: Boolean) {
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onSelected(event: AccessibilityEvent) {
                 var nodeId = getSourceId(event)
-                var node = provider.createAccessibilityNodeInfo(nodeId)
+                var node = createNodeInfo(nodeId)
                 assertThat("Selectable node has correct selected state", node.isSelected, equalTo(selected))
             }
         })
     }
 
     private fun setSelectionArguments(start: Int, end: Int): Bundle {
         val arguments = Bundle(2)
         arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, start)
@@ -273,17 +281,17 @@ class AccessibilityTest : BaseSessionTes
         waitForInitialFocus()
 
         mainSession.evaluateJS("$('input').focus()")
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Focused EditBox", node.className.toString(),
                         equalTo("android.widget.EditText"))
             }
 
             @AssertCalled(count = 1)
             override fun onTextSelectionChanged(event: AccessibilityEvent) {
                 assertThat("fromIndex should be at start", event.fromIndex, equalTo(0))
                 assertThat("toIndex should be at start", event.toIndex, equalTo(0))
@@ -325,17 +333,17 @@ class AccessibilityTest : BaseSessionTes
 
         provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Accessibility focus on first paragraph", node.text as String, startsWith("Lorem ipsum"))
             }
         })
 
         provider.performAction(nodeId,
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                 moveByGranularityArguments(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER))
         waitUntilTextTraversed(0, 1) // "L"
@@ -358,17 +366,17 @@ class AccessibilityTest : BaseSessionTes
 
         provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Accessibility focus on first paragraph", node.text as String, startsWith("Lorem ipsum"))
             }
         })
 
         provider.performAction(nodeId,
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                 moveByGranularityArguments(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD))
         waitUntilTextTraversed(0, 5) // "Lorem"
@@ -391,17 +399,17 @@ class AccessibilityTest : BaseSessionTes
 
         provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                val node = provider.createAccessibilityNodeInfo(nodeId)
+                val node = createNodeInfo(nodeId)
                 assertThat("Accessibility focus on first paragraph", node.text as String, startsWith("Lorem ipsum"))
             }
         })
 
         provider.performAction(nodeId,
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                 moveByGranularityArguments(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE))
         waitUntilTextTraversed(0, 18) // "Lorem ipsum dolor "
@@ -422,17 +430,17 @@ class AccessibilityTest : BaseSessionTes
         sessionRule.session.loadString("<label><input id='checkbox' type='checkbox'>many option</label>", "text/html")
         waitForInitialFocus()
 
         mainSession.evaluateJS("$('#checkbox').focus()")
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                var node = provider.createAccessibilityNodeInfo(nodeId)
+                var node = createNodeInfo(nodeId)
                 assertThat("Checkbox node is checkable", node.isCheckable, equalTo(true))
                 assertThat("Checkbox node is clickable", node.isClickable, equalTo(true))
                 assertThat("Checkbox node is focusable", node.isFocusable, equalTo(true))
                 assertThat("Checkbox node is not checked", node.isChecked, equalTo(false))
                 assertThat("Checkbox node has correct role", node.text.toString(), equalTo("many option check button"))
             }
         })
 
@@ -452,17 +460,17 @@ class AccessibilityTest : BaseSessionTes
                 </ul>""","text/html")
         waitForInitialFocus()
 
         provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onAccessibilityFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                var node = provider.createAccessibilityNodeInfo(nodeId)
+                var node = createNodeInfo(nodeId)
                 assertThat("Selectable node is clickable", node.isClickable, equalTo(true))
                 assertThat("Selectable node is not selected", node.isSelected, equalTo(false))
                 assertThat("Selectable node has correct role", node.text.toString(), equalTo("1 option list box"))
             }
         })
 
         provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_CLICK, null)
         waitUntilSelect(true)
@@ -473,17 +481,17 @@ class AccessibilityTest : BaseSessionTes
         provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_SELECT, null)
         waitUntilSelect(true)
 
         provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_SELECT, null)
         waitUntilSelect(false)
     }
 
     private fun screenContainsNode(nodeId: Int): Boolean {
-        var node = provider.createAccessibilityNodeInfo(nodeId)
+        var node = createNodeInfo(nodeId)
         var nodeBounds = Rect()
         node.getBoundsInScreen(nodeBounds)
         return screenRect.contains(nodeBounds)
     }
 
     @Test fun testScroll() {
         var nodeId = View.NO_ID
         sessionRule.session.loadString(
@@ -495,17 +503,17 @@ class AccessibilityTest : BaseSessionTes
                 </body>""",
                 "text/html")
         sessionRule.waitForPageStop()
 
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 1)
             override fun onFocused(event: AccessibilityEvent) {
                 nodeId = getSourceId(event)
-                var node = provider.createAccessibilityNodeInfo(nodeId)
+                var node = createNodeInfo(nodeId)
                 var nodeBounds = Rect()
                 node.getBoundsInParent(nodeBounds)
                 assertThat("Default root node bounds are correct", nodeBounds, equalTo(screenRect))
             }
         })
 
         provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
         sessionRule.waitUntilCalled(object : EventDelegate {
@@ -609,17 +617,17 @@ class AccessibilityTest : BaseSessionTes
         }
 
         // Perform auto-fill and return number of auto-fills performed.
         fun autoFillChild(id: Int, child: AccessibilityNodeInfo) {
             // Seal the node info instance so we can perform actions on it.
             if (child.childCount > 0) {
                 for (i in 0 until child.childCount) {
                     val childId = child.getChildId(i)
-                    autoFillChild(childId, provider.createAccessibilityNodeInfo(childId))
+                    autoFillChild(childId, createNodeInfo(childId))
                 }
             }
 
             if (EditText::class.java.name == child.className) {
                 assertThat("Input should be enabled", child.isEnabled, equalTo(true))
                 assertThat("Input should be focusable", child.isFocusable, equalTo(true))
                 if (Build.VERSION.SDK_INT >= 19) {
                     assertThat("Password type should match", child.isPassword, equalTo(
@@ -643,41 +651,36 @@ class AccessibilityTest : BaseSessionTes
                     "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"
                 val ACTION_SET_TEXT = if (Build.VERSION.SDK_INT >= 21)
                     AccessibilityNodeInfo.ACTION_SET_TEXT else 0x200000
 
                 args.putCharSequence(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, value)
                 assertThat("Can perform auto-fill",
                            provider.performAction(id, ACTION_SET_TEXT, args), equalTo(true))
             }
-            child.recycle()
         }
 
-        autoFillChild(View.NO_ID, provider.createAccessibilityNodeInfo(View.NO_ID))
+        autoFillChild(View.NO_ID, createNodeInfo(View.NO_ID))
 
         // Wait on the promises and check for correct values.
         for ((actual, expected) in promises.map { it.value.asJSList<String>() }) {
             assertThat("Auto-filled value must match", actual, equalTo(expected))
         }
     }
 
     @ReuseSession(false) // XXX automation crash fix (bug 1485107)
     @Test fun autoFill_navigation() {
         fun countAutoFillNodes(cond: (AccessibilityNodeInfo) -> Boolean =
                                        { it.className == "android.widget.EditText" },
                                id: Int = View.NO_ID): Int {
-            val info = provider.createAccessibilityNodeInfo(id)
-            try {
-                return (if (cond(info)) 1 else 0) + (if (info.childCount > 0)
-                    (0 until info.childCount).sumBy {
-                        countAutoFillNodes(cond, info.getChildId(it))
-                    } else 0)
-            } finally {
-                info.recycle()
-            }
+            val info = createNodeInfo(id)
+            return (if (cond(info)) 1 else 0) + (if (info.childCount > 0)
+                (0 until info.childCount).sumBy {
+                    countAutoFillNodes(cond, info.getChildId(it))
+                } else 0)
         }
 
         // Wait for the accessibility nodes to populate.
         mainSession.loadTestPath(FORMS_HTML_PATH)
         sessionRule.waitUntilCalled(object : EventDelegate {
             @AssertCalled(count = 4)
             override fun onWinContentChanged(event: AccessibilityEvent) {
             }
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -451,16 +451,18 @@ function createKeyboardEventDictionary_(
   let result = {dictionary: null, flags: 0};
   let keyCodeIsDefined = "keyCode" in keyEvent &&
       keyEvent.keyCode != undefined;
   let keyCode =
     (keyCodeIsDefined && keyEvent.keyCode >= 0 && keyEvent.keyCode <= 255) ?
       keyEvent.keyCode : 0;
   let keyName = "Unidentified";
 
+  let printable = false;
+
   if (key.indexOf("KEY_") == 0) {
     keyName = key.substr("KEY_".length);
     result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (key.indexOf("VK_") == 0) {
     keyCode = getKeyboardEvent_(win)["DOM_" + key];
     if (!keyCode) {
       throw "Unknown key: " + key;
     }
@@ -474,26 +476,27 @@ function createKeyboardEventDictionary_(
       keyCode = computeKeyCodeFromChar_(key.charAt(0), win);
     }
     if (!keyCode) {
       result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
     }
     // only force printable if "raw character" and event key match, like "a"
     if (!("key" in keyEvent && key != keyEvent.key)) {
       result.flags |= Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
+      printable = true;
     }
   }
 
   let locationIsDefined = "location" in keyEvent;
   if (locationIsDefined && keyEvent.location === 0) {
     result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEY_LOCATION_STANDARD;
   }
 
   let resultKey = "key" in keyEvent ? keyEvent.key : keyName;
-  if (!MODIFIER_KEYCODES_LOOKUP[key] && keyEvent.shiftKey) {
+  if (printable && keyEvent.shiftKey) {
     resultKey = resultKey.toUpperCase();
   }
 
   result.dictionary = {
     key: resultKey,
     code: "code" in keyEvent ? keyEvent.code : "",
     location: locationIsDefined ? keyEvent.location : 0,
     repeat: "repeat" in keyEvent ? keyEvent.repeat === true : false,
--- a/testing/talos/talos/test.py
+++ b/testing/talos/talos/test.py
@@ -427,17 +427,19 @@ class damp(PageloaderTest):
     tppagecycles = 5
     tploadnocache = True
     tpmozafterpaint = False
     gecko_profile_interval = 10
     gecko_profile_entries = 2000000
     win_counters = w7_counters = linux_counters = mac_counters = None
     filters = filter.ignore_first.prepare(1) + filter.median.prepare()
     preferences = {'devtools.memory.enabled': True,
-                   'addon.test.damp.webserver': '${webserver}'}
+                   'addon.test.damp.webserver': '${webserver}',
+                   'startup.homepage_welcome_url': '',
+                   'startup.homepage_welcome_url.additional': ''}
     unit = 'ms'
     subtest_alerts = True
     perfherder_framework = 'devtools'
 
 
 @register_test()
 class glterrain(PageloaderTest):
     """
--- a/testing/web-platform/tests/webdriver/tests/perform_actions/key.py
+++ b/testing/web-platform/tests/webdriver/tests/perform_actions/key.py
@@ -11,15 +11,23 @@ def test_null_response_value(session, ke
     assert value is None
 
 
 def test_no_browsing_context(session, closed_window, key_chain):
     with pytest.raises(NoSuchWindowException):
         key_chain.key_up("a").perform()
 
 
+def test_element_not_focused(session, test_actions_page, key_chain):
+    key_reporter = session.find.css("#keys", all=False)
+
+    key_chain.key_down("a").key_up("a").perform()
+
+    assert get_keys(key_reporter) == ""
+
+
 def test_backspace_erases_keys(session, key_reporter, key_chain):
     key_chain \
         .send_keys("efcd") \
         .send_keys([Keys.BACKSPACE, Keys.BACKSPACE]) \
         .perform()
 
     assert get_keys(key_reporter) == "ef"
--- a/testing/web-platform/tests/webdriver/tests/perform_actions/key_modifiers.py
+++ b/testing/web-platform/tests/webdriver/tests/perform_actions/key_modifiers.py
@@ -1,14 +1,27 @@
 import pytest
 
 from tests.perform_actions.support.keys import Keys
 
 
 @pytest.mark.parametrize("modifier", [Keys.SHIFT, Keys.R_SHIFT])
+def test_shift_modifier_and_non_printable_keys(session, key_reporter, key_chain, modifier):
+    key_chain \
+        .send_keys("foo") \
+        .key_down(modifier) \
+        .key_down(Keys.BACKSPACE) \
+        .key_up(modifier) \
+        .key_up(Keys.BACKSPACE) \
+        .perform()
+
+    assert key_reporter.property("value") == "fo"
+
+
+@pytest.mark.parametrize("modifier", [Keys.SHIFT, Keys.R_SHIFT])
 def test_shift_modifier_generates_capital_letters(session, key_reporter, key_chain, modifier):
     key_chain \
         .send_keys("b") \
         .key_down(modifier) \
         .key_down("c") \
         .key_up(modifier) \
         .key_up("c") \
         .key_down("d") \
--- a/toolkit/content/tests/chrome/test_props.xul
+++ b/toolkit/content/tests/chrome/test_props.xul
@@ -45,38 +45,35 @@ function test_props()
 
 function test_props_forelement(element, label, value)
 {
   // check the initial values
   is(element.label, label, "element label");
   if (value)
     is(element.value, value, "element value");
   is(element.accessKey, "B", "element accessKey");
-  is(element.crop, "end", "element crop");
   is(element.image, "happy.png", "element image");
   is(element.command, "cmd_nothing", "element command");
   ok(element.tabIndex === 0, "element tabIndex");
 
   synthesizeMouseExpectEvent(element, 4, 4, { }, $("cmd_nothing"), "command", "element");
 
   // make sure that setters return the value
   is(element.label = "Label", "Label", "element label setter return");
   if (value)
     is(element.value = "lb", "lb", "element value setter return");
   is(element.accessKey = "L", "L", "element accessKey setter return");
-  is(element.crop = "start", "start", "element crop setter return");
   is(element.image = "sad.png", "sad.png", "element image setter return");
   is(element.command = "cmd_action", "cmd_action", "element command setter return");
 
   // check the value after it was changed
   is(element.label, "Label", "element label after set");
   if (value)
     is(element.value, "lb", "element value after set");
   is(element.accessKey, "L", "element accessKey after set");
-  is(element.crop, "start", "element crop after set");
   is(element.image, "sad.png", "element image after set");
   is(element.command, "cmd_action", "element command after set");
 
   synthesizeMouseExpectEvent(element, 4, 4, { }, $("cmd_action"), "command", "element");
 
   // check that clicks on disabled items don't fire a command event
   ok((element.disabled = true) === true, "element disabled setter return");
   ok(element.disabled === true, "element disabled after set");
--- a/toolkit/content/widgets/text.xml
+++ b/toolkit/content/widgets/text.xml
@@ -13,18 +13,16 @@
   <binding id="text-base">
     <implementation>
       <property name="disabled" onset="if (val) this.setAttribute('disabled', 'true');
                                        else this.removeAttribute('disabled');
                                        return val;"
                                 onget="return this.getAttribute('disabled') == 'true';"/>
       <property name="value" onget="return this.getAttribute('value');"
                              onset="this.setAttribute('value', val); return val;"/>
-      <property name="crop" onget="return this.getAttribute('crop');"
-                            onset="this.setAttribute('crop', val); return val;"/>
     </implementation>
   </binding>
 
   <binding id="text-label" extends="chrome://global/content/bindings/text.xml#text-base">
     <implementation>
       <property name="accessKey">
         <getter>
           <![CDATA[
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -436,25 +436,22 @@ nsThread::AddToThreadList()
   sMaxActiveThreads = std::max(sActiveThreads, sMaxActiveThreads);
 
   ThreadList().insertBack(this);
 }
 
 void
 nsThread::MaybeRemoveFromThreadList()
 {
-  // We shouldn't need to lock before checking isInList at this point. We're
-  // destroying the last reference to this object, so there's no way for anyone
-  // else to remove it in the middle of our check. And the not-in-list state is
-  // determined the element's next and previous members pointing to itself, so a
-  // non-atomic update to an adjacent member won't affect the outcome either.
   if (isInList()) {
     OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    sActiveThreads--;
-    removeFrom(ThreadList());
+    if (isInList()) {
+      sActiveThreads--;
+      removeFrom(ThreadList());
+    }
   }
 }
 
 /*static*/ void
 nsThread::ThreadFunc(void* aArg)
 {
   using mozilla::ipc::BackgroundChild;