Bug 707940 - Rule view should maintain user entered values; r=msucan,dcamp

This commit is contained in:
Michael Ratcliffe 2012-02-23 12:24:23 +00:00
Родитель 4db4ab8874
Коммит 64c78ccb19
3 изменённых файлов: 229 добавлений и 6 удалений

Просмотреть файл

@ -103,6 +103,13 @@ function ElementStyle(aElement, aStore)
{
this.element = aElement;
this.store = aStore || {};
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
if (!("userProperties" in this.store)) {
this.store.userProperties = new UserProperties();
}
if (this.store.disabled) {
this.store.disabled = aStore.disabled;
} else {
@ -422,6 +429,7 @@ Rule.prototype = {
applyProperties: function Rule_applyProperties()
{
let disabledProps = [];
let store = this.elementStyle.store;
for each (let prop in this.textProps) {
if (!prop.enabled) {
@ -433,10 +441,11 @@ Rule.prototype = {
continue;
}
store.userProperties.setProperty(this.style, prop.name, prop.value);
this.style.setProperty(prop.name, prop.value, prop.priority);
// Refresh the property's value from the style, to reflect
// Refresh the property's priority from the style, to reflect
// any changes made during parsing.
prop.value = this.style.getPropertyValue(prop.name);
prop.priority = this.style.getPropertyPriority(prop.name);
prop.updateComputed();
}
@ -519,6 +528,7 @@ Rule.prototype = {
_getTextProperties: function Rule_getTextProperties()
{
this.textProps = [];
let store = this.elementStyle.store;
let lines = this.style.cssText.match(CSS_LINE_RE);
for each (let line in lines) {
let matches = CSS_PROP_RE.exec(line);
@ -530,8 +540,8 @@ Rule.prototype = {
!this.elementStyle.domUtils.isInheritedProperty(name)) {
continue;
}
let prop = new TextProperty(this, name, matches[2], matches[3] || "");
let value = store.userProperties.getProperty(this.style, name, matches[2]);
let prop = new TextProperty(this, name, value, matches[3] || "");
this.textProps.push(prop);
}
@ -542,8 +552,8 @@ Rule.prototype = {
}
for each (let prop in disabledProps) {
let textProp = new TextProperty(this, prop.name,
prop.value, prop.priority);
let value = store.userProperties.getProperty(this.style, prop.name, prop.value);
let textProp = new TextProperty(this, prop.name, value, prop.priority);
textProp.enabled = false;
this.textProps.push(textProp);
}
@ -1381,6 +1391,61 @@ InplaceEditor.prototype = {
}
};
/**
* Store of CSSStyleDeclarations mapped to properties that have been changed by
* the user.
*/
function UserProperties()
{
this.weakMap = new WeakMap();
}
UserProperties.prototype = {
/**
* Get a named property for a given CSSStyleDeclaration.
*
* @param {CSSStyleDeclaration} aStyle
* The CSSStyleDeclaration against which the property is mapped.
* @param {String} aName
* The name of the property to get.
* @param {Boolean} aDefault
* Indicates whether the property value is one entered by a user.
* @returns {String}
* The property value if it has previously been set by the user, null
* otherwise.
*/
getProperty: function UP_getProperty(aStyle, aName, aDefault) {
let entry = this.weakMap.get(aStyle, null);
if (entry && aName in entry) {
return entry[aName];
}
return typeof aDefault != "undefined" ? aDefault : null;
},
/**
* Set a named property for a given CSSStyleDeclaration.
*
* @param {CSSStyleDeclaration} aStyle
* The CSSStyleDeclaration against which the property is to be mapped.
* @param {String} aName
* The name of the property to set.
* @param {String} aValue
* The value of the property to set.
*/
setProperty: function UP_setProperty(aStyle, aName, aValue) {
let entry = this.weakMap.get(aStyle, null);
if (entry) {
entry[aName] = aValue;
} else {
let props = {};
props[aName] = aValue;
this.weakMap.set(aStyle, props);
}
},
};
/**
* Helper functions
*/

Просмотреть файл

@ -55,6 +55,7 @@ _BROWSER_TEST_FILES = \
browser_bug_692400_element_style.js \
browser_csslogic_inherited.js \
browser_ruleview_editor.js \
browser_ruleview_editor_changedvalues.js \
browser_ruleview_inherit.js \
browser_ruleview_manipulation.js \
browser_ruleview_override.js \

Просмотреть файл

@ -0,0 +1,157 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tempScope = {};
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
let CssRuleView = tempScope.CssRuleView;
let _ElementStyle = tempScope._ElementStyle;
let _editableField = tempScope._editableField;
let doc;
let ruleDialog;
let ruleView;
function waitForEditorFocus(aParent, aCallback)
{
aParent.addEventListener("focus", function onFocus(evt) {
if (evt.target.inplaceEditor) {
aParent.removeEventListener("focus", onFocus, true);
let editor = evt.target.inplaceEditor;
executeSoon(function() {
aCallback(editor);
});
}
}, true);
}
function waitForEditorBlur(aEditor, aCallback)
{
let input = aEditor.input;
input.addEventListener("blur", function onBlur() {
input.removeEventListener("blur", onBlur, false);
executeSoon(function() {
aCallback();
});
}, false);
}
var gRuleViewChanged = false;
function ruleViewChanged()
{
gRuleViewChanged = true;
}
function expectChange()
{
ok(gRuleViewChanged, "Rule view should have fired a change event.");
gRuleViewChanged = false;
}
function startTest()
{
let style = '' +
'#testid {' +
' background-color: blue;' +
'} ' +
'.testclass {' +
' background-color: green;' +
'}';
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let testElement = doc.getElementById("testid");
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
"cssruleviewtest",
"width=200,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad, true);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.highlight(testElement);
waitForFocus(testCancelNew, ruleDialog);
}, true);
}
function testCancelNew()
{
// Start at the beginning: start to add a rule to the element's style
// declaration, but leave it empty.
let elementRuleEditor = ruleView.element.children[0]._ruleEditor;
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
is(elementRuleEditor.newPropSpan.inplaceEditor, aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
waitForEditorBlur(aEditor, function () {
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
testCreateNew();
});
aEditor.input.blur();
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
}
function testCreateNew()
{
// Create a new property.
let elementRuleEditor = ruleView.element.children[0]._ruleEditor;
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
is(elementRuleEditor.newPropSpan.inplaceEditor, aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
input.value = "background-color";
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
expectChange();
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, textProp.editor.valueSpan.inplaceEditor, "Should be editing the value span now.");
aEditor.input.value = "#XYZ";
waitForEditorBlur(aEditor, function() {
expectChange();
is(textProp.value, "#XYZ", "Text prop should have been changed.");
finishTest();
});
aEditor.input.blur();
});
EventUtils.synthesizeKey("VK_RETURN", {}, ruleDialog);
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
}
function finishTest()
{
ruleView.element.removeEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function changedValues_load(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, changedValues_load, true);
doc = content.document;
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,test rule view user changes";
}