diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js index 3edfa1237ad7..c24f35bd9184 100644 --- a/browser/devtools/styleinspector/rule-view.js +++ b/browser/devtools/styleinspector/rule-view.js @@ -1628,12 +1628,13 @@ CssRuleView.prototype = { 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); diff --git a/browser/devtools/styleinspector/test/browser.ini b/browser/devtools/styleinspector/test/browser.ini index fb3673b7ba0b..5ed6f6bc0ea9 100644 --- a/browser/devtools/styleinspector/test/browser.ini +++ b/browser/devtools/styleinspector/test/browser.ini @@ -55,6 +55,7 @@ support-files = [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] diff --git a/browser/devtools/styleinspector/test/browser_ruleview_add-rule_pseudo_class.js b/browser/devtools/styleinspector/test/browser_ruleview_add-rule_pseudo_class.js new file mode 100644 index 000000000000..67c3d7e01e74 --- /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 = "

Test element

"; + +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"); + } +} diff --git a/toolkit/devtools/server/actors/styles.js b/toolkit/devtools/server/actors/styles.js index 25086aa64ac5..33a63d72ab4e 100644 --- a/toolkit/devtools/server/actors/styles.js +++ b/toolkit/devtools/server/actors/styles.js @@ -876,10 +876,12 @@ var PageStyleActor = protocol.ActorClass({ /** * 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; @@ -895,11 +897,16 @@ var PageStyleActor = protocol.ActorClass({ 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") }), @@ -953,8 +960,8 @@ var PageStyleFront = protocol.FrontClass(PageStyleActor, { 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]; }); }, {