Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Mon, 16 Apr 2018 12:58:28 +0300
changeset 467367 6c64530dd9240265d42c5e2c61ad94d875be01ae
parent 467366 8b14b0ba94dbaf348ae63d7c4014f95e4c4ea73e (current diff)
parent 467361 6276ec7ebbf33e3484997b189f20fc1511534187 (diff)
child 467368 55c90c91e3f3844e6db5b99c86c1ec6a0c24758d
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.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 mozilla-inbound. a=merge on a CLOSED TREE
--- a/accessible/windows/msaa/LazyInstantiator.cpp
+++ b/accessible/windows/msaa/LazyInstantiator.cpp
@@ -190,17 +190,17 @@ static const DllBlockInfo gBlockedInproc
   {L"dtvhooks.dll", MAKE_FILE_VERSION(18, 1, 11, 0)},
   {L"dtvhooks64.dll", MAKE_FILE_VERSION(18, 1, 11, 0)}
 };
 
 /**
  * This is the blocklist for known "bad" remote clients that instantiate a11y.
  */
 static const char* gBlockedRemoteClients[] = {
-  "tbnnotifier.exe" // Ask.com Toolbar, bug 1421018
+  "tbnotifier.exe" // Ask.com Toolbar, bug 1453876
 };
 
 /**
  * Check for the presence of any known "bad" injected DLLs that may be trying
  * to instantiate a11y.
  *
  * @return true to block a11y instantiation, otherwise false to continue
  */
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -4392,17 +4392,19 @@ EditorBase::ShouldHandleIMEComposition()
   return mComposition && mDidPostCreate;
 }
 
 nsresult
 EditorBase::DeleteSelectionAndPrepareToCreateNode()
 {
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  MOZ_ASSERT(selection->GetAnchorFocusRange());
+  if (NS_WARN_IF(!selection->GetAnchorFocusRange())) {
+    return NS_OK;
+  }
 
   if (!selection->GetAnchorFocusRange()->Collapsed()) {
     nsresult rv = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     NS_ENSURE_SUCCESS(rv, rv);
 
     MOZ_ASSERT(selection->GetAnchorFocusRange() &&
                selection->GetAnchorFocusRange()->Collapsed(),
                "Selection not collapsed after delete");
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -7779,18 +7779,17 @@ HTMLEditRules::JoinNodesSmart(nsIContent
       return EditorDOMPoint();
     }
     nsresult rv = mHTMLEditor->MoveNode(&aNodeRight, parent, parOffset);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return EditorDOMPoint();
     }
   }
 
-  EditorDOMPoint ret;
-  ret.SetToEndOf(&aNodeRight);
+  EditorDOMPoint ret(&aNodeRight, aNodeLeft.Length());
 
   // Separate join rules for differing blocks
   if (HTMLEditUtils::IsList(&aNodeLeft) || aNodeLeft.GetAsText()) {
     // For lists, merge shallow (wouldn't want to combine list items)
     nsresult rv = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return EditorDOMPoint();
     }
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -245,16 +245,17 @@ skip-if = toolkit == 'android' # bug 131
 [test_bug1318312.html]
 [test_bug1328023.html]
 [test_bug1330796.html]
 [test_bug1332876.html]
 [test_bug1352799.html]
 [test_bug1355792.html]
 [test_bug1358025.html]
 [test_bug1361008.html]
+[test_bug1361052.html]
 [test_bug1368544.html]
 [test_bug1385905.html]
 [test_bug1390562.html]
 [test_bug1394758.html]
 [test_bug1399722.html]
 [test_bug1406726.html]
 [test_bug1409520.html]
 [test_bug1425997.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1361052.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test for Bug 1361052</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1361052">Mozilla Bug 1361052</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(() => {
+  var strike = document.createElement('strike');
+  strike.contentEditable = true;
+  document.documentElement.appendChild(strike);
+
+  var textarea = document.createElement('textarea');
+  document.documentElement.appendChild(textarea);
+
+  var h5 = document.createElement('h5');
+  strike.appendChild(h5);
+
+  textarea.setCustomValidity("A");
+  document.documentElement.dir = "rtl";
+  document.designMode = "on";
+  document.execCommand("styleWithCSS", false, true);
+  document.designMode = "off";
+  textarea.reportValidity();
+  document.documentElement.dir = "ltr";
+
+  var range = document.createRange();
+  range.selectNode(h5);
+  window.getSelection().addRange(range);
+
+  document.execCommand("inserthorizontalrule", false, null);
+  ok(true, "No crash");
+
+  SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -140,16 +140,27 @@ class Keyword(object):
 
 def arg_to_bool(arg):
     if isinstance(arg, bool):
         return arg
     assert arg in ["True", "False"], "Unexpected value for boolean arguement: " + repr(arg)
     return arg == "True"
 
 
+def parse_property_aliases(alias_list):
+    result = []
+    if alias_list:
+        for alias in alias_list.split():
+            (name, _, pref) = alias.partition(":")
+            if name.startswith("-webkit-") and not pref:
+                pref = "layout.css.prefixes.webkit"
+            result.append((name, pref))
+    return result
+
+
 class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animation_value_type=None, keyword=None,
                  predefined_type=None, servo_pref=None, gecko_pref=None,
                  enabled_in="content", need_index=False,
                  gecko_ffi_name=None,
                  allowed_in_keyframe_block=True, cast_type='u8',
                  logical=False, alias=None, extra_prefixes=None, boxed=False,
                  flags=None, allowed_in_page_rule=False, allow_quirks=False, ignored_when_colors_disabled=False,
@@ -173,18 +184,18 @@ class Longhand(object):
         #  * "content" implies the property is accessible unconditionally,
         #    modulo a pref, set via servo_pref / gecko_pref.
         assert enabled_in in ["", "ua", "chrome", "content"]
         self.enabled_in = enabled_in
         self.need_index = need_index
         self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
         self.cast_type = cast_type
         self.logical = arg_to_bool(logical)
-        self.alias = alias.split() if alias else []
-        self.extra_prefixes = extra_prefixes.split() if extra_prefixes else []
+        self.alias = parse_property_aliases(alias)
+        self.extra_prefixes = parse_property_aliases(extra_prefixes)
         self.boxed = arg_to_bool(boxed)
         self.flags = flags.split() if flags else []
         self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
         self.allow_quirks = allow_quirks
         self.ignored_when_colors_disabled = ignored_when_colors_disabled
         self.is_vector = vector
 
         # https://drafts.csswg.org/css-animations/#keyframes
@@ -317,18 +328,18 @@ class Shorthand(object):
         self.spec = spec
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
         self.servo_pref = servo_pref
         self.gecko_pref = gecko_pref
         self.sub_properties = sub_properties
         assert enabled_in in ["", "ua", "chrome", "content"]
         self.enabled_in = enabled_in
-        self.alias = alias.split() if alias else []
-        self.extra_prefixes = extra_prefixes.split() if extra_prefixes else []
+        self.alias = parse_property_aliases(alias)
+        self.extra_prefixes = parse_property_aliases(extra_prefixes)
         self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
         self.flags = flags.split() if flags else []
 
         # https://drafts.csswg.org/css-animations/#keyframes
         # > The <declaration-list> inside of <keyframe-block> accepts any CSS property
         # > except those defined in this specification,
         # > but does accept the `animation-play-state` property and interprets it specially.
         self.allowed_in_keyframe_block = allowed_in_keyframe_block \
@@ -368,23 +379,23 @@ class Shorthand(object):
     def enabled_in_content(self):
         return self.enabled_in == "content"
 
     def nscsspropertyid(self):
         return "nsCSSPropertyID::eCSSProperty_%s" % self.ident
 
 
 class Alias(object):
-    def __init__(self, name, original):
+    def __init__(self, name, original, gecko_pref):
         self.name = name
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
         self.enabled_in = original.enabled_in
         self.servo_pref = original.servo_pref
-        self.gecko_pref = original.gecko_pref
+        self.gecko_pref = gecko_pref
         self.allowed_in_page_rule = original.allowed_in_page_rule
         self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
 
     def experimental(self, product):
         if product == "gecko":
             return bool(self.gecko_pref)
         return bool(self.servo_pref)
 
@@ -457,43 +468,47 @@ class PropertiesData(object):
 
     def active_style_structs(self):
         return [s for s in self.style_structs if s.additional_methods or s.longhands]
 
     def add_prefixed_aliases(self, property):
         # FIXME Servo's DOM architecture doesn't support vendor-prefixed properties.
         #       See servo/servo#14941.
         if self.product == "gecko":
-            for prefix in property.extra_prefixes:
-                property.alias.append('-%s-%s' % (prefix, property.name))
+            for (prefix, pref) in property.extra_prefixes:
+                # All webkit prefixed properties are currently under
+                # control of this pref in Gecko currently.
+                if prefix == "webkit" and not pref:
+                    pref = "layout.css.prefixes.webkit"
+                property.alias.append(('-%s-%s' % (prefix, property.name), pref))
 
     def declare_longhand(self, name, products="gecko servo", **kwargs):
         products = products.split()
         if self.product not in products:
             return
 
         longhand = Longhand(self.current_style_struct, name, **kwargs)
         self.add_prefixed_aliases(longhand)
-        longhand.alias = list(map(lambda x: Alias(x, longhand), longhand.alias))
+        longhand.alias = list(map(lambda (x, p): Alias(x, longhand, p), longhand.alias))
         self.longhand_aliases += longhand.alias
         self.current_style_struct.longhands.append(longhand)
         self.longhands.append(longhand)
         self.longhands_by_name[name] = longhand
 
         return longhand
 
     def declare_shorthand(self, name, sub_properties, products="gecko servo", *args, **kwargs):
         products = products.split()
         if self.product not in products:
             return
 
         sub_properties = [self.longhands_by_name[s] for s in sub_properties]
         shorthand = Shorthand(name, sub_properties, *args, **kwargs)
         self.add_prefixed_aliases(shorthand)
-        shorthand.alias = list(map(lambda x: Alias(x, shorthand), shorthand.alias))
+        shorthand.alias = list(map(lambda (x, p): Alias(x, shorthand, p), shorthand.alias))
         self.shorthand_aliases += shorthand.alias
         self.shorthands.append(shorthand)
         return shorthand
 
     def shorthands_except_all(self):
         return [s for s in self.shorthands if s.name != "all"]
 
     def all_aliases(self):
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -221,151 +221,155 @@
 // We allow it to apply to placeholders for UA sheets, which set it !important.
 <%helpers:longhand name="overflow-y" animation_value_type="discrete"
                    flags="APPLIES_TO_PLACEHOLDER",
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-y"
                    servo_restyle_damage = "reflow">
     pub use super::overflow_x::{SpecifiedValue, parse, get_initial_value, computed_value};
 </%helpers:longhand>
 
+<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %>
+
 ${helpers.predefined_type("transition-duration",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
                           parse_method="parse_non_negative",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=transition_extra_prefixes,
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")}
 
 ${helpers.predefined_type("transition-timing-function",
                           "TimingFunction",
                           "computed::TimingFunction::ease()",
                           initial_specified_value="specified::TimingFunction::ease()",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=transition_extra_prefixes,
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function")}
 
 ${helpers.predefined_type(
     "transition-property",
     "TransitionProperty",
     "computed::TransitionProperty::all()",
     initial_specified_value="specified::TransitionProperty::all()",
     vector=True,
     allow_empty="NotInitial",
     need_index=True,
     needs_context=False,
     animation_value_type="none",
-    extra_prefixes="moz webkit",
+    extra_prefixes=transition_extra_prefixes,
     spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property",
 )}
 
 ${helpers.predefined_type("transition-delay",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=transition_extra_prefixes,
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay")}
 
 
+<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %>
+
 ${helpers.predefined_type(
     "animation-name",
     "AnimationName",
     "computed::AnimationName::none()",
     initial_specified_value="specified::AnimationName::none()",
     vector=True,
     need_index=True,
     animation_value_type="none",
-    extra_prefixes="moz webkit",
+    extra_prefixes=animation_extra_prefixes,
     allowed_in_keyframe_block=False,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
 )}
 
 ${helpers.predefined_type("animation-duration",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
                           parse_method="parse_non_negative",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=animation_extra_prefixes,
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")}
 
 // animation-timing-function is the exception to the rule for allowed_in_keyframe_block:
 // https://drafts.csswg.org/css-animations/#keyframes
 ${helpers.predefined_type("animation-timing-function",
                           "TimingFunction",
                           "computed::TimingFunction::ease()",
                           initial_specified_value="specified::TimingFunction::ease()",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=animation_extra_prefixes,
                           allowed_in_keyframe_block=True,
                           spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function")}
 
 ${helpers.predefined_type(
     "animation-iteration-count",
     "AnimationIterationCount",
     "computed::AnimationIterationCount::one()",
     initial_specified_value="specified::AnimationIterationCount::one()",
     vector=True,
     need_index=True,
     animation_value_type="none",
-    extra_prefixes="moz webkit",
+    extra_prefixes=animation_extra_prefixes,
     allowed_in_keyframe_block=False,
     spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
 )}
 
 <% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
 ${helpers.single_keyword("animation-direction",
                          "normal reverse alternate alternate-reverse",
                          need_index=True,
                          animation_value_type="none",
                          vector=True,
                          gecko_enum_prefix="PlaybackDirection",
                          custom_consts=animation_direction_custom_consts,
-                         extra_prefixes="moz webkit",
+                         extra_prefixes=animation_extra_prefixes,
                          spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
                          allowed_in_keyframe_block=False)}
 
 ${helpers.single_keyword("animation-play-state",
                          "running paused",
                          need_index=True,
                          animation_value_type="none",
                          vector=True,
-                         extra_prefixes="moz webkit",
+                         extra_prefixes=animation_extra_prefixes,
                          spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
                          allowed_in_keyframe_block=False)}
 
 ${helpers.single_keyword("animation-fill-mode",
                          "none forwards backwards both",
                          need_index=True,
                          animation_value_type="none",
                          vector=True,
                          gecko_enum_prefix="FillMode",
-                         extra_prefixes="moz webkit",
+                         extra_prefixes=animation_extra_prefixes,
                          spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
                          allowed_in_keyframe_block=False)}
 
 ${helpers.predefined_type("animation-delay",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
                           vector=True,
                           need_index=True,
                           animation_value_type="none",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=animation_extra_prefixes,
                           spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
                           allowed_in_keyframe_block=False)}
 
 % for axis in ["x", "y"]:
     ${helpers.predefined_type(
         "scroll-snap-points-" + axis,
         "ScrollSnapPoint",
         "computed::ScrollSnapPoint::none()",
@@ -392,19 +396,21 @@
     vector=True,
     products="gecko",
     gecko_pref="layout.css.scroll-snap.enabled",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)",
     animation_value_type="discrete",
     allow_empty="NotInitial"
 )}
 
+<% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
+
 ${helpers.predefined_type("transform", "Transform",
                           "generics::transform::Transform::none()",
-                          extra_prefixes="webkit moz",
+                          extra_prefixes=transform_extra_prefixes,
                           animation_value_type="ComputedValue",
                           gecko_ffi_name="mSpecifiedTransform",
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
                           servo_restyle_damage = "reflow_out_of_flow")}
 
 ${helpers.predefined_type("rotate", "Rotate",
                           "generics::transform::Rotate::None",
@@ -512,35 +518,35 @@
                          animation_value_type="discrete")}
 
 ${helpers.predefined_type(
     "perspective",
     "Perspective",
     "computed::Perspective::none()",
     gecko_ffi_name="mChildPerspective",
     spec="https://drafts.csswg.org/css-transforms/#perspective",
-    extra_prefixes="moz webkit",
+    extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="AnimatedPerspective",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type("perspective-origin",
                           "position::Position",
                           "computed::position::Position::center()",
                           boxed=True,
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=transform_extra_prefixes,
                           spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
                           animation_value_type="ComputedValue",
                           servo_restyle_damage = "reflow_out_of_flow")}
 
 ${helpers.single_keyword("backface-visibility",
                          "visible hidden",
                          spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
-                         extra_prefixes="moz webkit",
+                         extra_prefixes=transform_extra_prefixes,
                          animation_value_type="discrete")}
 
 ${helpers.single_keyword("transform-box",
                          "border-box fill-box view-box",
                          gecko_enum_prefix="StyleGeometryBox",
                          products="gecko",
                          gecko_pref="svg.transform-box.enabled",
                          spec="https://drafts.csswg.org/css-transforms/#transform-box",
@@ -548,27 +554,27 @@
                          animation_value_type="discrete")}
 
 ${helpers.predefined_type(
     "transform-style",
     "TransformStyle",
     "computed::TransformStyle::" + ("Auto" if product == "servo" else "Flat"),
     spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
     needs_context=False,
-    extra_prefixes="moz webkit",
+    extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="discrete",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
 ${helpers.predefined_type("transform-origin",
                           "TransformOrigin",
                           "computed::TransformOrigin::initial_value()",
                           animation_value_type="ComputedValue",
-                          extra_prefixes="moz webkit",
+                          extra_prefixes=transform_extra_prefixes,
                           gecko_ffi_name="mTransformOrigin",
                           boxed=True,
                           spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
                           servo_restyle_damage = "reflow_out_of_flow")}
 
 ${helpers.predefined_type("contain",
                           "Contain",
                           "specified::Contain::empty()",
--- a/servo/components/style/properties/longhand/column.mako.rs
+++ b/servo/components/style/properties/longhand/column.mako.rs
@@ -68,16 +68,16 @@
     ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
 )}
 
 ${helpers.single_keyword("column-span", "none all",
                          products="gecko", animation_value_type="discrete",
                          gecko_pref="layout.css.column-span.enabled",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
-                         extra_prefixes="moz")}
+                         extra_prefixes="moz:layout.css.column-span.enabled")}
 
 ${helpers.single_keyword("column-rule-style",
                          "none hidden dotted dashed solid double groove ridge inset outset",
                          products="gecko", extra_prefixes="moz",
                          gecko_constant_prefix="NS_STYLE_BORDER_STYLE",
                          animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style")}
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -145,17 +145,17 @@
                                 flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="discrete")}
 
 ${helpers.predefined_type("font-feature-settings",
                           "FontFeatureSettings",
                           products="gecko",
                           initial_value="computed::FontFeatureSettings::normal()",
                           initial_specified_value="specified::FontFeatureSettings::normal()",
-                          extra_prefixes="moz",
+                          extra_prefixes="moz:layout.css.prefixes.font-features",
                           animation_value_type="discrete",
                           flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings")}
 
 <%
 # This spec link is too long to fit elsewhere
 variation_spec = """\
 https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-the-font-variation-settings-property\
@@ -173,17 +173,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
                           spec="${variation_spec}")}
 
 ${helpers.predefined_type("font-language-override",
                           "FontLanguageOverride",
                           products="gecko",
                           initial_value="computed::FontLanguageOverride::zero()",
                           initial_specified_value="specified::FontLanguageOverride::normal()",
                           animation_value_type="discrete",
-                          extra_prefixes="moz",
+                          extra_prefixes="moz:layout.css.prefixes.font-features",
                           flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override")}
 
 ${helpers.single_keyword_system("font-optical-sizing",
                                 "auto none",
                                 products="gecko",
                                 gecko_pref="layout.css.font-variations.enabled",
                                 gecko_ffi_name="mFont.opticalSizing",
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -52,22 +52,24 @@ macro_rules! impl_align_conversions {
 )}
 
 // CSS Flexible Box Layout Module Level 1
 // http://www.w3.org/TR/css3-flexbox/
 
 // Flex container properties
 ${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
-                         extra_prefixes="webkit", animation_value_type="discrete",
+                         extra_prefixes="webkit",
+                         animation_value_type="discrete",
                          servo_restyle_damage = "reflow")}
 
 ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
-                         extra_prefixes="webkit", animation_value_type="discrete",
+                         extra_prefixes="webkit",
+                         animation_value_type="discrete",
                          servo_restyle_damage = "reflow")}
 
 % if product == "servo":
     // FIXME: Update Servo to support the same Syntax as Gecko.
     ${helpers.single_keyword("justify-content", "flex-start stretch flex-end center space-between space-around",
                              extra_prefixes="webkit",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                              animation_value_type="discrete",
@@ -262,17 +264,17 @@ macro_rules! impl_align_conversions {
                                   logical=logical,
                                   allow_quirks=not logical,
                                   servo_restyle_damage = "reflow")}
     % endif
 % endfor
 
 ${helpers.single_keyword("box-sizing",
                          "content-box border-box",
-                         extra_prefixes="moz webkit",
+                         extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
                          spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
                          gecko_enum_prefix="StyleBoxSizing",
                          custom_consts={ "content-box": "Content", "border-box": "Border" },
                          animation_value_type="discrete",
                          servo_restyle_damage = "reflow")}
 
 ${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
                          products="gecko", animation_value_type="discrete",
--- a/servo/components/style/properties/shorthand/border.mako.rs
+++ b/servo/components/style/properties/shorthand/border.mako.rs
@@ -241,17 +241,18 @@ pub fn parse_border<'i, 't>(
 
             BorderRadius::serialize_rects(widths, heights, dest)
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="border-image" sub_properties="border-image-outset
     border-image-repeat border-image-slice border-image-source border-image-width"
-    extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
+    extra_prefixes="moz:layout.css.prefixes.border-image webkit"
+    spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
     use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
     use properties::longhands::{border_image_source, border_image_width};
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         % for name in "outset repeat slice source width".split():
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -112,17 +112,18 @@ macro_rules! try_parse_one {
             }) {
                 $var = Some(value);
                 continue;
             }
         }
     };
 }
 
-<%helpers:shorthand name="transition" extra_prefixes="moz webkit"
+<%helpers:shorthand name="transition"
+                    extra_prefixes="moz:layout.css.prefixes.transitions webkit"
                     sub_properties="transition-property transition-duration
                                     transition-timing-function
                                     transition-delay"
                     spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
     % for prop in "delay duration property timing_function".split():
     use properties::longhands::transition_${prop};
     % endfor
 
@@ -252,17 +253,18 @@ macro_rules! try_parse_one {
                     self.transition_${name}.0[i].to_css(dest)?;
                 % endfor
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
-<%helpers:shorthand name="animation" extra_prefixes="moz webkit"
+<%helpers:shorthand name="animation"
+                    extra_prefixes="moz:layout.css.prefixes.animations webkit"
                     sub_properties="animation-name animation-duration
                                     animation-timing-function animation-delay
                                     animation-iteration-count animation-direction
                                     animation-fill-mode animation-play-state"
                     allowed_in_keyframe_block="False"
                     spec="https://drafts.csswg.org/css-animations/#propdef-animation">
     <%
         props = "name duration timing_function delay iteration_count \
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -470,16 +470,22 @@
     ]
    ],
    "editor/initial_selection_on_focus.html": [
     [
      "/_mozilla/editor/initial_selection_on_focus.html",
      {}
     ]
    ],
+   "editor/joining_nodes.html": [
+    [
+     "/_mozilla/editor/joining_nodes.html",
+     {}
+    ]
+   ],
    "fetch/api/redirect/redirect-referrer.https.html": [
     [
      "/_mozilla/fetch/api/redirect/redirect-referrer.https.html",
      {}
     ]
    ],
    "focus/Range_collapse.html": [
     [
@@ -1027,16 +1033,20 @@
   "dom/throttling/throttling-ws.window.js": [
    "67a981ba2a4d08b684947ed42aba6648dcd262b4",
    "testharness"
   ],
   "editor/initial_selection_on_focus.html": [
    "06948dbf72160a7de5a0baaa2f6cf1bb54fbeb8f",
    "testharness"
   ],
+  "editor/joining_nodes.html": [
+   "048cf7d99acdecb927f97c4554c4d04ca8b15a8a",
+   "testharness"
+  ],
   "fetch/api/redirect/redirect-referrer-mixed-content.js": [
    "f9d7ec9cf9fa8c847e45664b05482e3f8c191385",
    "support"
   ],
   "fetch/api/redirect/redirect-referrer.https.html": [
    "99cbd16b78771f35e075e4012d8dbc5dce3209c0",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/editor/joining_nodes.html.ini
@@ -0,0 +1,86 @@
+[joining_nodes.html]
+  type: testharness
+  [Joining <dt> and <dd> nodes, delete command]
+    expected: FAIL
+
+  [Joining <dt> and <dd> nodes, forwarddelete command]
+    expected: FAIL
+
+  [Joining <dd> and <dt> nodes, delete command]
+    expected: FAIL
+
+  [Joining <dd> and <dt> nodes, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h1> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h1> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h2> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h2> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h3> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h3> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h4> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h4> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h5> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h5> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <h6> and <p> elements, delete command]
+    expected: FAIL
+
+  [Joining <h6> and <p> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h1> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h1> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h2> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h2> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h3> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h3> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h4> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h4> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h5> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h5> elements, forwarddelete command]
+    expected: FAIL
+
+  [Joining <p> and <h6> elements, delete command]
+    expected: FAIL
+
+  [Joining <p> and <h6> elements, forwarddelete command]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/editor/joining_nodes.html
@@ -0,0 +1,256 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Joining nodes with delete/forwardDelete command</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+</head>
+<body>
+<script>
+"use strict";
+
+(function() {
+  const kTests = [
+    { description: "Joining text nodes separated by <br>",
+      innerHTML: "<p>foo bar<br id=\"separator\">baz</p>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    // XXX Attributes of right <li> element are cloned but this may not be expected behavior.
+    { description: "Joining <li> nodes in <ul>",
+      innerHTML: "<ul><li>foo bar</li><li id=\"separator\">baz</li></ul>",
+      expectedInnerHTML: "<ul><li id=\"separator\">foo barbaz</li></ul>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    // XXX Attributes of right <li> element are cloned but this may not be expected behavior.
+    { description: "Joining <li> nodes in <ol>",
+      innerHTML: "<ol><li>foo bar</li><li id=\"separator\">baz</li></ol>",
+      expectedInnerHTML: "<ol><li id=\"separator\">foo barbaz</li></ol>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <dt> and <dd> nodes",
+      innerHTML: "<dl><dt>foo bar</dt><dd id=\"separator\">baz</dd></dl>",
+      expectedInnerHTML: "<dl><dt>foo barbaz</dt></dl>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <dd> and <dt> nodes",
+      innerHTML: "<dl><dd>foo bar</dd><dt id=\"separator\">baz</dt></dl>",
+      expectedInnerHTML: "<dl><dd>foo barbaz</dd></dl>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    // XXX Attributes of right <p> element are cloned but this may not be expected behavior.
+    { description: "Joining <p> elements",
+      innerHTML: "<p>foo bar</p><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<p id=\"separator\">foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    // XXX Attributes of right <div> element are cloned but this may not be expected behavior.
+    { description: "Joining <div> elements",
+      innerHTML: "<div>foo bar</div><div id=\"separator\">baz</div>",
+      expectedInnerHTML: "<div id=\"separator\">foo barbaz</div>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h1> and <p> elements",
+      innerHTML: "<h1>foo bar</h1><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h1>foo barbaz</h1>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h2> and <p> elements",
+      innerHTML: "<h2>foo bar</h2><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h2>foo barbaz</h2>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h3> and <p> elements",
+      innerHTML: "<h3>foo bar</h3><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h3>foo barbaz</h3>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h4> and <p> elements",
+      innerHTML: "<h4>foo bar</h4><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h4>foo barbaz</h4>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h5> and <p> elements",
+      innerHTML: "<h5>foo bar</h5><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h5>foo barbaz</h5>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <h6> and <p> elements",
+      innerHTML: "<h6>foo bar</h6><p id=\"separator\">baz</p>",
+      expectedInnerHTML: "<h6>foo barbaz</h6>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h1> elements",
+      innerHTML: "<p>foo bar</p><h1 id=\"separator\">baz</h1>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h2> elements",
+      innerHTML: "<p>foo bar</p><h2 id=\"separator\">baz</h2>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h3> elements",
+      innerHTML: "<p>foo bar</p><h3 id=\"separator\">baz</h3>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h4> elements",
+      innerHTML: "<p>foo bar</p><h4 id=\"separator\">baz</h4>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h5> elements",
+      innerHTML: "<p>foo bar</p><h5 id=\"separator\">baz</h5>",
+      expectedInnerHTML: "<p>foo barbaz</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+    { description: "Joining <p> and <h6> elements",
+      innerHTML: "<p>foo bar</p><h6 id=\"separator\">baz</h6>",
+      expectedInnerHTML: "<p>foo barbar</p>",
+      expectedSelectionRange: function (editor) {
+        return { collapsed: true,
+                 startContainer: editor.firstChild.firstChild,
+                 startOffset: 7 };
+      }, },
+  ];
+
+  document.body.innerHTML = "<div id=\"editor\" contenteditable></div>";
+  let editor = document.getElementById("editor");
+  editor.focus();
+  let selection = document.getSelection();
+
+  for (const kTest of kTests) {
+    editor.innerHTML = kTest.innerHTML;
+    let separator = document.getElementById("separator");
+    function getFirstLeafNode(node) {
+      for (; node.firstChild; node = node.firstChild) {
+      }
+      return node;
+    }
+    if (separator.nodeName.toLowerCase() == "br") {
+      if (separator.nextSibling) {
+        selection.collapse(getFirstLeafNode(separator.nextSibling), 0);
+      } else {
+        selection.collapse(separator.parentNode,
+                           separator.parentNode.childNodes.length);
+      }
+    } else {
+      selection.collapse(getFirstLeafNode(separator), 0);
+    }
+    test(function () {
+      document.execCommand("delete", false);
+      assert_equals(editor.innerHTML, kTest.expectedInnerHTML);
+      const kExpectedSelectionRange = kTest.expectedSelectionRange(editor);
+      let range = selection.getRangeAt(0);
+      assert_equals(range.collapsed, kExpectedSelectionRange.collapsed);
+      assert_equals(range.startContainer, kExpectedSelectionRange.startContainer);
+      assert_equals(range.startOffset, kExpectedSelectionRange.startOffset);
+      if (kExpectedSelectionRange.collapsed) {
+        assert_equals(range.endContainer, kExpectedSelectionRange.startContainer);
+        assert_equals(range.endOffset, kExpectedSelectionRange.startOffset);
+      } else {
+        assert_equals(range.endContainer, kExpectedSelectionRange.endContainer);
+        assert_equals(range.endOffset, kExpectedSelectionRange.endOffset);
+      }
+    }, kTest.description + ", delete command");
+
+    editor.innerHTML = kTest.innerHTML;
+    separator = document.getElementById("separator");
+    function getLastLeafNode(node) {
+      for (; node.lastChild; node = node.lastChild) {
+      }
+      return node;
+    }
+    function getLength(node) {
+      if (node.length !== undefined) {
+        return node.length;
+      }
+      return node.childNodes.length;
+    }
+    if (separator.previousSibling) {
+      let lastLeafNode = getLastLeafNode(separator.previousSibling);
+      selection.collapse(lastLeafNode, getLength(lastLeafNode));
+    } else {
+      selection.collapse(separator.parentNode, 0);
+    }
+    test(function () {
+      try {
+        document.execCommand("forwarddelete", false);
+      } catch (e) {
+        console.log(e);
+      }
+      assert_equals(editor.innerHTML, kTest.expectedInnerHTML);
+      const kExpectedSelectionRange = kTest.expectedSelectionRange(editor);
+      let range = selection.getRangeAt(0);
+      assert_equals(range.collapsed, kExpectedSelectionRange.collapsed);
+      assert_equals(range.startContainer, kExpectedSelectionRange.startContainer);
+      assert_equals(range.startOffset, kExpectedSelectionRange.startOffset);
+      if (kExpectedSelectionRange.collapsed) {
+        assert_equals(range.endContainer, kExpectedSelectionRange.startContainer);
+        assert_equals(range.endOffset, kExpectedSelectionRange.startOffset);
+      } else {
+        assert_equals(range.endContainer, kExpectedSelectionRange.endContainer);
+        assert_equals(range.endOffset, kExpectedSelectionRange.endOffset);
+      }
+    }, kTest.description + ", forwarddelete command");
+  }
+})();
+</script>
+</body>
+</html>