--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -34,22 +34,29 @@ function EditingSession(doc, rules) {
this._rules = rules;
this._modifications = new Map();
}
EditingSession.prototype = {
/**
* Gets the value of a single property from the CSS rule.
*
- * @param rule The CSS rule
- * @param property The name of the property
+ * @param {StyleRuleFront} rule The CSS rule.
+ * @param {String} property The name of the property.
+ * @return {String} The value.
*/
getPropertyFromRule: function (rule, property) {
+ // Use the parsed declarations in the StyleRuleFront object if available.
+ let index = this.getPropertyIndex(property, rule);
+ if (index !== -1) {
+ return rule.declarations[index].value;
+ }
+
+ // Fallback to parsing the cssText locally otherwise.
let dummyStyle = this._element.style;
-
dummyStyle.cssText = rule.cssText;
return dummyStyle.getPropertyValue(property);
},
/**
* Returns the current value for a property as a string or the empty string if
* no style rules affect the property.
*
@@ -72,59 +79,101 @@ EditingSession.prototype = {
return value;
}
}
div.remove();
return "";
},
/**
- * Sets a number of properties on the node. Returns a promise that will be
- * resolved when the modifications are complete.
- *
+ * Get the index of a given css property name in a CSS rule.
+ * Or -1, if there are no properties in the rule yet.
+ * @param {String} name The property name.
+ * @param {StyleRuleFront} rule Optional, defaults to the element style rule.
+ * @return {Number} The property index in the rule.
+ */
+ getPropertyIndex: function (name, rule = this._rules[0]) {
+ let elementStyleRule = this._rules[0];
+ if (!elementStyleRule.declarations.length) {
+ return -1;
+ }
+
+ return elementStyleRule.declarations.findIndex(p => p.name === name);
+ },
+
+ /**
+ * Sets a number of properties on the node.
* @param properties An array of properties, each is an object with name and
* value properties. If the value is "" then the property
* is removed.
+ * @return {Promise} Resolves when the modifications are complete.
*/
- setProperties: function (properties) {
- let modifications = this._rules[0].startModifyingProperties();
+ setProperties: Task.async(function* (properties) {
+ for (let property of properties) {
+ // Get a RuleModificationList or RuleRewriter helper object from the
+ // StyleRuleActor to make changes to CSS properties.
+ // Note that RuleRewriter doesn't support modifying several properties at
+ // once, so we do this in a sequence here.
+ let modifications = this._rules[0].startModifyingProperties();
- for (let property of properties) {
+ // Remember the property so it can be reverted.
if (!this._modifications.has(property.name)) {
this._modifications.set(property.name,
this.getPropertyFromRule(this._rules[0], property.name));
}
- if (property.value == "") {
- modifications.removeProperty(-1, property.name);
- } else {
- modifications.setProperty(-1, property.name, property.value, "");
+ // Find the index of the property to be changed, or get the next index to
+ // insert the new property at.
+ let index = this.getPropertyIndex(property.name);
+ if (index === -1) {
+ index = this._rules[0].declarations.length;
}
- }
- return modifications.apply().then(null, console.error);
- },
+ if (property.value == "") {
+ modifications.removeProperty(index, property.name);
+ } else {
+ modifications.setProperty(index, property.name, property.value, "");
+ }
+
+ yield modifications.apply();
+ }
+ }),
/**
- * Reverts all of the property changes made by this instance. Returns a
- * promise that will be resolved when complete.
+ * Reverts all of the property changes made by this instance.
+ * @return {Promise} Resolves when all properties have been reverted.
*/
- revert: function () {
- let modifications = this._rules[0].startModifyingProperties();
+ revert: Task.async(function* () {
+ // Revert each property that we modified previously, one by one. See
+ // setProperties for information about why.
+ for (let [property, value] of this._modifications) {
+ let modifications = this._rules[0].startModifyingProperties();
- for (let [property, value] of this._modifications) {
+ // Find the index of the property to be reverted.
+ let index = this.getPropertyIndex(property);
+
if (value != "") {
- modifications.setProperty(-1, property, value, "");
+ // If the property doesn't exist anymore, insert at the beginning of the
+ // rule.
+ if (index === -1) {
+ index = 0;
+ }
+ modifications.setProperty(index, property, value, "");
} else {
- modifications.removeProperty(-1, property);
+ // If the property doesn't exist anymore, no need to remove it. It had
+ // not been added after all.
+ if (index === -1) {
+ continue;
+ }
+ modifications.removeProperty(index, property);
}
+
+ yield modifications.apply();
}
-
- return modifications.apply().then(null, console.error);
- },
+ }),
destroy: function () {
this._doc = null;
this._rules = null;
this._modifications.clear();
}
};
@@ -337,24 +386,25 @@ LayoutView.prototype = {
if (property.substring(0, 7) == "border-") {
let bprop = property.substring(0, property.length - 5) + "style";
let style = session.getProperty(bprop);
if (!style || style == "none" || style == "hidden") {
properties.push({ name: bprop, value: "solid" });
}
}
- session.setProperties(properties);
+ session.setProperties(properties).catch(e => console.error(e));
},
done: (value, commit) => {
editor.elt.parentNode.classList.remove("layout-editing");
if (!commit) {
- session.revert();
- session.destroy();
+ session.revert().then(() => {
+ session.destroy();
+ }, e => console.error(e));
}
}
}, event);
},
/**
* Is the layoutview visible in the sidebar.
* @return {Boolean}
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -191,17 +191,21 @@ Rule.prototype = {
this.textProps.splice(ind, 0, prop);
} else {
ind = this.textProps.length;
this.textProps.push(prop);
}
this.applyProperties((modifications) => {
modifications.createProperty(ind, name, value, priority);
+ // 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;
},
/**
* Helper function for applyProperties that is called when the actor
* does not support as-authored styles. Store disabled properties
* in the element style's store.
*/
@@ -236,16 +240,19 @@ Rule.prototype = {
if (disabledProps.length > 0) {
disabled.set(this.style, disabledProps);
} else {
disabled.delete(this.style);
}
return modifications.apply().then(() => {
let cssProps = {};
+ // Note that even though StyleRuleActors normally provide parsed
+ // declarations already, _applyPropertiesNoAuthored is only used when
+ // connected to older backend that do not provide them. So parse here.
for (let cssProp of parseDeclarations(this.style.authoredText)) {
cssProps[cssProp.name] = cssProp;
}
for (let textProp of this.textProps) {
if (!textProp.enabled) {
continue;
}
@@ -428,17 +435,23 @@ Rule.prototype = {
/**
* Get the list of TextProperties from the style. Needs
* to parse the style's authoredText.
*/
_getTextProperties: function () {
let textProps = [];
let store = this.elementStyle.store;
- let props = parseDeclarations(this.style.authoredText, true);
+
+ // Starting with FF49, StyleRuleActors provide parsed declarations.
+ let props = this.style.declarations;
+ if (!props) {
+ props = parseDeclarations(this.style.authoredText, true);
+ }
+
for (let prop of props) {
let name = prop.name;
// If the authored text has an invalid property, it will show up
// as nameless. Skip these as we don't currently have a good
// way to display them.
if (!name) {
continue;
}
--- a/devtools/client/inspector/rules/models/text-property.js
+++ b/devtools/client/inspector/rules/models/text-property.js
@@ -194,18 +194,36 @@ TextProperty.prototype = {
return true;
} catch (e) {
return false;
}
},
/**
* Validate this property. Does it make sense for this value to be assigned
- * to this property name? This does not apply the property value
+ * to this property name?
*
* @return {Boolean} true if the property value is valid, false otherwise.
*/
isValid: function () {
- return domUtils.cssPropertyIsValid(this.name, this.value);
+ // Starting with FF49, StyleRuleActors provide a list of parsed
+ // declarations, with data about their validity, but if we don't have this,
+ // compute validity locally (which might not be correct, but better than
+ // nothing).
+ if (!this.rule.domRule.declarations) {
+ return domUtils.cssPropertyIsValid(this.name, this.value);
+ }
+
+ let selfIndex = this.rule.textProps.indexOf(this);
+
+ // When adding a new property in the rule-view, the TextProperty object is
+ // created right away before the rule gets updated on the server, so we're
+ // not going to find the corresponding declaration object yet. Default to
+ // true.
+ if (!this.rule.domRule.declarations[selfIndex]) {
+ return true;
+ }
+
+ return this.rule.domRule.declarations[selfIndex].isValid;
}
};
exports.TextProperty = TextProperty;
--- 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
@@ -10,29 +10,33 @@
const TEST_URI = URL_ROOT + "doc_content_stylesheet.html";
add_task(function* () {
yield addTab(TEST_URI);
let {inspector, view} = yield openRuleView();
yield selectNode("#target", inspector);
info("Setting a font-weight property on all rules");
- setPropertyOnAllRules(view);
+ yield setPropertyOnAllRules(view);
info("Reselecting the element");
yield selectNode("body", inspector);
yield selectNode("#target", inspector);
checkPropertyOnAllRules(view);
});
-function setPropertyOnAllRules(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", "");
}
+ yield onRefreshed;
}
function checkPropertyOnAllRules(view) {
for (let rule of view._elementStyle.rules) {
let lastRule = rule.textProps[rule.textProps.length - 1];
is(lastRule.name, "font-weight", "Last rule name is font-weight");
is(lastRule.value, "bold", "Last rule value is bold");
--- a/devtools/client/inspector/rules/test/browser_rules_add-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-property_02.js
@@ -1,69 +1,66 @@
/* 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";
-//
+// Test adding a valid property to a CSS rule, and navigating through the fields
+// by pressing ENTER.
const TEST_URI = `
<style type="text/css">
#testid {
- background-color: blue;
+ color: blue;
}
- .testclass, .unmatched {
- background-color: green;
- };
</style>
- <div id='testid' class='testclass'>Styled Node</div>
- <div id='testid2'>Styled Node</div>
+ <div id='testid'>Styled Node</div>
`;
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
+ yield selectNode("#testid", inspector);
info("Focus the new property name field");
- let elementRuleEditor = getRuleViewRuleEditor(view, 0);
- let editor = yield focusNewRuleViewProperty(elementRuleEditor);
+ let ruleEditor = getRuleViewRuleEditor(view, 1);
+ let editor = yield focusNewRuleViewProperty(ruleEditor);
let input = editor.input;
- is(inplaceEditor(elementRuleEditor.newPropSpan), editor,
+ is(inplaceEditor(ruleEditor.newPropSpan), editor,
"Next focused editor should be the new property editor.");
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length,
"Editor contents are selected.");
// Try clicking on the editor's input again, shouldn't cause trouble
// (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, {}, view.styleWindow);
input.select();
info("Entering the property name");
editor.input.value = "background-color";
info("Pressing RETURN and waiting for the value field focus");
- let onNameAdded = view.once("ruleview-changed");
+ // Pressing ENTER triggeres 2 changed events, one for the new property, and
+ // one for the preview.
+ let onNameAdded = waitForNEvents(view, "ruleview-changed", 2);
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
yield onNameAdded;
editor = inplaceEditor(view.styleDocument.activeElement);
- is(elementRuleEditor.rule.textProps.length, 1,
+ is(ruleEditor.rule.textProps.length, 2,
"Should have created a new text property.");
- is(elementRuleEditor.propertyList.children.length, 1,
+ is(ruleEditor.propertyList.children.length, 2,
"Should have created a property editor.");
- let textProp = elementRuleEditor.rule.textProps[0];
+ let textProp = ruleEditor.rule.textProps[1];
is(editor, inplaceEditor(textProp.editor.valueSpan),
"Should be editing the value span now.");
info("Entering the property value");
- // We're editing inline style, so expect a style attribute mutation.
- let onMutated = inspector.once("markupmutation");
let onValueAdded = view.once("ruleview-changed");
editor.input.value = "purple";
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
yield onValueAdded;
- yield onMutated;
is(textProp.value, "purple", "Text prop should have been changed.");
});
--- a/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
@@ -54,23 +54,22 @@ function* checkCopySelection(view) {
let win = view.styleWindow;
let prop = contentDoc.querySelector(".ruleview-property");
let values = contentDoc.querySelectorAll(".ruleview-propertyvaluecontainer");
let range = contentDoc.createRange();
range.setStart(prop, 0);
range.setEnd(values[4], 2);
win.getSelection().addRange(range);
-
info("Checking that _Copy() returns the correct clipboard value");
let expectedPattern = " margin: 10em;[\\r\\n]+" +
" font-size: 14pt;[\\r\\n]+" +
- " font-family: helvetica,sans-serif;[\\r\\n]+" +
- " color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
+ " font-family: helvetica, sans-serif;[\\r\\n]+" +
+ " color: #AAA;[\\r\\n]+" +
"}[\\r\\n]+" +
"html {[\\r\\n]+" +
" color: #000000;[\\r\\n]*";
let onPopup = once(view._contextmenu._menupopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(prop,
{button: 2, type: "contextmenu"}, win);
yield onPopup;
@@ -96,18 +95,18 @@ function* checkSelectAll(view) {
let prop = contentDoc.querySelector(".ruleview-property");
info("Checking that _SelectAll() then copy returns the correct " +
"clipboard value");
view._contextmenu._onSelectAll();
let expectedPattern = "element {[\\r\\n]+" +
" margin: 10em;[\\r\\n]+" +
" font-size: 14pt;[\\r\\n]+" +
- " font-family: helvetica,sans-serif;[\\r\\n]+" +
- " color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
+ " font-family: helvetica, sans-serif;[\\r\\n]+" +
+ " color: #AAA;[\\r\\n]+" +
"}[\\r\\n]+" +
"html {[\\r\\n]+" +
" color: #000000;[\\r\\n]+" +
"}[\\r\\n]*";
let onPopup = once(view._contextmenu._menupopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(prop,
{button: 2, type: "contextmenu"}, win);
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
@@ -1,17 +1,17 @@
/* 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";
// Test "Copy color" item of the context menu #1: Test _isColorPopup.
const TEST_URI = `
- <div style="color: #123ABC; margin: 0px; background: span[data-color];">
+ <div style="color:rgb(18, 58, 188);margin:0px;background:span[data-color];">
Test "Copy color" context menu option
</div>
`;
add_task(function* () {
// Test is slow on Linux EC2 instances - Bug 1137765
requestLongerTimeout(2);
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
--- a/devtools/client/shared/css-parsing-utils.js
+++ b/devtools/client/shared/css-parsing-utils.js
@@ -903,16 +903,22 @@ RuleRewriter.prototype = {
/**
* Remove a declaration.
*
* @param {Number} index index of the property in the rule.
* @param {String} name the name of the property to remove
*/
removeProperty: function (index, name) {
this.completeInitialization(index);
+
+ // If asked to remove a property that does not exist, bail out.
+ if (!this.decl) {
+ return;
+ }
+
let copyOffset = this.decl.offsets[1];
// Maybe removing this rule left us with a completely blank
// line. In this case, we'll delete the whole thing. We only
// bother with this if we're looking at sources that already
// have a newline somewhere.
if (this.hasNewLine) {
let nlOffset = this.skipWhitespaceBackward(this.result,
this.decl.offsets[0]);
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -6,36 +6,26 @@
const {Cc, Ci} = require("chrome");
const promise = require("promise");
const protocol = require("devtools/server/protocol");
const {Arg, Option, method, RetVal, types} = protocol;
const events = require("sdk/event/core");
const {Class} = require("sdk/core/heritage");
const {LongStringActor} = require("devtools/server/actors/string");
-const {
- getDefinedGeometryProperties
-} = require("devtools/server/actors/highlighters/geometry-editor");
+const {getDefinedGeometryProperties} = require("devtools/server/actors/highlighters/geometry-editor");
+const {parseDeclarations} = require("devtools/client/shared/css-parsing-utils");
// This will also add the "stylesheet" actor type for protocol.js to recognize
-const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} =
- require("devtools/server/actors/stylesheets");
+const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} = require("devtools/server/actors/stylesheets");
loader.lazyRequireGetter(this, "CSS", "CSS");
-
-loader.lazyGetter(this, "CssLogic", () => {
- return require("devtools/shared/inspector/css-logic").CssLogic;
-});
-loader.lazyGetter(this, "DOMUtils", () => {
- return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
-
-loader.lazyGetter(this, "RuleRewriter", () => {
- return require("devtools/client/shared/css-parsing-utils").RuleRewriter;
-});
+loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
+loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
+loader.lazyGetter(this, "RuleRewriter", () => require("devtools/client/shared/css-parsing-utils").RuleRewriter);
// The PageStyle actor flattens the DOM CSS objects a little bit, merging
// Rules and their Styles into one actor. For elements (which have a style
// but no associated rule) we fake a rule with the following style id.
const ELEMENT_STYLE = 100;
exports.ELEMENT_STYLE = ELEMENT_STYLE;
// When gathering rules to read for pseudo elements, we will skip
@@ -1203,27 +1193,26 @@ var StyleRuleActor = protocol.ActorClass
// to which this rule belongs.
get marshallPool() {
return this.pageStyle;
},
// True if this rule supports as-authored styles, meaning that the
// rule text can be rewritten using setRuleText.
get canSetRuleText() {
- // Special case about:PreferenceStyleSheet, as it is
- // generated on the fly and the URI is not registered with the
- // about: handler.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
- return !!(this._parentSheet &&
- // If a rule does not have source, then it has been
- // modified via CSSOM; and we should fall back to
- // non-authored editing.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
- this.sheetActor.allRulesHaveSource() &&
- this._parentSheet.href !== "about:PreferenceStyleSheet");
+ return this.type === ELEMENT_STYLE ||
+ (this._parentSheet &&
+ // If a rule does not have source, then it has been modified via
+ // CSSOM; and we should fall back to non-authored editing.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
+ this.sheetActor.allRulesHaveSource() &&
+ // Special case about:PreferenceStyleSheet, as it is generated on
+ // the fly and the URI is not registered with the about:handler
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
+ this._parentSheet.href !== "about:PreferenceStyleSheet");
},
getDocument: function(sheet) {
let document;
if (sheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) {
document = sheet.ownerNode;
} else {
@@ -1291,16 +1280,17 @@ var StyleRuleActor = protocol.ActorClass
break;
case ELEMENT_STYLE:
// Elements don't have a parent stylesheet, and therefore
// don't have an associated URI. Provide a URI for
// those.
let doc = this.rawNode.ownerDocument;
form.href = doc.location ? doc.location.href : "";
form.cssText = this.rawStyle.cssText || "";
+ form.authoredText = this.rawNode.getAttribute("style");
break;
case Ci.nsIDOMCSSRule.CHARSET_RULE:
form.encoding = this.rawRule.encoding;
break;
case Ci.nsIDOMCSSRule.IMPORT_RULE:
form.href = this.rawRule.href;
break;
case Ci.nsIDOMCSSRule.KEYFRAMES_RULE:
@@ -1308,16 +1298,28 @@ var StyleRuleActor = protocol.ActorClass
form.name = this.rawRule.name;
break;
case Ci.nsIDOMCSSRule.KEYFRAME_RULE:
form.cssText = this.rawStyle.cssText || "";
form.keyText = this.rawRule.keyText || "";
break;
}
+ // Parse the text into a list of declarations so the client doesn't have to
+ // and so that we can safely determine if a declaration is valid rather than
+ // have the client guess it.
+ if (form.authoredText || form.cssText) {
+ let declarations = parseDeclarations(form.authoredText ||
+ form.cssText, true);
+ form.declarations = declarations.map(decl => {
+ decl.isValid = DOMUtils.cssPropertyIsValid(decl.name, decl.value);
+ return decl;
+ });
+ }
+
return form;
},
/**
* Send an event notifying that the location of the rule has
* changed.
*
* @param {Number} line the new line number
@@ -1450,31 +1452,36 @@ var StyleRuleActor = protocol.ActorClass
/**
* Set the contents of the rule. This rewrites the rule in the
* stylesheet and causes it to be re-evaluated.
*
* @param {String} newText the new text of the rule
* @returns the rule with updated properties
*/
setRuleText: method(Task.async(function* (newText) {
- if (!this.canSetRuleText ||
- (this.type !== Ci.nsIDOMCSSRule.STYLE_RULE &&
- this.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE)) {
+ if (!this.canSetRuleText) {
throw new Error("invalid call to setRuleText");
}
- let parentStyleSheet = this.pageStyle._sheetRef(this._parentSheet);
- let {str: cssText} = yield parentStyleSheet.getText();
+ if (this.type === ELEMENT_STYLE) {
+ // For element style rules, set the node's style attribute.
+ this.rawNode.setAttribute("style", newText);
+ } else {
+ // For stylesheet rules, set the text in the stylesheet.
+ let parentStyleSheet = this.pageStyle._sheetRef(this._parentSheet);
+ let {str: cssText} = yield parentStyleSheet.getText();
- let {offset, text} = getRuleText(cssText, this.line, this.column);
- cssText = cssText.substring(0, offset) + newText +
- cssText.substring(offset + text.length);
+ let {offset, text} = getRuleText(cssText, this.line, this.column);
+ cssText = cssText.substring(0, offset) + newText +
+ cssText.substring(offset + text.length);
+
+ yield parentStyleSheet.update(cssText, false, UPDATE_PRESERVING_RULES);
+ }
this.authoredText = newText;
- yield parentStyleSheet.update(cssText, false, UPDATE_PRESERVING_RULES);
return this;
}), {
request: { modification: Arg(0, "string") },
response: { rule: RetVal("domstylerule") }
}),
/**
@@ -1759,16 +1766,19 @@ protocol.FrontClass(StyleRuleActor, {
return this._form.column || -1;
},
get cssText() {
return this._form.cssText;
},
get authoredText() {
return this._form.authoredText || this._form.cssText;
},
+ get declarations() {
+ return this._form.declarations || [];
+ },
get keyText() {
return this._form.keyText;
},
get name() {
return this._form.name;
},
get selectors() {
return this._form.selectors;
--- a/devtools/server/tests/mochitest/test_styles-modify.html
+++ b/devtools/server/tests/mochitest/test_styles-modify.html
@@ -17,71 +17,86 @@ window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gWalker = null;
var gStyles = null;
var gClient = null;
-addTest(function setup() {
+addAsyncTest(function* setup() {
let url = document.getElementById("inspectorContent").href;
- attachURL(url, function(err, client, tab, doc) {
- gInspectee = doc;
- let {InspectorFront} = require("devtools/server/actors/inspector");
- let inspector = InspectorFront(client, tab);
- promiseDone(inspector.getWalker().then(walker => {
- ok(walker, "getWalker() should return an actor.");
+ let inspector;
+
+ yield new Promise(resolve => {
+ attachURL(url, function(err, client, tab, doc) {
+ gInspectee = doc;
gClient = client;
- gWalker = walker;
- return inspector.getPageStyle();
- }).then(styles => {
- gStyles = styles;
- }).then(runNextTest));
+ let {InspectorFront} = require("devtools/server/actors/inspector");
+ inspector = InspectorFront(client, tab);
+ resolve();
+ });
});
+
+ gWalker = yield inspector.getWalker();
+ gStyles = yield inspector.getPageStyle();
+
+ runNextTest();
});
-addTest(function modifyProperties() {
+addAsyncTest(function* modifyProperties() {
let localNode = gInspectee.querySelector("#inheritable-rule-inheritable-style");
- let elementStyle = null;
- promiseDone(gWalker.querySelector(gWalker.rootNode, "#inheritable-rule-inheritable-style").then(node => {
- return gStyles.getApplied(node, { inherited: false, filter: "user" });
- }).then(applied => {
- elementStyle = applied[0].rule;
- is(elementStyle.cssText, localNode.style.cssText, "Got expected css text");
+
+ let node = yield gWalker.querySelector(gWalker.rootNode,
+ "#inheritable-rule-inheritable-style");
+
+ let applied = yield gStyles.getApplied(node,
+ { inherited: false, filter: "user" });
- // Will start with "color:blue"
- let changes = elementStyle.startModifyingProperties();
+ let elementStyle = applied[0].rule;
+ is(elementStyle.cssText, localNode.style.cssText, "Got expected css text");
- // Change an existing property...
- changes.setProperty(-1, "color", "black");
- // Create a new property
- changes.setProperty(-1, "background-color", "green");
+ // Change an existing property...
+ yield setProperty(elementStyle, 0, "color", "black");
+ // Create a new property
+ yield setProperty(elementStyle, 1, "background-color", "green");
+
+ // Create a new property and then change it immediately.
+ yield setProperty(elementStyle, 2, "border", "1px solid black");
+ yield setProperty(elementStyle, 2, "border", "2px solid black");
- // Create a new property and then change it immediately.
- changes.setProperty(-1, "border", "1px solid black");
- changes.setProperty(-1, "border", "2px solid black");
+ is(elementStyle.cssText,
+ "color: black; background-color: green; border: 2px solid black;",
+ "Should have expected cssText");
+ is(elementStyle.cssText, localNode.style.cssText,
+ "Local node and style front match.");
- return changes.apply();
- }).then(() => {
- is(elementStyle.cssText, "color: black; background-color: green; border: 2px solid black;", "Should have expected cssText");
- is(elementStyle.cssText, localNode.style.cssText, "Local node and style front match.");
+ // Remove all the properties
+ yield removeProperty(elementStyle, 0, "color");
+ yield removeProperty(elementStyle, 0, "background-color");
+ yield removeProperty(elementStyle, 0, "border");
+
+ is(elementStyle.cssText, "", "Should have expected cssText");
+ is(elementStyle.cssText, localNode.style.cssText,
+ "Local node and style front match.");
- // Remove all the properties
- let changes = elementStyle.startModifyingProperties();
- changes.removeProperty(-1, "color");
- changes.removeProperty(-1, "background-color");
- changes.removeProperty(-1, "border");
+ runNextTest();
+});
- return changes.apply();
- }).then(() => {
- is(elementStyle.cssText, "", "Should have expected cssText");
- is(elementStyle.cssText, localNode.style.cssText, "Local node and style front match.");
- }).then(runNextTest));
-});
+function* setProperty(rule, index, name, value) {
+ let changes = rule.startModifyingProperties();
+ changes.setProperty(index, name, value);
+ yield changes.apply();
+}
+
+function* removeProperty(rule, index, name) {
+ let changes = rule.startModifyingProperties();
+ changes.removeProperty(index, name);
+ yield changes.apply();
+}
addTest(function cleanup() {
delete gStyles;
delete gWalker;
delete gClient;
runNextTest();
});