Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 17 Jan 2019 23:54:34 +0200
changeset 454334 1db2248f441513991df17a73112ffd9cc1846846
parent 454333 24982570fc8338ba51f32f142c7d0e6f342cce7d (current diff)
parent 454284 47d3dbd969f5849d813f4e312b1aa9ee78abac5e (diff)
child 454335 732c870d85fcb67eb1a9e770d6200058332462a0
child 454387 6896f8bf818180ef3bdf042d707713a0aa410b9f
child 454423 081c6ac45c5d2fb2d64465ec5d5f18771f8626f9
push id111243
push userncsoregi@mozilla.com
push dateThu, 17 Jan 2019 22:00:01 +0000
treeherdermozilla-inbound@732c870d85fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
1db2248f4415 / 66.0a1 / 20190117215514 / files
nightly linux64
1db2248f4415 / 66.0a1 / 20190117215514 / files
nightly mac
1db2248f4415 / 66.0a1 / 20190117215514 / files
nightly win32
1db2248f4415 / 66.0a1 / 20190117215514 / files
nightly win64
1db2248f4415 / 66.0a1 / 20190117215514 / 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/components/aboutconfig/content/aboutconfig.css
+++ b/browser/components/aboutconfig/content/aboutconfig.css
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
   height: 100%;
 }
 
 body.config-background {
-  height: 100%;
+  min-height: 100%;
   background-image: url("chrome://browser/content/aboutconfig/background.svg");
   background-repeat: no-repeat;
   background-position: center center;
   background-size: 450px;
 }
 
 body.config-warning {
   background-image: url("chrome://browser/content/aboutconfig/background.svg#warning");
--- a/browser/components/aboutconfig/test/browser/head.js
+++ b/browser/components/aboutconfig/test/browser/head.js
@@ -15,16 +15,20 @@ const PREF_NUMBER_DEFAULT_ZERO = "access
 const PREF_STRING_DEFAULT_EMPTY = "browser.helperApps.neverAsk.openFile";
 const PREF_STRING_DEFAULT_NOTEMPTY = "accessibility.typeaheadfind.soundURL";
 const PREF_STRING_DEFAULT_NOTEMPTY_VALUE = "beep";
 const PREF_STRING_LOCALIZED_MISSING = "gecko.handlerService.schemes.irc.1.name";
 
 // Other preference names used in tests.
 const PREF_NEW = "test.aboutconfig.new";
 
+// These tests can be slow to execute because they show all the preferences
+// several times, and each time can require a second on some virtual machines.
+requestLongerTimeout(2);
+
 class AboutConfigRowTest {
   constructor(element) {
     this.element = element;
   }
 
   querySelector(selector) {
     return this.element.querySelector(selector);
   }
--- a/browser/config/whats_new_page.yml
+++ b/browser/config/whats_new_page.yml
@@ -13,45 +13,107 @@
       blob-types: [wnp]
       release-types: [release, release-rc]
       products: [firefox]
       update-channel: release
       # e.g.: ["<61.0"]. {version.major_number} reflects the current version.
       # This is done by taskgraph.
       versions: ["<{version.major_number}.0"]
       locales:
+          - ach
+          - af
+          - an
+          - ar
+          - as
+          - ast
           - az
           - be
+          - bg
+          - bn-BD
+          - bn-IN
+          - br
+          - bs
+          - ca
           - cak
+          - cs
           - cy
           - da
           - de
           - dsb
+          - el
           - en-CA
           - en-GB
           - en-US
+          - en-ZA
+          - eo
           - es-AR
+          - es-CL
+          - es-ES
+          - es-MX
           - et
+          - eu
+          - fa
+          - ff
+          - fi
           - fr
+          - fy-NL
+          - ga-IE
+          - gd
+          - gl
+          - gn
+          - gu-IN
+          - he
           - hi-IN
+          - hr
           - hsb
           - hu
+          - hy-AM
           - ia
           - id
+          - is
           - it
+          - ja
           - ka
+          - kab
+          - kk
+          - km
+          - kn
+          - ko
           - lij
           - lt
+          - lv
+          - mai
+          - mk
+          - ml
+          - mr
           - ms
+          - my
           - nb-NO
+          - ne-NP
           - nl
+          - nn-NO
+          - oc
+          - or
+          - pa-IN
           - pl
+          - pt-BR
           - pt-PT
+          - rm
           - ro
           - ru
+          - si
           - sk
           - sl
+          - son
           - sq
+          - sr
           - sv-SE
+          - ta
+          - te
           - th
           - tr
+          - uk
+          - ur
+          - uz
+          - vi
+          - xh
           - zh-CN
           - zh-TW
--- a/devtools/client/inspector/rules/actions/index.js
+++ b/devtools/client/inspector/rules/actions/index.js
@@ -14,12 +14,15 @@ createEnum([
 
   // Sets the entire pseudo class state with the new list of applied pseudo-class
   // locks.
   "SET_PSEUDO_CLASSES",
 
   // Toggles on or off the given pseudo class value for the current selected element.
   "TOGGLE_PSEUDO_CLASS",
 
+  // Updates the highlighted selector.
+  "UPDATE_HIGHLIGHTED_SELECTOR",
+
   // Updates the rules state with the new list of CSS rules for the selected element.
   "UPDATE_RULES",
 
 ], module.exports);
--- a/devtools/client/inspector/rules/actions/rules.js
+++ b/devtools/client/inspector/rules/actions/rules.js
@@ -1,21 +1,35 @@
 /* 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 {
+  UPDATE_HIGHLIGHTED_SELECTOR,
   UPDATE_RULES,
 } = require("./index");
 
 module.exports = {
 
   /**
+   * Updates the highlighted selector.
+   *
+   * @param  {String} highlightedSelector
+   *         The selector of the element to be highlighted by the selector highlighter.
+   */
+  updateHighlightedSelector(highlightedSelector) {
+    return {
+      type: UPDATE_HIGHLIGHTED_SELECTOR,
+      highlightedSelector,
+    };
+  },
+
+  /**
    * Updates the rules state with the new list of CSS rules for the selected element.
    *
    * @param  {Array} rules
    *         Array of Rule objects containing the selected element's CSS rules.
    */
   updateRules(rules) {
     return {
       type: UPDATE_RULES,
--- a/devtools/client/inspector/rules/components/Rule.js
+++ b/devtools/client/inspector/rules/components/Rule.js
@@ -5,31 +5,34 @@
 "use strict";
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const Declarations = createFactory(require("./Declarations"));
 const Selector = createFactory(require("./Selector"));
+const SelectorHighlighter = createFactory(require("./SelectorHighlighter"));
 const SourceLink = createFactory(require("./SourceLink"));
 
 const Types = require("../types");
 
 class Rule extends PureComponent {
   static get propTypes() {
     return {
       onToggleDeclaration: PropTypes.func.isRequired,
+      onToggleSelectorHighlighter: PropTypes.func.isRequired,
       rule: PropTypes.shape(Types.rule).isRequired,
     };
   }
 
   render() {
     const {
       onToggleDeclaration,
+      onToggleSelectorHighlighter,
       rule,
     } = this.props;
     const {
       declarations,
       selector,
       sourceLink,
       type,
     } = rule;
@@ -39,16 +42,23 @@ class Rule extends PureComponent {
         { className: "ruleview-rule devtools-monospace" },
         SourceLink({ sourceLink }),
         dom.div({ className: "ruleview-code" },
           dom.div({},
             Selector({
               selector,
               type,
             }),
+            type !== CSSRule.KEYFRAME_RULE ?
+              SelectorHighlighter({
+                onToggleSelectorHighlighter,
+                selector,
+              })
+              :
+              null,
             dom.span({ className: "ruleview-ruleopen" }, " {")
           ),
           Declarations({
             declarations,
             onToggleDeclaration,
           }),
           dom.div({ className: "ruleview-ruleclose" }, "}")
         )
--- a/devtools/client/inspector/rules/components/Rules.js
+++ b/devtools/client/inspector/rules/components/Rules.js
@@ -10,29 +10,32 @@ const PropTypes = require("devtools/clie
 const Rule = createFactory(require("./Rule"));
 
 const Types = require("../types");
 
 class Rules extends PureComponent {
   static get propTypes() {
     return {
       onToggleDeclaration: PropTypes.func.isRequired,
+      onToggleSelectorHighlighter: PropTypes.func.isRequired,
       rules: PropTypes.arrayOf(PropTypes.shape(Types.rule)).isRequired,
     };
   }
 
   render() {
     const {
       onToggleDeclaration,
+      onToggleSelectorHighlighter,
       rules,
     } = this.props;
 
     return rules.map(rule => {
       return Rule({
         key: rule.id,
         onToggleDeclaration,
+        onToggleSelectorHighlighter,
         rule,
       });
     });
   }
 }
 
 module.exports = Rules;
--- a/devtools/client/inspector/rules/components/RulesApp.js
+++ b/devtools/client/inspector/rules/components/RulesApp.js
@@ -25,16 +25,17 @@ const Types = require("../types");
 
 const SHOW_PSEUDO_ELEMENTS_PREF = "devtools.inspector.show_pseudo_elements";
 
 class RulesApp extends PureComponent {
   static get propTypes() {
     return {
       onToggleDeclaration: PropTypes.func.isRequired,
       onTogglePseudoClass: PropTypes.func.isRequired,
+      onToggleSelectorHighlighter: PropTypes.func.isRequired,
       rules: PropTypes.arrayOf(PropTypes.shape(Types.rule)).isRequired,
     };
   }
 
   renderInheritedRules(rules) {
     if (!rules.length) {
       return null;
     }
@@ -48,16 +49,17 @@ class RulesApp extends PureComponent {
 
         output.push(
           dom.div({ className: "ruleview-header" }, rule.inheritance.inheritedSource)
         );
       }
 
       output.push(Rule({
         onToggleDeclaration: this.props.onToggleDeclaration,
+        onToggleSelectorHighlighter: this.props.onToggleSelectorHighlighter,
         rule,
       }));
     }
 
     return output;
   }
 
   renderKeyframesRules(rules) {
@@ -75,16 +77,17 @@ class RulesApp extends PureComponent {
 
       lastKeyframes = rule.keyframesRule.id;
 
       const items = [
         {
           component: Rules,
           componentProps: {
             onToggleDeclaration: this.props.onToggleDeclaration,
+            onToggleSelectorHighlighter: this.props.onToggleSelectorHighlighter,
             rules: rules.filter(r => r.keyframesRule.id === lastKeyframes),
           },
           header: rule.keyframesRule.keyframesName,
           opened: true,
         },
       ];
 
       output.push(Accordion({ items }));
@@ -95,30 +98,32 @@ class RulesApp extends PureComponent {
 
   renderStyleRules(rules) {
     if (!rules.length) {
       return null;
     }
 
     return Rules({
       onToggleDeclaration: this.props.onToggleDeclaration,
+      onToggleSelectorHighlighter: this.props.onToggleSelectorHighlighter,
       rules,
     });
   }
 
   renderPseudoElementRules(rules) {
     if (!rules.length) {
       return null;
     }
 
     const items = [
       {
         component: Rules,
         componentProps: {
           onToggleDeclaration: this.props.onToggleDeclaration,
+          onToggleSelectorHighlighter: this.props.onToggleSelectorHighlighter,
           rules,
         },
         header: getStr("rule.pseudoElement"),
         opened: Services.prefs.getBoolPref(SHOW_PSEUDO_ELEMENTS_PREF),
         onToggled: () => {
           const opened = Services.prefs.getBoolPref(SHOW_PSEUDO_ELEMENTS_PREF);
           Services.prefs.setBoolPref(SHOW_PSEUDO_ELEMENTS_PREF, !opened);
         },
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/components/SelectorHighlighter.js
@@ -0,0 +1,84 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const { getStr } = require("../utils/l10n");
+const Types = require("../types");
+
+class SelectorHighlighter extends PureComponent {
+  static get propTypes() {
+    return {
+      highlightedSelector: PropTypes.string.isRequired,
+      onToggleSelectorHighlighter: PropTypes.func.isRequired,
+      selector: PropTypes.shape(Types.selector).isRequired,
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      // A unique selector to the current Rule. This is checked against the value
+      // of any existing highlighted selector in order mark the toggled state of
+      // the component.
+      uniqueSelector: "",
+    };
+
+    this.onToggleHighlighterClick = this.onToggleHighlighterClick.bind(this);
+  }
+
+  componentDidMount() {
+    // Only fetch the unique selector if a selector is highlighted.
+    if (this.props.highlightedSelector) {
+      this.updateState();
+    }
+  }
+
+  async onToggleHighlighterClick(event) {
+    event.stopPropagation();
+
+    const {
+      onToggleSelectorHighlighter,
+      selector,
+    } = this.props;
+
+    const uniqueSelector = await selector.getUniqueSelector();
+    this.setState({ uniqueSelector });
+    onToggleSelectorHighlighter(uniqueSelector);
+  }
+
+  async updateState() {
+    const uniqueSelector = await this.props.selector.getUniqueSelector();
+    this.setState({ uniqueSelector });
+  }
+
+  render() {
+    const { highlightedSelector } = this.props;
+    const { uniqueSelector } = this.state;
+
+    return (
+      dom.span({
+        className: "ruleview-selectorhighlighter" +
+                   (highlightedSelector && highlightedSelector === uniqueSelector ?
+                    " highlighted" : ""),
+        onClick: this.onToggleHighlighterClick,
+        title: getStr("rule.selectorHighlighter.tooltip"),
+      })
+    );
+  }
+}
+
+const mapStateToProps = state => {
+  return {
+    highlightedSelector: state.rules.highlightedSelector,
+  };
+};
+
+module.exports = connect(mapStateToProps)(SelectorHighlighter);
--- a/devtools/client/inspector/rules/components/moz.build
+++ b/devtools/client/inspector/rules/components/moz.build
@@ -7,11 +7,12 @@ DevToolsModules(
     'Declaration.js',
     'Declarations.js',
     'PseudoClassPanel.js',
     'Rule.js',
     'Rules.js',
     'RulesApp.js',
     'SearchBox.js',
     'Selector.js',
+    'SelectorHighlighter.js',
     'SourceLink.js',
     'Toolbar.js',
 )
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -52,16 +52,18 @@ function Rule(elementStyle, options) {
   }
 
   this.cssProperties = this.elementStyle.ruleView.cssProperties;
 
   // Populate the text properties with the style's current authoredText
   // value, and add in any disabled properties from the store.
   this.textProps = this._getTextProperties();
   this.textProps = this.textProps.concat(this._getDisabledProperties());
+
+  this.getUniqueSelector = this.getUniqueSelector.bind(this);
 }
 
 Rule.prototype = {
   mediaText: "",
 
   get declarations() {
     return this.textProps;
   },
@@ -74,16 +76,17 @@ Rule.prototype = {
     return {
       inherited: this.inherited,
       inheritedSource: this.inheritedSource,
     };
   },
 
   get selector() {
     return {
+      getUniqueSelector: this.getUniqueSelector,
       matchedSelectors: this.matchedSelectors,
       selectors: this.domRule.selectors,
       selectorText: this.keyframes ? this.domRule.keyText : this.selectorText,
     };
   },
 
   get sourceLink() {
     return {
@@ -176,16 +179,37 @@ Rule.prototype = {
    * @return {TextProperty|undefined} with the given id in the current Rule or undefined
    * if it cannot be found.
    */
   getDeclaration: function(id) {
     return this.textProps.find(textProp => textProp.id === id);
   },
 
   /**
+   * Returns an unique selector for the CSS rule.
+   */
+  async getUniqueSelector() {
+    let selector = "";
+
+    if (this.domRule.selectors) {
+      // This is a style rule with a selector.
+      selector = this.domRule.selectors.join(", ");
+    } else if (this.inherited) {
+      // This is an inline style from an inherited rule. Need to resolve the unique
+      // selector from the node which rule this is inherited from.
+      selector = await this.inherited.getUniqueSelector();
+    } else {
+      // This is an inline style from the current node.
+      selector = this.elementStyle.ruleView.inspector.selectionCssSelector;
+    }
+
+    return selector;
+  },
+
+  /**
    * Returns true if the rule matches the creation options
    * specified.
    *
    * @param {Object} options
    *        Creation options. See the Rule constructor for documentation.
    */
   matches: function(options) {
     return this.domRule === options.rule;
--- a/devtools/client/inspector/rules/new-rules.js
+++ b/devtools/client/inspector/rules/new-rules.js
@@ -3,23 +3,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
+const EventEmitter = require("devtools/shared/event-emitter");
 
 const {
   disableAllPseudoClasses,
   setPseudoClassLocks,
   togglePseudoClass,
 } = require("./actions/pseudo-classes");
-const { updateRules } = require("./actions/rules");
+const {
+  updateHighlightedSelector,
+  updateRules,
+} = require("./actions/rules");
 
 const RulesApp = createFactory(require("./components/RulesApp"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
@@ -35,33 +39,37 @@ class RulesView {
     this.telemetry = inspector.telemetry;
     this.toolbox = inspector.toolbox;
 
     this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
 
     this.onSelection = this.onSelection.bind(this);
     this.onToggleDeclaration = this.onToggleDeclaration.bind(this);
     this.onTogglePseudoClass = this.onTogglePseudoClass.bind(this);
+    this.onToggleSelectorHighlighter = this.onToggleSelectorHighlighter.bind(this);
     this.updateRules = this.updateRules.bind(this);
 
     this.inspector.sidebar.on("select", this.onSelection);
     this.selection.on("detached-front", this.onSelection);
     this.selection.on("new-node-front", this.onSelection);
 
     this.init();
+
+    EventEmitter.decorate(this);
   }
 
   init() {
     if (!this.inspector) {
       return;
     }
 
     const rulesApp = RulesApp({
       onToggleDeclaration: this.onToggleDeclaration,
       onTogglePseudoClass: this.onTogglePseudoClass,
+      onToggleSelectorHighlighter: this.onToggleSelectorHighlighter,
     });
 
     const provider = createElement(Provider, {
       id: "ruleview",
       key: "ruleview",
       store: this.store,
       title: INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
     }, rulesApp);
@@ -70,16 +78,21 @@ class RulesView {
     this.provider = provider;
   }
 
   destroy() {
     this.inspector.sidebar.off("select", this.onSelection);
     this.selection.off("detached-front", this.onSelection);
     this.selection.off("new-node-front", this.onSelection);
 
+    if (this._selectHighlighter) {
+      this._selectorHighlighter.finalize();
+      this._selectorHighlighter = null;
+    }
+
     if (this.elementStyle) {
       this.elementStyle.destroy();
     }
 
     this._dummyElement = null;
     this.cssProperties = null;
     this.doc = null;
     this.elementStyle = null;
@@ -105,16 +118,51 @@ class RulesView {
     if (!this._dummyElement) {
       this._dummyElement = this.doc.createElement("div");
     }
 
     return this._dummyElement;
   }
 
   /**
+   * Get the highlighters overlay from the Inspector.
+   *
+   * @return {HighlighterOverlay}.
+   */
+  get highlighters() {
+    return this.inspector.highlighters;
+  }
+
+  /**
+   * Get an instance of SelectorHighlighter (used to highlight nodes that match
+   * selectors in the rule-view).
+   *
+   * @return {Promise} resolves to the instance of the highlighter.
+   */
+  async getSelectorHighlighter() {
+    if (!this.inspector) {
+      return null;
+    }
+
+    if (this._selectorHighlighter) {
+      return this._selectorHighlighter;
+    }
+
+    try {
+      const front = this.inspector.inspector;
+      this._selectorHighlighter = await front.getHighlighterByType("SelectorHighlighter");
+      return this._selectorHighlighter;
+    } catch (e) {
+      // The SelectorHighlighter type could not be created in the
+      // current target. It could be an older server, or a XUL page.
+      return null;
+    }
+  }
+
+  /**
    * Returns true if the rules panel is visible, and false otherwise.
    */
   isPanelVisible() {
     return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
            this.inspector.toolbox.currentToolId === "inspector" &&
            this.inspector.sidebar.getCurrentTabID() === "newruleview";
   }
 
@@ -160,16 +208,55 @@ class RulesView {
    *         The pseudo class to toggle on or off.
    */
   onTogglePseudoClass(value) {
     this.store.dispatch(togglePseudoClass(value));
     this.inspector.togglePseudoClass(value);
   }
 
   /**
+   * Handler for toggling the selector highlighter for the given selector.
+   * Highlight/unhighlight all the nodes that match a given set of selectors inside the
+   * document of the current selected node. Only one selector can be highlighted at a
+   * time, so calling the method a second time with a different selector will first
+   * unhighlight the previously highlighted nodes. Calling the method a second time with
+   * the same select will unhighlight the highlighted nodes.
+   *
+   * @param  {String} selector
+   *         The selector used to find nodes in the page.
+   */
+  async onToggleSelectorHighlighter(selector) {
+    const highlighter = await this.getSelectorHighlighter();
+    if (!highlighter) {
+      return;
+    }
+
+    await highlighter.hide();
+
+    if (selector !== this.highlighters.selectorHighlighterShown) {
+      this.store.dispatch(updateHighlightedSelector(selector));
+
+      await highlighter.show(this.selection.nodeFront, {
+        hideInfoBar: true,
+        hideGuides: true,
+        selector,
+      });
+
+      this.highlighters.selectorHighlighterShown = selector;
+      // This event is emitted for testing purposes.
+      this.emit("ruleview-selectorhighlighter-toggled", true);
+    } else {
+      this.highlighters.selectorHighlighterShown = null;
+      this.store.dispatch(updateHighlightedSelector(""));
+      // This event is emitted for testing purposes.
+      this.emit("ruleview-selectorhighlighter-toggled", false);
+    }
+  }
+
+  /**
    * Updates the rules view by dispatching the new rules data of the newly selected
    * element. This is called when the rules view becomes visible or upon new node
    * selection.
    *
    * @param  {NodeFront|null} element
    *         The NodeFront of the current selected element.
    */
   async update(element) {
--- a/devtools/client/inspector/rules/reducers/rules.js
+++ b/devtools/client/inspector/rules/reducers/rules.js
@@ -1,19 +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/. */
 
 "use strict";
 
 const {
   UPDATE_RULES,
+  UPDATE_HIGHLIGHTED_SELECTOR,
 } = require("../actions/index");
 
 const INITIAL_RULES = {
+  // The selector of the node that is highlighted by the selector highlighter.
+  highlightedSelector: "",
   // Array of CSS rules.
   rules: [],
 };
 
 /**
  * Given a rule's TextProperty, returns the properties that are needed to render a
  * CSS declaration.
  *
@@ -76,19 +79,27 @@ function getRuleState(rule) {
     sourceLink: rule.sourceLink,
     // The CSS rule type.
     type: rule.domRule.type,
   };
 }
 
 const reducers = {
 
-  [UPDATE_RULES](_, { rules }) {
+  [UPDATE_HIGHLIGHTED_SELECTOR](rules, { highlightedSelector }) {
     return {
-      rules: rules.map(rule => getRuleState(rule)),
+      ...rules,
+      highlightedSelector,
+    };
+  },
+
+  [UPDATE_RULES](rules, { rules: newRules }) {
+    return {
+      highlightedSelector: rules.highlightedSelector,
+      rules: newRules.map(rule => getRuleState(rule)),
     };
   },
 
 };
 
 module.exports = function(rules = INITIAL_RULES, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
--- a/devtools/client/inspector/rules/types.js
+++ b/devtools/client/inspector/rules/types.js
@@ -80,16 +80,18 @@ exports.pseudoClasses = {
     isDisabled: PropTypes.bool,
   }),
 };
 
 /**
  * A CSS selector.
  */
 const selector = exports.selector = {
+  // Function that returns a Promise containing an unique CSS selector.
+  getUniqueSelector: PropTypes.func,
   // Array of the selectors that match the selected element.
   matchedSelectors: PropTypes.arrayOf(PropTypes.string),
   // The CSS rule's selector text content.
   selectorText: PropTypes.string,
   // Array of the CSS rule's selectors.
   selectors: PropTypes.arrayOf(PropTypes.string),
 };
 
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -3718,16 +3718,38 @@ exports.CSS_PROPERTIES = {
       "ridge",
       "solid",
       "thick",
       "thin",
       "transparent",
       "unset"
     ]
   },
+  "border-block-color": {
+    "isInherited": false,
+    "subproperties": [
+      "border-block-start-color",
+      "border-block-end-color"
+    ],
+    "supports": [
+      2
+    ],
+    "values": [
+      "COLOR",
+      "currentColor",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "rgb",
+      "rgba",
+      "transparent",
+      "unset"
+    ]
+  },
   "border-block-end": {
     "isInherited": false,
     "subproperties": [
       "border-block-end-color",
       "border-block-end-style",
       "border-block-end-width"
     ],
     "supports": [
@@ -3906,16 +3928,55 @@ exports.CSS_PROPERTIES = {
       "inherit",
       "initial",
       "medium",
       "thick",
       "thin",
       "unset"
     ]
   },
+  "border-block-style": {
+    "isInherited": false,
+    "subproperties": [
+      "border-block-start-style",
+      "border-block-end-style"
+    ],
+    "supports": [],
+    "values": [
+      "dashed",
+      "dotted",
+      "double",
+      "groove",
+      "hidden",
+      "inherit",
+      "initial",
+      "inset",
+      "none",
+      "outset",
+      "ridge",
+      "solid",
+      "unset"
+    ]
+  },
+  "border-block-width": {
+    "isInherited": false,
+    "subproperties": [
+      "border-block-start-width",
+      "border-block-end-width"
+    ],
+    "supports": [],
+    "values": [
+      "inherit",
+      "initial",
+      "medium",
+      "thick",
+      "thin",
+      "unset"
+    ]
+  },
   "border-bottom": {
     "isInherited": false,
     "subproperties": [
       "border-bottom-color",
       "border-bottom-style",
       "border-bottom-width"
     ],
     "supports": [
@@ -4227,16 +4288,38 @@ exports.CSS_PROPERTIES = {
       "ridge",
       "solid",
       "thick",
       "thin",
       "transparent",
       "unset"
     ]
   },
+  "border-inline-color": {
+    "isInherited": false,
+    "subproperties": [
+      "border-inline-start-color",
+      "border-inline-end-color"
+    ],
+    "supports": [
+      2
+    ],
+    "values": [
+      "COLOR",
+      "currentColor",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "rgb",
+      "rgba",
+      "transparent",
+      "unset"
+    ]
+  },
   "border-inline-end": {
     "isInherited": false,
     "subproperties": [
       "border-inline-end-color",
       "border-inline-end-style",
       "border-inline-end-width"
     ],
     "supports": [
@@ -4415,16 +4498,55 @@ exports.CSS_PROPERTIES = {
       "inherit",
       "initial",
       "medium",
       "thick",
       "thin",
       "unset"
     ]
   },
+  "border-inline-style": {
+    "isInherited": false,
+    "subproperties": [
+      "border-inline-start-style",
+      "border-inline-end-style"
+    ],
+    "supports": [],
+    "values": [
+      "dashed",
+      "dotted",
+      "double",
+      "groove",
+      "hidden",
+      "inherit",
+      "initial",
+      "inset",
+      "none",
+      "outset",
+      "ridge",
+      "solid",
+      "unset"
+    ]
+  },
+  "border-inline-width": {
+    "isInherited": false,
+    "subproperties": [
+      "border-inline-start-width",
+      "border-inline-end-width"
+    ],
+    "supports": [],
+    "values": [
+      "inherit",
+      "initial",
+      "medium",
+      "thick",
+      "thin",
+      "unset"
+    ]
+  },
   "border-left": {
     "isInherited": false,
     "subproperties": [
       "border-left-color",
       "border-left-style",
       "border-left-width"
     ],
     "supports": [
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3972,16 +3972,17 @@ nsCSSFrameConstructor::FindXULTagData(co
 #ifdef MOZ_XUL
       SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
       SCROLLABLE_XUL_CREATE(thumb, NS_NewButtonBoxFrame),
       SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
       SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
       SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
       SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
       SCROLLABLE_XUL_CREATE(toolbarpaletteitem, NS_NewBoxFrame),
+      SCROLLABLE_XUL_CREATE(treecolpicker, NS_NewButtonBoxFrame),
       SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
       SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
       SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
       SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
       SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
       SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
       SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
       SIMPLE_TAG_CHAIN(description,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1313,37 +1313,72 @@ var gCSSProperties = {
     domProp: "borderInlineEnd",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ],
     initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
     other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
     invalid_values: [ "5%", "5", "5 green none" ]
   },
+  "border-inline-color": {
+    domProp: "borderInlineColor",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-inline-start-color", "border-inline-end-color" ],
+    initial_values: [ "currentColor" ],
+    other_values: [ "green", "rgba(255,128,0,0.5) blue", "blue transparent" ],
+    invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ]
+  },
   "border-inline-end-color": {
     domProp: "borderInlineEndColor",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     initial_values: [ "currentColor" ],
     other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
     invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ]
   },
+  "border-inline-style": {
+    domProp: "borderInlineStyle",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-inline-start-style", "border-inline-end-style" ],
+    initial_values: [ "none" ],
+    other_values: [ "solid", "dashed solid", "solid dotted", "double double", "inset outset",
+                    "inset double", "none groove", "ridge none" ],
+    invalid_values: []
+  },
   "border-inline-end-style": {
     domProp: "borderInlineEndStyle",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     /* XXX hidden is sometimes the same as initial */
     initial_values: [ "none" ],
     other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
     invalid_values: []
   },
+  "border-inline-width": {
+    domProp: "borderInlineWidth",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-inline-start-width", "border-inline-end-width" ],
+    prerequisites: { "border-style": "solid" },
+    initial_values: [ "medium", "3px", "medium medium" ],
+    other_values: [ "thin", "thick", "1px", "2em",
+      "calc(2px)", "calc(2px) thin",
+      "calc(-2px)", "calc(-2px) thick",
+      "calc(0em)", "medium calc(0em)",
+      "calc(0px)", "1px calc(0px)",
+      "calc(5em)", "1em calc(5em)",
+    ],
+    invalid_values: [ "5%", "5", "5 thin", "thin 5%", "blue", "solid" ]
+  },
   "border-inline-end-width": {
     domProp: "borderInlineEndWidth",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     prerequisites: { "border-inline-end-style": "solid" },
     initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
@@ -6019,25 +6054,16 @@ var gCSSProperties = {
   "text-orientation": {
     domProp: "textOrientation",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "mixed" ],
     other_values: [ "upright", "sideways", "sideways-right" ], /* sideways-right alias for backward compatibility */
     invalid_values: [ "none", "3em", "sideways-left" ] /* sideways-left removed from CSS Writing Modes */
   },
-  "border-block-end": {
-    domProp: "borderBlockEnd",
-    inherited: false,
-    type: CSS_TYPE_TRUE_SHORTHAND,
-    subproperties: [ "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
-    initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
-    other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
-    invalid_values: [ "5%", "5", "5 solid green" ]
-  },
   "block-size": {
     domProp: "blockSize",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     axis: true,
     /* XXX testing auto has prerequisites */
     initial_values: [ "auto" ],
@@ -6059,37 +6085,81 @@ var gCSSProperties = {
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "border-block-start-color", "border-block-start-style", "border-block-start-width",
                      "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
     initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
     other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
     invalid_values: [ "5%", "5", "5 solid green" ]
   },
+  "border-block-end": {
+    domProp: "borderBlockEnd",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
+    initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+    other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+    invalid_values: [ "5%", "5", "5 solid green" ]
+  },
+  "border-block-color": {
+    domProp: "borderBlockColor",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-block-start-color", "border-block-end-color" ],
+    initial_values: [ "currentColor" ],
+    other_values: [ "green", "rgba(255,128,0,0.5) blue", "blue transparent" ],
+    invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ]
+  },
   "border-block-end-color": {
     domProp: "borderBlockEndColor",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     initial_values: [ "currentColor" ],
     other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
     invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ]
   },
+  "border-block-style": {
+    domProp: "borderBlockStyle",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-block-start-style", "border-block-end-style" ],
+    initial_values: [ "none" ],
+    other_values: [ "solid", "dashed solid", "solid dotted", "double double", "inset outset",
+                    "inset double", "none groove", "ridge none" ],
+    invalid_values: []
+  },
   "border-block-end-style": {
     domProp: "borderBlockEndStyle",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     /* XXX hidden is sometimes the same as initial */
     initial_values: [ "none" ],
     other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
     invalid_values: []
   },
+  "border-block-width": {
+    domProp: "borderBlockWidth",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-block-start-width", "border-block-end-width" ],
+    prerequisites: { "border-style": "solid" },
+    initial_values: [ "medium", "3px", "medium medium" ],
+    other_values: [ "thin", "thick", "1px", "2em",
+      "calc(2px)", "calc(2px) thin",
+      "calc(-2px)", "calc(-2px) thick",
+      "calc(0em)", "medium calc(0em)",
+      "calc(0px)", "1px calc(0px)",
+      "calc(5em)", "1em calc(5em)",
+    ],
+    invalid_values: [ "5%", "5", "5 thin", "thin 5%", "blue", "solid" ]
+  },
   "border-block-end-width": {
     domProp: "borderBlockEndWidth",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     prerequisites: { "border-block-end-style": "solid" },
     initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
--- a/layout/style/test/test_visited_reftests.html
+++ b/layout/style/test/test_visited_reftests.html
@@ -87,18 +87,17 @@ var gTests = [
   //"== first-line-1.html first-line-1-ref.html",
   "== white-to-transparent-1.html white-to-transparent-1-ref.html",
   "== link-root-1.xhtml link-root-1-ref.xhtml",
   "== mathml-links.html mathml-links-ref.html",
   "== placeholder-1.html placeholder-1-ref.html",
   "== visited-inherit-1.html visited-inherit-1-ref.html",
   "== transition-on-visited.html transition-on-visited-ref.html",
   "== logical-box-border-color-visited-link-001.html logical-box-border-color-visited-link-ref.html",
-  // TODO: test should equal the reference after implementing logical border shorthands.
-  "!= logical-box-border-color-visited-link-002.html logical-box-border-color-visited-link-ref.html",
+  "== logical-box-border-color-visited-link-002.html logical-box-border-color-visited-link-ref.html",
   "== logical-box-border-color-visited-link-003.html logical-box-border-color-visited-link-ref.html",
   "== svg-paint-currentcolor-visited.svg svg-paint-currentcolor-visited-ref.svg",
 ];
 
 // We record the maximum number of times we had to look at a test before
 // it switched to the passing state (though we assume it's 10 to start
 // rather than 0 so that we have a reasonable default).  Then we make a
 // test "time out" if it takes more than gTimeoutFactor times that
--- a/servo/components/style/properties/shorthands/border.mako.rs
+++ b/servo/components/style/properties/shorthands/border.mako.rs
@@ -352,16 +352,60 @@ pub fn parse_border<'i, 't>(
             self.border_image_outset.to_css(dest)?;
             dest.write_str(" ")?;
             self.border_image_repeat.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
 % for axis in ["block", "inline"]:
+    % for prop in ["width", "style", "color"]:
+        <%
+            spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop)
+        %>
+        <%helpers:shorthand
+            name="border-${axis}-${prop}"
+            sub_properties="${' '.join(
+                'border-%s-%s-%s' % (axis, side, prop)
+                for side in ['start', 'end']
+            )}"
+            spec="${spec}">
+
+            use crate::properties::longhands::border_${axis}_start_${prop};
+            pub fn parse_value<'i, 't>(
+                context: &ParserContext,
+                input: &mut Parser<'i, 't>,
+            ) -> Result<Longhands, ParseError<'i>> {
+                let start_value = border_${axis}_start_${prop}::parse(context, input)?;
+                let end_value =
+                    input.try(|input| border_${axis}_start_${prop}::parse(context, input)).unwrap_or_else(|_| start_value.clone());
+
+                Ok(expanded! {
+                    border_${axis}_start_${prop}: start_value,
+                    border_${axis}_end_${prop}: end_value,
+                })
+            }
+
+            impl<'a> ToCss for LonghandsToSerialize<'a>  {
+                fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+                    self.border_${axis}_start_${prop}.to_css(dest)?;
+
+                    if self.border_${axis}_end_${prop} != self.border_${axis}_start_${prop} {
+                        dest.write_str(" ")?;
+                        self.border_${axis}_end_${prop}.to_css(dest)?;
+                    }
+
+                    Ok(())
+                }
+            }
+        </%helpers:shorthand>
+    % endfor
+% endfor
+
+% for axis in ["block", "inline"]:
     <%
         spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis)
     %>
     <%helpers:shorthand
         name="border-${axis}"
         sub_properties="${' '.join(
             'border-%s-%s-width' % (axis, side)
             for side in ['start', 'end']
--- a/testing/geckodriver/doc/index.rst
+++ b/testing/geckodriver/doc/index.rst
@@ -45,15 +45,15 @@ For developers
 
 
 Communication
 =============
 
 The mailing list for geckodriver discussion is
 tools-marionette@lists.mozilla.org (`subscribe`_, `archive`_).
 
-If you prefer real-time chat, there is often someone in the #ateam IRC
+If you prefer real-time chat, there is often someone in the #interop IRC
 channel on irc.mozilla.org.  Don’t ask if you may ask a question;
 just go ahead and ask, and please wait for an answer as we might
 not be in your timezone.
 
 .. _subscribe: https://lists.mozilla.org/listinfo/tools-marionette
 .. _archive: https://lists.mozilla.org/pipermail/tools-marionette/
--- a/testing/mozbase/docs/manifestparser.rst
+++ b/testing/mozbase/docs/manifestparser.rst
@@ -469,17 +469,17 @@ how to use the API.  Tests are run via `
 
 Bugs
 ````
 
 Please file any bugs or feature requests at
 
 https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
 
-Or contact jhammel @mozilla.org or in #ateam on irc.mozilla.org
+Or contact in #cia on irc.mozilla.org
 
 CLI
 ```
 
 Run `manifestparser help` for usage information.
 
 To create a manifest from a set of directories:
 
--- a/testing/web-platform/meta/css/css-logical/logical-box-border-color.html.ini
+++ b/testing/web-platform/meta/css/css-logical/logical-box-border-color.html.ini
@@ -1,39 +1,9 @@
 [logical-box-border-color.html]
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-color shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '.]
-    expected: FAIL
-
   [Test that border-block-color shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-inline-color shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-color shorthand sets longhands and serializes correctly.]
     expected: FAIL
--- a/testing/web-platform/meta/css/css-logical/logical-box-border-style.html.ini
+++ b/testing/web-platform/meta/css/css-logical/logical-box-border-style.html.ini
@@ -1,39 +1,9 @@
 [logical-box-border-style.html]
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-style shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '.]
-    expected: FAIL
-
   [Test that border-inline-style shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-block-style shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-style shorthand sets longhands and serializes correctly.]
     expected: FAIL
--- a/testing/web-platform/meta/css/css-logical/logical-box-border-width.html.ini
+++ b/testing/web-platform/meta/css/css-logical/logical-box-border-width.html.ini
@@ -1,39 +1,9 @@
 [logical-box-border-width.html]
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that border-*-width shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '.]
-    expected: FAIL
-
   [Test that border-width shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-block-width shorthand sets longhands and serializes correctly.]
     expected: FAIL
 
   [Test that border-inline-width shorthand sets longhands and serializes correctly.]
     expected: FAIL
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -127,16 +127,17 @@ skip-if = os == "android" # checking for
 skip-if = os == 'android' # Bug 1258975 on android.
 [test_ext_telemetry.js]
 [test_ext_trustworthy_origin.js]
 [test_ext_topSites.js]
 skip-if = os == "android"
 [test_ext_unload_frame.js]
 skip-if = true # Too frequent intermittent failures
 [test_ext_userScripts.js]
+skip-if = os == "android" && debug # Bug 1512741
 [test_ext_userScripts_telemetry.js]
 [test_ext_webRequest_auth.js]
 skip-if = os == "android" && debug
 [test_ext_webRequest_filterResponseData.js]
 skip-if = os == "android" && debug
 [test_ext_webRequest_incognito.js]
 skip-if = os == "android" && debug
 [test_ext_webRequest_host.js]
--- a/toolkit/content/widgets/tree.js
+++ b/toolkit/content/widgets/tree.js
@@ -159,16 +159,103 @@
       if ("_ensureColumnOrder" in this.parentNode)
         this.parentNode._ensureColumnOrder();
 
     }
   }
 
   customElements.define("treechildren", MozTreeChildren);
 
+  class MozTreecolPicker extends MozElements.BaseControl {
+    constructor() {
+      super();
+
+      this.addEventListener("command", (event) => {
+        if (event.originalTarget == this) {
+          var popup = this.querySelector("[anonid=\"popup\"]");
+          this.buildPopup(popup);
+          popup.openPopup(this, "after_end");
+        } else {
+          var tree = this.parentNode.parentNode;
+          tree.stopEditing(true);
+          var menuitem = this.querySelector("[anonid=\"menuitem\"]");
+          if (event.originalTarget == menuitem) {
+            tree.columns.restoreNaturalOrder();
+            this.removeAttribute("ordinal");
+            tree._ensureColumnOrder();
+          } else {
+            var colindex = event.originalTarget.getAttribute("colindex");
+            var column = tree.columns[colindex];
+            if (column) {
+              var element = column.element;
+              if (element.getAttribute("hidden") == "true")
+                element.setAttribute("hidden", "false");
+              else
+                element.setAttribute("hidden", "true");
+            }
+          }
+        }
+      });
+
+    }
+
+    connectedCallback() {
+      if (this.delayConnectedCallback()) {
+        return;
+      }
+
+      this.textContent = "";
+      this.appendChild(MozXULElement.parseXULToFragment(`
+        <image class="tree-columnpicker-icon"></image>
+        <menupopup anonid="popup">
+          <menuseparator anonid="menuseparator"></menuseparator>
+          <menuitem anonid="menuitem" label="&restoreColumnOrder.label;"></menuitem>
+        </menupopup>
+      `, ["chrome://global/locale/tree.dtd"]));
+
+    }
+
+    buildPopup(aPopup) {
+      // We no longer cache the picker content, remove the old content.
+      while (aPopup.childNodes.length > 2)
+        aPopup.firstChild.remove();
+
+      var refChild = aPopup.firstChild;
+
+      var tree = this.parentNode.parentNode;
+      for (var currCol = tree.columns.getFirstColumn(); currCol; currCol = currCol.getNext()) {
+        // Construct an entry for each column in the row, unless
+        // it is not being shown.
+        var currElement = currCol.element;
+        if (!currElement.hasAttribute("ignoreincolumnpicker")) {
+          var popupChild = document.createElement("menuitem");
+          popupChild.setAttribute("type", "checkbox");
+          var columnName = currElement.getAttribute("display") ||
+            currElement.getAttribute("label");
+          popupChild.setAttribute("label", columnName);
+          popupChild.setAttribute("colindex", currCol.index);
+          if (currElement.getAttribute("hidden") != "true")
+            popupChild.setAttribute("checked", "true");
+          if (currCol.primary)
+            popupChild.setAttribute("disabled", "true");
+          aPopup.insertBefore(popupChild, refChild);
+        }
+      }
+
+      var hidden = !tree.enableColumnDrag;
+      const anonids = ["menuseparator", "menuitem"];
+      for (var i = 0; i < anonids.length; i++) {
+        var element = this.querySelector(`[anonid=\"${anonids[i]}\"]`);
+        element.hidden = hidden;
+      }
+    }
+  }
+
+  customElements.define("treecolpicker", MozTreecolPicker);
+
   class MozTreecol extends MozElements.BaseControl {
     static get observedAttributes() {
       return [
         "label",
         "sortdirection",
         "hideheader",
         "crop",
       ];
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -906,93 +906,9 @@
           else if (event.detail == 0)
             tree.removeAttribute("hidevscroll");
           event.stopPropagation();
         ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="columnpicker" display="xul:button"
-           extends="chrome://global/content/bindings/general.xml#basecontrol">
-    <content>
-      <xul:image class="tree-columnpicker-icon"/>
-      <xul:menupopup anonid="popup">
-        <xul:menuseparator anonid="menuseparator"/>
-        <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/>
-      </xul:menupopup>
-    </content>
-
-    <implementation>
-      <method name="buildPopup">
-        <parameter name="aPopup"/>
-        <body>
-          <![CDATA[
-            // We no longer cache the picker content, remove the old content.
-            while (aPopup.childNodes.length > 2)
-              aPopup.firstChild.remove();
-
-            var refChild = aPopup.firstChild;
-
-            var tree = this.parentNode.parentNode;
-            for (var currCol = tree.columns.getFirstColumn(); currCol;
-                 currCol = currCol.getNext()) {
-              // Construct an entry for each column in the row, unless
-              // it is not being shown.
-              var currElement = currCol.element;
-              if (!currElement.hasAttribute("ignoreincolumnpicker")) {
-                var popupChild = document.createElement("menuitem");
-                popupChild.setAttribute("type", "checkbox");
-                var columnName = currElement.getAttribute("display") ||
-                                 currElement.getAttribute("label");
-                popupChild.setAttribute("label", columnName);
-                popupChild.setAttribute("colindex", currCol.index);
-                if (currElement.getAttribute("hidden") != "true")
-                  popupChild.setAttribute("checked", "true");
-                if (currCol.primary)
-                  popupChild.setAttribute("disabled", "true");
-                aPopup.insertBefore(popupChild, refChild);
-              }
-            }
-
-            var hidden = !tree.enableColumnDrag;
-            const anonids = ["menuseparator", "menuitem"];
-            for (var i = 0; i < anonids.length; i++) {
-              var element = document.getAnonymousElementByAttribute(this, "anonid", anonids[i]);
-              element.hidden = hidden;
-            }
-          ]]>
-        </body>
-      </method>
-    </implementation>
-
-    <handlers>
-      <handler event="command">
-        <![CDATA[
-          if (event.originalTarget == this) {
-            var popup = document.getAnonymousElementByAttribute(this, "anonid", "popup");
-            this.buildPopup(popup);
-            popup.openPopup(this, "after_end");
-          } else {
-            var tree = this.parentNode.parentNode;
-            tree.stopEditing(true);
-            var menuitem = document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
-            if (event.originalTarget == menuitem) {
-              tree.columns.restoreNaturalOrder();
-              this.removeAttribute("ordinal");
-              tree._ensureColumnOrder();
-            } else {
-              var colindex = event.originalTarget.getAttribute("colindex");
-              var column = tree.columns[colindex];
-              if (column) {
-                var element = column.element;
-                if (element.getAttribute("hidden") == "true")
-                  element.setAttribute("hidden", "false");
-                else
-                  element.setAttribute("hidden", "true");
-              }
-            }
-          }
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
 </bindings>
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -443,20 +443,16 @@ tree > treechildren {
   -moz-user-select: none;
   -moz-box-flex: 1;
 }
 
 treerows {
   -moz-binding: url("chrome://global/content/bindings/tree.xml#treerows");
 }
 
-treecolpicker {
-  -moz-binding: url("chrome://global/content/bindings/tree.xml#columnpicker");
-}
-
 tree {
   -moz-box-orient: vertical;
   min-width: 0px;
   min-height: 0px;
   width: 10px;
   height: 10px;
 }