Merge mozilla-central to autoland. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 17 Jan 2019 23:57:04 +0200
changeset 514342 6896f8bf818180ef3bdf042d707713a0aa410b9f
parent 514341 bd5f0c60f8a9274030f8e38893fb72549ae3d5be (current diff)
parent 514313 1db2248f441513991df17a73112ffd9cc1846846 (diff)
child 514343 c28522aaf3918c5f556c65432f8bf14a65bff65a
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone66.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 autoland. CLOSED TREE
--- 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;
 }