Bug 1301078 - correctly disable pasted commented-out properties; r=pbro
authorTom Tromey <tom@tromey.com>
Wed, 07 Sep 2016 11:08:12 -0600
changeset 357014 7b1cb8b0be7caa5e785159bb7c3650079d244303
parent 357013 9679b6eb4beeb595899447cea1c4e513cb91993a
child 357030 2e35fd4a4932abe6ea89311f914233dbf251b6de
child 357040 e4e5156020e885e301664de38c6c19bca74f9601
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1301078
milestone51.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 1301078 - correctly disable pasted commented-out properties; r=pbro MozReview-Commit-ID: 97ueaqvYVVN
devtools/client/inspector/rules/models/rule.js
devtools/client/inspector/rules/test/browser_rules_add-property-and-reselect.js
devtools/client/inspector/rules/test/browser_rules_add-rule-and-property.js
devtools/client/inspector/rules/test/browser_rules_add-rule-iframes.js
devtools/client/inspector/rules/test/browser_rules_add-rule-then-property-edit-selector.js
devtools/client/inspector/rules/test/browser_rules_keyframes-rule_02.js
devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
devtools/client/inspector/rules/test/browser_rules_refresh-on-attribute-change_02.js
devtools/client/shared/test/unit/test_rewriteDeclarations.js
devtools/client/styleeditor/test/browser.ini
devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js
devtools/shared/css-parsing-utils.js
devtools/shared/fronts/styles.js
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -184,17 +184,17 @@ Rule.prototype = {
       ind = this.textProps.indexOf(siblingProp) + 1;
       this.textProps.splice(ind, 0, prop);
     } else {
       ind = this.textProps.length;
       this.textProps.push(prop);
     }
 
     this.applyProperties((modifications) => {
-      modifications.createProperty(ind, name, value, priority);
+      modifications.createProperty(ind, name, value, priority, enabled);
       // Now that the rule has been updated, the server might have given us data
       // that changes the state of the property. Update it now.
       prop.updateEditor();
     });
 
     return prop;
   },
 
--- a/devtools/client/inspector/rules/test/browser_rules_add-property-and-reselect.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-property-and-reselect.js
@@ -24,17 +24,17 @@ add_task(function* () {
   checkPropertyOnAllRules(view);
 });
 
 function* setPropertyOnAllRules(view) {
   // Wait for the properties to be properly created on the backend and for the
   // view to be updated.
   let onRefreshed = view.once("ruleview-refreshed");
   for (let rule of view._elementStyle.rules) {
-    rule.editor.addProperty("font-weight", "bold", "");
+    rule.editor.addProperty("font-weight", "bold", "", true);
   }
   yield onRefreshed;
 }
 
 function checkPropertyOnAllRules(view) {
   for (let rule of view._elementStyle.rules) {
     let lastRule = rule.textProps[rule.textProps.length - 1];
 
--- a/devtools/client/inspector/rules/test/browser_rules_add-rule-and-property.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-rule-and-property.js
@@ -15,16 +15,16 @@ add_task(function* () {
 
   info("Adding a new rule for this node and blurring the new selector field");
   yield addNewRuleAndDismissEditor(inspector, view, "#testid", 1);
 
   info("Adding a new property for this rule");
   let ruleEditor = getRuleViewRuleEditor(view, 1);
 
   let onRuleViewChanged = view.once("ruleview-changed");
-  ruleEditor.addProperty("font-weight", "bold", "");
+  ruleEditor.addProperty("font-weight", "bold", "", true);
   yield onRuleViewChanged;
 
   let textProps = ruleEditor.rule.textProps;
   let prop = textProps[textProps.length - 1];
   is(prop.name, "font-weight", "The last property name is font-weight");
   is(prop.value, "bold", "The last property value is bold");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_add-rule-iframes.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-rule-iframes.js
@@ -42,16 +42,16 @@ add_task(function* () {
  * @param {String} value
  *        The value of the new property.
  */
 function* addNewProperty(view, index, name, value) {
   let idRuleEditor = getRuleViewRuleEditor(view, index);
   info(`Adding new property "${name}: ${value};"`);
 
   let onRuleViewChanged = view.once("ruleview-changed");
-  idRuleEditor.addProperty(name, value, "");
+  idRuleEditor.addProperty(name, value, "", true);
   yield onRuleViewChanged;
 
   let textProps = idRuleEditor.rule.textProps;
   let lastProperty = textProps[textProps.length - 1];
   is(lastProperty.name, name, "Last property has the expected name");
   is(lastProperty.value, value, "Last property has the expected value");
 }
--- a/devtools/client/inspector/rules/test/browser_rules_add-rule-then-property-edit-selector.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-rule-then-property-edit-selector.js
@@ -34,17 +34,17 @@ add_task(function* () {
   yield selectNode("span", inspector);
 
   info("Check new rule and property exist in the modified element");
   yield checkModifiedElement(view, "span", 1);
 });
 
 function* testAddingProperty(view, index) {
   let ruleEditor = getRuleViewRuleEditor(view, index);
-  ruleEditor.addProperty("font-weight", "bold", "");
+  ruleEditor.addProperty("font-weight", "bold", "", true);
   let textProps = ruleEditor.rule.textProps;
   let lastRule = textProps[textProps.length - 1];
   is(lastRule.name, "font-weight", "Last rule name is font-weight");
   is(lastRule.value, "bold", "Last rule value is bold");
 }
 
 function* testEditSelector(view, name) {
   let idRuleEditor = getRuleViewRuleEditor(view, 1);
--- a/devtools/client/inspector/rules/test/browser_rules_keyframes-rule_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_keyframes-rule_02.js
@@ -31,17 +31,17 @@ function* testPacman(inspector, view) {
   // Dynamic changes test disabled because of Bug 1050940
   // If this part of the test is ever enabled again, it should be changed to
   // use addProperty (in head.js) and stop using _applyingModifications
 
   // info("Test dynamic changes to keyframe rule for #pacman");
 
   // let defaultView = element.ownerDocument.defaultView;
   // let ruleEditor = view.element.children[5].childNodes[0]._ruleEditor;
-  // ruleEditor.addProperty("opacity", "0");
+  // ruleEditor.addProperty("opacity", "0", true);
 
   // yield ruleEditor._applyingModifications;
   // yield once(element, "animationend");
 
   // is
   // (
   //   convertTextPropsToString(rules.keyframeRules[1].textProps),
   //   "left: 750px; opacity: 0",
--- a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
@@ -65,22 +65,22 @@ function* testTopLeft(inspector, view) {
     })[0]._ruleEditor;
 
   is(convertTextPropsToString(elementFirstLineRule.textProps),
      "color: orange",
      "TopLeft firstLine properties are correct");
 
   let onAdded = view.once("ruleview-changed");
   let firstProp = elementFirstLineRuleView.addProperty("background-color",
-    "rgb(0, 255, 0)", "");
+    "rgb(0, 255, 0)", "", true);
   yield onAdded;
 
   onAdded = view.once("ruleview-changed");
   let secondProp = elementFirstLineRuleView.addProperty("font-style",
-    "italic", "");
+    "italic", "", true);
   yield onAdded;
 
   is(firstProp,
      elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 2],
      "First added property is on back of array");
   is(secondProp,
      elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 1],
      "Second added property is on back of array");
@@ -103,17 +103,17 @@ function* testTopLeft(inspector, view) {
 
   is((yield getComputedStyleProperty(id, ":first-line", "background-color")),
      "rgb(0, 255, 0)", "Added property should have been used.");
   is((yield getComputedStyleProperty(id, null, "text-decoration")),
      "none", "Added property should not apply to element");
 
   onAdded = view.once("ruleview-changed");
   firstProp = elementRuleView.addProperty("background-color",
-                                          "rgb(0, 0, 255)", "");
+                                          "rgb(0, 0, 255)", "", true);
   yield onAdded;
 
   is((yield getComputedStyleProperty(id, null, "background-color")),
      "rgb(0, 0, 255)", "Added property should have been used.");
   is((yield getComputedStyleProperty(id, ":first-line", "background-color")),
      "rgb(0, 255, 0)", "Added prop does not apply to pseudo");
 }
 
--- a/devtools/client/inspector/rules/test/browser_rules_refresh-on-attribute-change_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_refresh-on-attribute-change_02.js
@@ -27,17 +27,17 @@ add_task(function* () {
   yield testPropertyChange5(inspector, view, "#testid", testActor);
   yield testPropertyChange6(inspector, view, "#testid", testActor);
 });
 
 function* testPropertyChanges(inspector, ruleView) {
   info("Adding a second margin-top value in the element selector");
   let ruleEditor = ruleView._elementStyle.rules[0].editor;
   let onRefreshed = inspector.once("rule-view-refreshed");
-  ruleEditor.addProperty("margin-top", "5px", "");
+  ruleEditor.addProperty("margin-top", "5px", "", true);
   yield onRefreshed;
 
   let rule = ruleView._elementStyle.rules[0];
   validateTextProp(rule.textProps[0], false, "margin-top", "1px",
     "Original margin property active");
 }
 
 function* testPropertyChange0(inspector, ruleView, selector, testActor) {
--- a/devtools/client/shared/test/unit/test_rewriteDeclarations.js
+++ b/devtools/client/shared/test/unit/test_rewriteDeclarations.js
@@ -59,33 +59,33 @@ const TEST_DATA = [
     input: "p:v;",
     instruction: {type: "rename", name: "p", newName: "a b", index: 0},
     expected: "a\\ b:v;"
   },
   {
     desc: "simple create",
     input: "",
     instruction: {type: "create", name: "p", value: "v", priority: "important",
-                  index: 0},
+                  index: 0, enabled: true},
     expected: "p: v !important;"
   },
   {
     desc: "create between two properties",
     input: "a:b; e: f;",
     instruction: {type: "create", name: "c", value: "d", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "a:b; c: d;e: f;"
   },
   // "create" is passed the name that the user entered, and must do
   // any escaping necessary to ensure that this is an identifier.
   {
     desc: "create requiring escape",
     input: "",
     instruction: {type: "create", name: "a b", value: "d", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "a\\ b: d;"
   },
   {
     desc: "simple disable",
     input: "p:v;",
     instruction: {type: "enable", name: "p", value: false, index: 0},
     expected: "/*! p:v; */"
   },
@@ -132,95 +132,95 @@ const TEST_DATA = [
     instruction: {type: "enable", name: "color", value: true, index: 0},
     expected: "color:red; color: blue;"
   },
   {
     desc: "create requiring semicolon insertion",
     // Note the lack of a trailing semicolon.
     input: "color: red",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "color: red;a: b;"
   },
 
   // Newline insertion.
   {
     desc: "simple newline insertion",
     input: "\ncolor: red;\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\ncolor: red;\na: b;\n"
   },
   // Newline insertion.
   {
     desc: "semicolon insertion before newline",
     // Note the lack of a trailing semicolon.
     input: "\ncolor: red\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\ncolor: red;\na: b;\n"
   },
   // Newline insertion.
   {
     desc: "newline and semicolon insertion",
     // Note the lack of a trailing semicolon and newline.
     input: "\ncolor: red",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\ncolor: red;\na: b;\n"
   },
 
   // Newline insertion and indentation.
   {
     desc: "indentation with create",
     input: "\n  color: red;\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\n  color: red;\n  a: b;\n"
   },
   // Newline insertion and indentation.
   {
     desc: "indentation plus semicolon insertion before newline",
     // Note the lack of a trailing semicolon.
     input: "\n  color: red\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\n  color: red;\n  a: b;\n"
   },
   {
     desc: "indentation inserted before trailing whitespace",
     // Note the trailing whitespace.  This could come from a rule
     // like:
     // @supports (mumble) {
     //   body {
     //     color: red;
     //   }
     // }
     // Here if we create a rule we don't want it to follow
     // the indentation of the "}".
     input: "\n    color: red;\n  ",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\n    color: red;\n    a: b;\n  "
   },
   // Newline insertion and indentation.
   {
     desc: "indentation comes from preceding comment",
     // Note how the comment comes before the declaration.
     input: "\n  /* comment */ color: red\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "\n  /* comment */ color: red;\n  a: b;\n"
   },
   // Default indentation.
   {
     desc: "use of default indentation",
     input: "\n",
     instruction: {type: "create", name: "a", value: "b", priority: "",
-                  index: 0},
+                  index: 0, enabled: true},
     expected: "\n\ta: b;\n"
   },
 
   // Deletion handles newlines properly.
   {
     desc: "deletion removes newline",
     input: "a:b;\nc:d;\ne:f;",
     instruction: {type: "remove", name: "c", index: 1},
@@ -270,17 +270,17 @@ const TEST_DATA = [
     expected: "content: 'hi'; color: red;",
     changed: {0: "'hi'"}
   },
   // Termination insertion corner case.
   {
     desc: "create single quote termination",
     input: "content: 'hi",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "content: 'hi';color: red;",
     changed: {0: "'hi'"}
   },
 
   // Termination insertion corner case.
   {
     desc: "enable double quote termination",
     input: "/* content: \"hi */ color: red;",
@@ -288,17 +288,17 @@ const TEST_DATA = [
     expected: "content: \"hi\"; color: red;",
     changed: {0: "\"hi\""}
   },
   // Termination insertion corner case.
   {
     desc: "create double quote termination",
     input: "content: \"hi",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "content: \"hi\";color: red;",
     changed: {0: "\"hi\""}
   },
 
   // Termination insertion corner case.
   {
     desc: "enable url termination",
     input: "/* background-image: url(something.jpg */ color: red;",
@@ -307,17 +307,17 @@ const TEST_DATA = [
     expected: "background-image: url(something.jpg); color: red;",
     changed: {0: "url(something.jpg)"}
   },
   // Termination insertion corner case.
   {
     desc: "create url termination",
     input: "background-image: url(something.jpg",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "background-image: url(something.jpg);color: red;",
     changed: {0: "url(something.jpg)"}
   },
 
   // Termination insertion corner case.
   {
     desc: "enable url single quote termination",
     input: "/* background-image: url('something.jpg */ color: red;",
@@ -326,17 +326,17 @@ const TEST_DATA = [
     expected: "background-image: url('something.jpg'); color: red;",
     changed: {0: "url('something.jpg')"}
   },
   // Termination insertion corner case.
   {
     desc: "create url single quote termination",
     input: "background-image: url('something.jpg",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "background-image: url('something.jpg');color: red;",
     changed: {0: "url('something.jpg')"}
   },
 
   // Termination insertion corner case.
   {
     desc: "create url double quote termination",
     input: "/* background-image: url(\"something.jpg */ color: red;",
@@ -345,74 +345,74 @@ const TEST_DATA = [
     expected: "background-image: url(\"something.jpg\"); color: red;",
     changed: {0: "url(\"something.jpg\")"}
   },
   // Termination insertion corner case.
   {
     desc: "enable url double quote termination",
     input: "background-image: url(\"something.jpg",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "background-image: url(\"something.jpg\");color: red;",
     changed: {0: "url(\"something.jpg\")"}
   },
 
   // Termination insertion corner case.
   {
     desc: "create backslash termination",
     input: "something: \\",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "something: \\\\;color: red;",
     // The lexer rewrites the token before we see it.  However this is
     // so obscure as to be inconsequential.
     changed: {0: "\uFFFD\\"}
   },
 
   // Termination insertion corner case.
   {
     desc: "enable backslash single quote termination",
     input: "something: '\\",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "something: '\\\\';color: red;",
     changed: {0: "'\\\\'"}
   },
   {
     desc: "enable backslash double quote termination",
     input: "something: \"\\",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "something: \"\\\\\";color: red;",
     changed: {0: "\"\\\\\""}
   },
 
   // Termination insertion corner case.
   {
     desc: "enable comment termination",
     input: "something: blah /* comment ",
     instruction: {type: "create", name: "color", value: "red", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "something: blah /* comment*/; color: red;"
   },
 
   // Rewrite a "heuristic override" comment.
   {
     desc: "enable with heuristic override comment",
     input: "/*! walrus: zebra; */",
     instruction: {type: "enable", name: "walrus", value: true, index: 0},
     expected: "walrus: zebra;"
   },
 
   // Sanitize a bad value.
   {
     desc: "create sanitize unpaired brace",
     input: "",
     instruction: {type: "create", name: "p", value: "}", priority: "",
-                  index: 0},
+                  index: 0, enabled: true},
     expected: "p: \\};",
     changed: {0: "\\}"}
   },
   // Sanitize a bad value.
   {
     desc: "set sanitize unpaired brace",
     input: "walrus: zebra;",
     instruction: {type: "set", name: "walrus", value: "{{}}}", priority: "",
@@ -430,20 +430,35 @@ const TEST_DATA = [
   },
 
   // Creating a new declaration does not require an attempt to
   // terminate a previous commented declaration.
   {
     desc: "disabled declaration does not need semicolon insertion",
     input: "/*! no: semicolon */\n",
     instruction: {type: "create", name: "walrus", value: "zebra", priority: "",
-                  index: 1},
+                  index: 1, enabled: true},
     expected: "/*! no: semicolon */\nwalrus: zebra;\n",
     changed: {}
   },
+
+  {
+    desc: "create commented-out property",
+    input: "p: v",
+    instruction: {type: "create", name: "shoveler", value: "duck", priority: "",
+                  index: 1, enabled: false},
+    expected: "p: v;/*! shoveler: duck; */",
+  },
+  {
+    desc: "disabled create with comment ender in string",
+    input: "",
+    instruction: {type: "create", name: "content", value: "'*/'", priority: "",
+                  index: 0, enabled: false},
+    expected: "/*! content: '*\\/'; */"
+  },
 ];
 
 function rewriteDeclarations(inputString, instruction, defaultIndentation) {
   let rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
   rewriter.defaultIndentation = defaultIndentation;
 
   switch (instruction.type) {
     case "rename":
@@ -453,17 +468,18 @@ function rewriteDeclarations(inputString
 
     case "enable":
       rewriter.setPropertyEnabled(instruction.index, instruction.name,
                                   instruction.value);
       break;
 
     case "create":
       rewriter.createProperty(instruction.index, instruction.name,
-                              instruction.value, instruction.priority);
+                              instruction.value, instruction.priority,
+                              instruction.enabled);
       break;
 
     case "set":
       rewriter.setProperty(instruction.index, instruction.name,
                            instruction.value, instruction.priority);
       break;
 
     case "remove":
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -90,14 +90,15 @@ skip-if = e10s && debug # Bug 1252201 - 
 [browser_styleeditor_sv_keynav.js]
 [browser_styleeditor_sv_resize.js]
 [browser_styleeditor_selectstylesheet.js]
 [browser_styleeditor_sourcemaps.js]
 [browser_styleeditor_sourcemaps_inline.js]
 [browser_styleeditor_sourcemap_large.js]
 [browser_styleeditor_sourcemap_watching.js]
 [browser_styleeditor_sync.js]
+[browser_styleeditor_syncAddProperty.js]
 [browser_styleeditor_syncAddRule.js]
 [browser_styleeditor_syncAlreadyOpen.js]
 [browser_styleeditor_syncEditSelector.js]
 [browser_styleeditor_syncIntoRuleView.js]
 [browser_styleeditor_transition_rule.js]
 [browser_styleeditor_xul.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js
@@ -0,0 +1,45 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that adding a new rule is synced to the style editor.
+
+const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
+
+const expectedText = `
+  body {
+    border-width: 15px;
+    color: red;
+  }
+
+  #testid {
+    font-size: 4em;
+    /*! background-color: yellow; */
+  }
+  `;
+
+add_task(function* () {
+  yield addTab(TESTCASE_URI);
+  let { inspector, view } = yield openRuleView();
+  yield selectNode("#testid", inspector);
+
+  info("Focusing a new property name in the rule-view");
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let editor = yield focusEditableField(view, ruleEditor.closeBrace);
+  is(inplaceEditor(ruleEditor.newPropSpan), editor,
+    "The new property editor has focus");
+
+  let input = editor.input;
+  input.value = "/* background-color: yellow; */";
+
+  info("Pressing return to commit and focus the new value field");
+  let onModifications = view.once("ruleview-changed");
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+  yield onModifications;
+
+  let { ui } = yield openStyleEditor();
+  let sourceEditor = yield ui.editors[0].getSourceEditor();
+  let text = sourceEditor.sourceEditor.getText();
+  is(text, expectedText, "selector edits are synced");
+});
--- a/devtools/shared/css-parsing-utils.js
+++ b/devtools/shared/css-parsing-utils.js
@@ -795,20 +795,22 @@ RuleRewriter.prototype = {
    * An internal function to create a new declaration.  This does all
    * the work of |createProperty|.
    *
    * @param {Number} index index of the property in the rule.
    * @param {String} name name of the new property
    * @param {String} value value of the new property
    * @param {String} priority priority of the new property; either
    *                          the empty string or "important"
+   * @param {Boolean} enabled True if the new property should be
+   *                          enabled, false if disabled
    * @return {Promise} a promise that is resolved when the edit has
    *                   completed
    */
-  internalCreateProperty: Task.async(function* (index, name, value, priority) {
+  internalCreateProperty: Task.async(function* (index, name, value, priority, enabled) {
     this.completeInitialization(index);
     let newIndentation = "";
     if (this.hasNewLine) {
       if (this.declarations.length > 0) {
         newIndentation = this.getIndentation(this.inputString,
                                              this.declarations[0].offsets[0]);
       } else if (this.defaultIndentation) {
         newIndentation = this.defaultIndentation;
@@ -828,23 +830,28 @@ RuleRewriter.prototype = {
       let wsOffset = this.skipWhitespaceBackward(this.result,
                                                  this.result.length);
       if (this.result[wsOffset] === "\r" || this.result[wsOffset] === "\n") {
         savedWhitespace = this.result.substring(wsOffset + 1);
         this.result = this.result.substring(0, wsOffset + 1);
       }
     }
 
-    this.result += newIndentation + CSS.escape(name) + ": " +
-      this.sanitizeText(value, index);
-
+    let newText = CSS.escape(name) + ": " + this.sanitizeText(value, index);
     if (priority === "important") {
-      this.result += " !important";
+      newText += " !important";
     }
-    this.result += ";";
+    newText += ";";
+
+    if (!enabled) {
+      newText = "/*" + COMMENT_PARSING_HEURISTIC_BYPASS_CHAR + " " +
+        escapeCSSComment(newText) + " */";
+    }
+
+    this.result += newIndentation + newText;
     if (this.hasNewLine) {
       this.result += "\n";
     }
     this.result += savedWhitespace;
 
     if (this.decl) {
       // Still want to copy in the declaration previously at this
       // index.
@@ -855,20 +862,22 @@ RuleRewriter.prototype = {
   /**
    * Create a new declaration.
    *
    * @param {Number} index index of the property in the rule.
    * @param {String} name name of the new property
    * @param {String} value value of the new property
    * @param {String} priority priority of the new property; either
    *                          the empty string or "important"
+   * @param {Boolean} enabled True if the new property should be
+   *                          enabled, false if disabled
    */
-  createProperty: function (index, name, value, priority) {
+  createProperty: function (index, name, value, priority, enabled) {
     this.editPromise = this.internalCreateProperty(index, name, value,
-                                                   priority);
+                                                   priority, enabled);
   },
 
   /**
    * Set a declaration's value.
    *
    * @param {Number} index index of the property in the rule.
    *                       This can be -1 in the case where
    *                       the rule does not support setRuleText;
@@ -879,17 +888,17 @@ RuleRewriter.prototype = {
    * @param {String} priority the property's priority, either the empty
    *                          string or "important"
    */
   setProperty: function (index, name, value, priority) {
     this.completeInitialization(index);
     // We might see a "set" on a previously non-existent property; in
     // that case, act like "create".
     if (!this.decl) {
-      this.createProperty(index, name, value, priority);
+      this.createProperty(index, name, value, priority, true);
       return;
     }
 
     // Note that this assumes that "set" never operates on disabled
     // properties.
     this.result += this.inputString.substring(this.decl.offsets[0],
                                               this.decl.colonOffsets[1]) +
       this.sanitizeText(value, index);
--- a/devtools/shared/fronts/styles.js
+++ b/devtools/shared/fronts/styles.js
@@ -393,27 +393,29 @@ var RuleModificationList = Class({
       this.removeProperty(index, name);
     }
   },
 
   /**
    * Create a new property.  This implementation does nothing, because
    * |setRuleText| is not available.
    *
-   * These parameter are passed, but as they are not used in this
+   * These parameters are passed, but as they are not used in this
    * implementation, they are omitted.  They are documented here as
    * this code also defined the interface implemented by @see
    * RuleRewriter.
    *
    * @param {Number} index index of the property in the rule.
    *                       This can be -1 in the case where
    *                       the rule does not support setRuleText;
    *                       generally for setting properties
    *                       on an element's style.
    * @param {String} name name of the new property
    * @param {String} value value of the new property
    * @param {String} priority priority of the new property; either
    *                          the empty string or "important"
+   * @param {Boolean} enabled True if the new property should be
+   *                          enabled, false if disabled
    */
   createProperty: function () {
     // Nothing.
   },
 });