Bug 1165032 - Make 'Add new rule' button add a new rule for currently emulated pseudo class locks. r=bgrins
authorTim Nguyen <ntim.bugs@gmail.com>
Mon, 08 Jun 2015 12:25:00 -0400
changeset 248135 4bd79939fea127dab2f1fbc64236d2a0dd8592fd
parent 248134 b4f355ca322fb6ca04d795720209dd59addc8a14
child 248136 fdaad6bb56fdf82a5cbc1c4053c3e84df3ef31f0
push id60888
push userkwierso@gmail.com
push dateThu, 11 Jun 2015 01:38:38 +0000
treeherdermozilla-inbound@39e638ed06bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1165032
milestone41.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
Bug 1165032 - Make 'Add new rule' button add a new rule for currently emulated pseudo class locks. r=bgrins
browser/devtools/styleinspector/rule-view.js
browser/devtools/styleinspector/test/browser.ini
browser/devtools/styleinspector/test/browser_ruleview_add-rule_pseudo_class.js
toolkit/devtools/server/actors/styles.js
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1623,22 +1623,23 @@ CssRuleView.prototype = {
   /**
    * Add a new rule to the current element.
    */
   _onAddRule: function() {
     let elementStyle = this._elementStyle;
     let element = elementStyle.element;
     let rules = elementStyle.rules;
     let client = this.inspector.toolbox._target.client;
+    let pseudoClasses = element.pseudoClassLocks;
 
     if (!client.traits.addNewRule) {
       return;
     }
 
-    this.pageStyle.addNewRule(element).then(options => {
+    this.pageStyle.addNewRule(element, pseudoClasses).then(options => {
       let newRule = new Rule(elementStyle, options);
       rules.push(newRule);
       let editor = new RuleEditor(this, newRule);
 
       // Insert the new rule editor after the inline element rule
       if (rules.length <= 1) {
         this.element.appendChild(editor.element);
       } else {
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -50,16 +50,17 @@ support-files =
 [browser_ruleview_add-property-cancel_02.js]
 [browser_ruleview_add-property-cancel_03.js]
 [browser_ruleview_add-property_01.js]
 [browser_ruleview_add-property_02.js]
 [browser_ruleview_add-property-svg.js]
 [browser_ruleview_add-rule_01.js]
 [browser_ruleview_add-rule_02.js]
 [browser_ruleview_add-rule_03.js]
+[browser_ruleview_add-rule_pseudo_class.js]
 [browser_ruleview_colorpicker-and-image-tooltip_01.js]
 [browser_ruleview_colorpicker-and-image-tooltip_02.js]
 [browser_ruleview_colorpicker-appears-on-swatch-click.js]
 [browser_ruleview_colorpicker-commit-on-ENTER.js]
 [browser_ruleview_colorpicker-edit-gradient.js]
 [browser_ruleview_colorpicker-hides-on-tooltip.js]
 [browser_ruleview_colorpicker-multiple-changes.js]
 [browser_ruleview_colorpicker-release-outside-frame.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-rule_pseudo_class.js
@@ -0,0 +1,107 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests adding a rule with pseudo class locks on
+
+let PAGE_CONTENT = "<p id='element'>Test element</p>";
+
+const EXPECTED_SELECTOR = "#element";
+const TEST_DATA = [
+  [],
+  [":hover"],
+  [":hover", ":active"],
+  [":hover", ":active", ":focus"],
+  [":active"],
+  [":active", ":focus"],
+  [":focus"]
+];
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(PAGE_CONTENT));
+
+  info("Opening the rule-view");
+  let {toolbox, inspector, view} = yield openRuleView();
+
+  info("Selecting the test element");
+  yield selectNode("#element", inspector);
+
+  info("Iterating over the test data");
+  for (let data of TEST_DATA) {
+    yield runTestData(inspector, view, data);
+  }
+});
+
+function* runTestData(inspector, view, pseudoClasses) {
+  yield setPseudoLocks(inspector, view, pseudoClasses);
+  yield addNewRule(inspector, view);
+  yield testNewRule(view, pseudoClasses, 1);
+  yield resetPseudoLocks(inspector, view);
+}
+
+function* addNewRule(inspector, view) {
+  info("Adding the new rule using the button");
+  view.addRuleButton.click();
+  info("Waiting for rule view to change");
+  let onRuleViewChanged = once(view, "ruleview-changed");
+  yield onRuleViewChanged;
+}
+
+function* testNewRule(view, pseudoClasses, index) {
+  let idRuleEditor = getRuleViewRuleEditor(view, index);
+  let editor = idRuleEditor.selectorText.ownerDocument.activeElement;
+  let expected = EXPECTED_SELECTOR + pseudoClasses.join("");
+
+  is(editor.value, expected,
+      "Selector editor value is as expected: " + expected);
+
+  info("Entering the escape key");
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+
+  is(idRuleEditor.selectorText.textContent, expected,
+      "Selector text value is as expected: " + expected);
+}
+
+function* setPseudoLocks(inspector, view, pseudoClasses) {
+  if (pseudoClasses.length == 0) {
+    return;
+  }
+  for (var pseudoClass of pseudoClasses) {
+    switch (pseudoClass) {
+      case ":hover":
+        view.hoverCheckbox.click();
+        yield inspector.once("rule-view-refreshed");
+        break;
+      case ":active":
+        view.activeCheckbox.click();
+        yield inspector.once("rule-view-refreshed");
+        break;
+      case ":focus":
+        view.focusCheckbox.click();
+        yield inspector.once("rule-view-refreshed");
+        break;
+    }
+  }
+}
+
+function* resetPseudoLocks(inspector, view) {
+  if (!view.hoverCheckbox.checked &&
+      !view.activeCheckbox.checked &&
+      !view.focusCheckbox.checked) {
+    return;
+  }
+  if (view.hoverCheckbox.checked) {
+    view.hoverCheckbox.click();
+    yield inspector.once("rule-view-refreshed");
+  }
+  if (view.activeCheckbox.checked) {
+    view.activeCheckbox.click();
+    yield inspector.once("rule-view-refreshed");
+  }
+  if (view.focusCheckbox.checked) {
+    view.focusCheckbox.click();
+    yield inspector.once("rule-view-refreshed");
+  }
+}
--- a/toolkit/devtools/server/actors/styles.js
+++ b/toolkit/devtools/server/actors/styles.js
@@ -871,40 +871,47 @@ var PageStyleActor = protocol.ActorClass
   getNewAppliedProps: function(node, rule) {
     let ruleActor = this._styleRef(rule);
     return this.getAppliedProps(node, [{ rule: ruleActor }],
       { matchedSelectors: true });
   },
 
   /**
    * Adds a new rule, and returns the new StyleRuleActor.
-   * @param   NodeActor node
+   * @param NodeActor node
+   * @param [string] pseudoClasses The list of pseudo classes to append to the
+   * new selector.
    * @returns StyleRuleActor of the new rule
    */
-  addNewRule: method(function(node) {
+  addNewRule: method(function(node, pseudoClasses) {
     let style = this.styleElement;
     let sheet = style.sheet;
     let cssRules = sheet.cssRules;
     let rawNode = node.rawNode;
 
     let selector;
     if (rawNode.id) {
       selector = "#" + CSS.escape(rawNode.id);
     } else if (rawNode.className) {
       selector = "." +
         rawNode.className.split(" ").map(c => CSS.escape(c)).join(".");
     } else {
       selector = rawNode.tagName.toLowerCase();
     }
 
+    if (pseudoClasses && pseudoClasses.length > 0) {
+      selector += pseudoClasses.join("");
+    }
+
     let index = sheet.insertRule(selector + " {}", cssRules.length);
     return this.getNewAppliedProps(node, cssRules.item(index));
   }, {
     request: {
-      node: Arg(0, "domnode")
+      node: Arg(0, "domnode"),
+      pseudoClasses: Arg(1, "nullable:array:string")
     },
     response: RetVal("appliedStylesReturn")
   }),
 });
 exports.PageStyleActor = PageStyleActor;
 
 /**
  * Front object for the PageStyleActor
@@ -948,18 +955,18 @@ var PageStyleFront = protocol.FrontClass
       yield this.getLayout(node);
     }
     let ret = yield this._getApplied(node, options);
     return ret.entries;
   }), {
     impl: "_getApplied"
   }),
 
-  addNewRule: protocol.custom(function(node) {
-    return this._addNewRule(node).then(ret => {
+  addNewRule: protocol.custom(function(node, pseudoClasses) {
+    return this._addNewRule(node, pseudoClasses).then(ret => {
       return ret.entries[0];
     });
   }, {
     impl: "_addNewRule"
   })
 });
 
 /**