mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 984880 - as-authored styles in the rule view; r=pbrosset,r=bgrins
This commit is contained in:
parent
a821c5000b
commit
86b4213d48
@ -1379,7 +1379,7 @@ pref("devtools.inspector.showAllAnonymousContent", false);
|
|||||||
pref("devtools.inspector.mdnDocsTooltip.enabled", true);
|
pref("devtools.inspector.mdnDocsTooltip.enabled", true);
|
||||||
|
|
||||||
// DevTools default color unit
|
// DevTools default color unit
|
||||||
pref("devtools.defaultColorUnit", "hex");
|
pref("devtools.defaultColorUnit", "authored");
|
||||||
|
|
||||||
// Enable the Responsive UI tool
|
// Enable the Responsive UI tool
|
||||||
pref("devtools.responsiveUI.no-reload-notification", false);
|
pref("devtools.responsiveUI.no-reload-notification", false);
|
||||||
|
@ -69,6 +69,10 @@ values from browser.dtd. -->
|
|||||||
- inspector. This is visible in the options panel. -->
|
- inspector. This is visible in the options panel. -->
|
||||||
<!ENTITY options.defaultColorUnit.accesskey "U">
|
<!ENTITY options.defaultColorUnit.accesskey "U">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (options.defaultColorUnit.authored): This is used in the
|
||||||
|
- 'Default color unit' dropdown list and is visible in the options panel. -->
|
||||||
|
<!ENTITY options.defaultColorUnit.authored "As Authored">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (options.defaultColorUnit.hex): This is used in the
|
<!-- LOCALIZATION NOTE (options.defaultColorUnit.hex): This is used in the
|
||||||
- 'Default color unit' dropdown list and is visible in the options panel. -->
|
- 'Default color unit' dropdown list and is visible in the options panel. -->
|
||||||
<!ENTITY options.defaultColorUnit.hex "Hex">
|
<!ENTITY options.defaultColorUnit.hex "Hex">
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
<menulist id="defaultColorUnitMenuList"
|
<menulist id="defaultColorUnitMenuList"
|
||||||
data-pref="devtools.defaultColorUnit">
|
data-pref="devtools.defaultColorUnit">
|
||||||
<menupopup>
|
<menupopup>
|
||||||
|
<menuitem label="&options.defaultColorUnit.authored;" value="authored"/>
|
||||||
<menuitem label="&options.defaultColorUnit.hex;" value="hex"/>
|
<menuitem label="&options.defaultColorUnit.hex;" value="hex"/>
|
||||||
<menuitem label="&options.defaultColorUnit.hsl;" value="hsl"/>
|
<menuitem label="&options.defaultColorUnit.hsl;" value="hsl"/>
|
||||||
<menuitem label="&options.defaultColorUnit.rgb;" value="rgb"/>
|
<menuitem label="&options.defaultColorUnit.rgb;" value="rgb"/>
|
||||||
|
@ -95,9 +95,9 @@ EditingSession.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (property.value == "") {
|
if (property.value == "") {
|
||||||
modifications.removeProperty(property.name);
|
modifications.removeProperty(-1, property.name);
|
||||||
} else {
|
} else {
|
||||||
modifications.setProperty(property.name, property.value, "");
|
modifications.setProperty(-1, property.name, property.value, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,9 +113,9 @@ EditingSession.prototype = {
|
|||||||
|
|
||||||
for (let [property, value] of this._modifications) {
|
for (let [property, value] of this._modifications) {
|
||||||
if (value != "") {
|
if (value != "") {
|
||||||
modifications.setProperty(property, value, "");
|
modifications.setProperty(-1, property, value, "");
|
||||||
} else {
|
} else {
|
||||||
modifications.removeProperty(property);
|
modifications.removeProperty(-1, property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,8 @@ var COLOR_TEST_CLASS = "test-class";
|
|||||||
// property. |value| is the CSS text to use. |segments| is an array
|
// property. |value| is the CSS text to use. |segments| is an array
|
||||||
// describing the expected result. If an element of |segments| is a
|
// describing the expected result. If an element of |segments| is a
|
||||||
// string, it is simply appended to the expected string. Otherwise,
|
// string, it is simply appended to the expected string. Otherwise,
|
||||||
// it must be an object with a |value| property and a |name| property.
|
// it must be an object with a |value| property, which is the color
|
||||||
// These describe the color and are both used in the generated
|
// name as it appears in the input.
|
||||||
// expected output -- |name| is the color name as it appears in the
|
|
||||||
// input (e.g., "red"); and |value| is the hash-style numeric value
|
|
||||||
// for the color, which parseCssProperty emits in some spots (e.g.,
|
|
||||||
// "#F00").
|
|
||||||
//
|
//
|
||||||
// This approach is taken to reduce boilerplate and to make it simpler
|
// This approach is taken to reduce boilerplate and to make it simpler
|
||||||
// to modify the test when the parseCssProperty output changes.
|
// to modify the test when the parseCssProperty output changes.
|
||||||
@ -53,10 +49,10 @@ function makeColorTest(name, value, segments) {
|
|||||||
if (typeof (segment) === "string") {
|
if (typeof (segment) === "string") {
|
||||||
result.expected += segment;
|
result.expected += segment;
|
||||||
} else {
|
} else {
|
||||||
result.expected += "<span data-color=\"" + segment.value + "\">" +
|
result.expected += "<span data-color=\"" + segment.name + "\">" +
|
||||||
"<span style=\"background-color:" + segment.name +
|
"<span style=\"background-color:" + segment.name +
|
||||||
"\" class=\"" + COLOR_TEST_CLASS + "\"></span><span>" +
|
"\" class=\"" + COLOR_TEST_CLASS + "\"></span><span>" +
|
||||||
segment.value + "</span></span>";
|
segment.name + "</span></span>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,25 +64,25 @@ function makeColorTest(name, value, segments) {
|
|||||||
function testParseCssProperty(doc, parser) {
|
function testParseCssProperty(doc, parser) {
|
||||||
let tests = [
|
let tests = [
|
||||||
makeColorTest("border", "1px solid red",
|
makeColorTest("border", "1px solid red",
|
||||||
["1px solid ", {name: "red", value: "#F00"}]),
|
["1px solid ", {name: "red"}]),
|
||||||
|
|
||||||
makeColorTest("background-image",
|
makeColorTest("background-image",
|
||||||
"linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
|
"linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
|
||||||
["linear-gradient(to right, ", {name: "#F60", value: "#F60"},
|
["linear-gradient(to right, ", {name: "#F60"},
|
||||||
" 10%, ", {name: "rgba(0,0,0,1)", value: "#000"},
|
" 10%, ", {name: "rgba(0,0,0,1)"},
|
||||||
")"]),
|
")"]),
|
||||||
|
|
||||||
// In "arial black", "black" is a font, not a color.
|
// In "arial black", "black" is a font, not a color.
|
||||||
makeColorTest("font-family", "arial black", ["arial black"]),
|
makeColorTest("font-family", "arial black", ["arial black"]),
|
||||||
|
|
||||||
makeColorTest("box-shadow", "0 0 1em red",
|
makeColorTest("box-shadow", "0 0 1em red",
|
||||||
["0 0 1em ", {name: "red", value: "#F00"}]),
|
["0 0 1em ", {name: "red"}]),
|
||||||
|
|
||||||
makeColorTest("box-shadow",
|
makeColorTest("box-shadow",
|
||||||
"0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)",
|
"0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)",
|
||||||
["0 0 1em ", {name: "red", value: "#F00"},
|
["0 0 1em ", {name: "red"},
|
||||||
", 2px 2px 0 0 ",
|
", 2px 2px 0 0 ",
|
||||||
{name: "rgba(0,0,0,.5)", value: "rgba(0,0,0,.5)"}]),
|
{name: "rgba(0,0,0,.5)"}]),
|
||||||
|
|
||||||
makeColorTest("content", "\"red\"", ["\"red\""]),
|
makeColorTest("content", "\"red\"", ["\"red\""]),
|
||||||
|
|
||||||
@ -98,7 +94,7 @@ function testParseCssProperty(doc, parser) {
|
|||||||
["<span data-filters=\"blur(1px) drop-shadow(0 0 0 blue) ",
|
["<span data-filters=\"blur(1px) drop-shadow(0 0 0 blue) ",
|
||||||
"url(red.svg#blue)\"><span>",
|
"url(red.svg#blue)\"><span>",
|
||||||
"blur(1px) drop-shadow(0 0 0 ",
|
"blur(1px) drop-shadow(0 0 0 ",
|
||||||
{name: "blue", value: "#00F"},
|
{name: "blue"},
|
||||||
") url(red.svg#blue)</span></span>"]),
|
") url(red.svg#blue)</span></span>"]),
|
||||||
|
|
||||||
makeColorTest("color", "currentColor", ["currentColor"]),
|
makeColorTest("color", "currentColor", ["currentColor"]),
|
||||||
|
@ -1145,6 +1145,8 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
|
|||||||
// Then set spectrum's color and listen to color changes to preview them
|
// Then set spectrum's color and listen to color changes to preview them
|
||||||
if (this.activeSwatch) {
|
if (this.activeSwatch) {
|
||||||
this.currentSwatchColor = this.activeSwatch.nextSibling;
|
this.currentSwatchColor = this.activeSwatch.nextSibling;
|
||||||
|
this._colorUnit =
|
||||||
|
colorUtils.classifyColor(this.currentSwatchColor.textContent);
|
||||||
let color = this.activeSwatch.style.backgroundColor;
|
let color = this.activeSwatch.style.backgroundColor;
|
||||||
this.spectrum.then(spectrum => {
|
this.spectrum.then(spectrum => {
|
||||||
spectrum.off("changed", this._onSpectrumColorChange);
|
spectrum.off("changed", this._onSpectrumColorChange);
|
||||||
@ -1222,6 +1224,7 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
|
|||||||
|
|
||||||
_toDefaultType: function(color) {
|
_toDefaultType: function(color) {
|
||||||
let colorObj = new colorUtils.CssColor(color);
|
let colorObj = new colorUtils.CssColor(color);
|
||||||
|
colorObj.colorUnit = this._colorUnit;
|
||||||
return colorObj.toString();
|
return colorObj.toString();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
|||||||
this.walker = walker;
|
this.walker = walker;
|
||||||
this.highlighter = highlighter;
|
this.highlighter = highlighter;
|
||||||
|
|
||||||
|
// True when we've called update() on the style sheet.
|
||||||
|
this._isUpdating = false;
|
||||||
|
// True when we've just set the editor text based on a style-applied
|
||||||
|
// event from the StyleSheetActor.
|
||||||
|
this._justSetText = false;
|
||||||
|
|
||||||
this._state = { // state to use when inputElement attaches
|
this._state = { // state to use when inputElement attaches
|
||||||
text: "",
|
text: "",
|
||||||
selection: {
|
selection: {
|
||||||
@ -103,7 +109,8 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
|||||||
this._onPropertyChange = this._onPropertyChange.bind(this);
|
this._onPropertyChange = this._onPropertyChange.bind(this);
|
||||||
this._onError = this._onError.bind(this);
|
this._onError = this._onError.bind(this);
|
||||||
this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
|
this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
|
||||||
this._onMediaRulesChanged = this._onMediaRulesChanged.bind(this)
|
this._onMediaRulesChanged = this._onMediaRulesChanged.bind(this);
|
||||||
|
this._onStyleApplied = this._onStyleApplied.bind(this);
|
||||||
this.checkLinkedFileForChanges = this.checkLinkedFileForChanges.bind(this);
|
this.checkLinkedFileForChanges = this.checkLinkedFileForChanges.bind(this);
|
||||||
this.markLinkedFileBroken = this.markLinkedFileBroken.bind(this);
|
this.markLinkedFileBroken = this.markLinkedFileBroken.bind(this);
|
||||||
this.saveToFile = this.saveToFile.bind(this);
|
this.saveToFile = this.saveToFile.bind(this);
|
||||||
@ -119,6 +126,7 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
|||||||
this.cssSheet.getMediaRules().then(this._onMediaRulesChanged, Cu.reportError);
|
this.cssSheet.getMediaRules().then(this._onMediaRulesChanged, Cu.reportError);
|
||||||
}
|
}
|
||||||
this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
|
this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
|
||||||
|
this.cssSheet.on("style-applied", this._onStyleApplied);
|
||||||
this.savedFile = file;
|
this.savedFile = file;
|
||||||
this.linkCSSFile();
|
this.linkCSSFile();
|
||||||
}
|
}
|
||||||
@ -244,6 +252,27 @@ StyleSheetEditor.prototype = {
|
|||||||
this.emit("linked-css-file");
|
this.emit("linked-css-file");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function that fetches the source text from the style
|
||||||
|
* sheet. The text is possibly prettified using
|
||||||
|
* CssLogic.prettifyCSS. This also sets |this._state.text| to the
|
||||||
|
* new text.
|
||||||
|
*
|
||||||
|
* @return {Promise} a promise that resolves to the new text
|
||||||
|
*/
|
||||||
|
_getSourceTextAndPrettify: function() {
|
||||||
|
return this.styleSheet.getText().then((longStr) => {
|
||||||
|
return longStr.string();
|
||||||
|
}).then((source) => {
|
||||||
|
let ruleCount = this.styleSheet.ruleCount;
|
||||||
|
if (!this.styleSheet.isOriginalSource) {
|
||||||
|
source = CssLogic.prettifyCSS(source, ruleCount);
|
||||||
|
}
|
||||||
|
this._state.text = source;
|
||||||
|
return source;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start fetching the full text source for this editor's sheet.
|
* Start fetching the full text source for this editor's sheet.
|
||||||
*
|
*
|
||||||
@ -251,19 +280,11 @@ StyleSheetEditor.prototype = {
|
|||||||
* A promise that'll resolve with the source text once the source
|
* A promise that'll resolve with the source text once the source
|
||||||
* has been loaded or reject on unexpected error.
|
* has been loaded or reject on unexpected error.
|
||||||
*/
|
*/
|
||||||
fetchSource: function () {
|
fetchSource: function() {
|
||||||
return Task.spawn(function* () {
|
return this._getSourceTextAndPrettify().then((source) => {
|
||||||
let longStr = yield this.styleSheet.getText();
|
|
||||||
let source = yield longStr.string();
|
|
||||||
let ruleCount = this.styleSheet.ruleCount;
|
|
||||||
if (!this.styleSheet.isOriginalSource) {
|
|
||||||
source = CssLogic.prettifyCSS(source, ruleCount);
|
|
||||||
}
|
|
||||||
this._state.text = source;
|
|
||||||
this.sourceLoaded = true;
|
this.sourceLoaded = true;
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
}.bind(this)).then(null, e => {
|
}).then(null, e => {
|
||||||
if (this._isDestroyed) {
|
if (this._isDestroyed) {
|
||||||
console.warn("Could not fetch the source for " +
|
console.warn("Could not fetch the source for " +
|
||||||
this.styleSheet.href +
|
this.styleSheet.href +
|
||||||
@ -323,6 +344,26 @@ StyleSheetEditor.prototype = {
|
|||||||
this.emit("property-change", property, value);
|
this.emit("property-change", property, value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the stylesheet text changes.
|
||||||
|
*/
|
||||||
|
_onStyleApplied: function() {
|
||||||
|
if (this._isUpdating) {
|
||||||
|
// We just applied an edit in the editor, so we can drop this
|
||||||
|
// notification.
|
||||||
|
this._isUpdating = false;
|
||||||
|
} else if (this.sourceEditor) {
|
||||||
|
this._getSourceTextAndPrettify().then((newText) => {
|
||||||
|
this._justSetText = true;
|
||||||
|
let firstLine = this.sourceEditor.getFirstVisibleLine();
|
||||||
|
let pos = this.sourceEditor.getCursor();
|
||||||
|
this.sourceEditor.setText(newText);
|
||||||
|
this.sourceEditor.setFirstVisibleLine(firstLine);
|
||||||
|
this.sourceEditor.setCursor(pos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles changes to the list of @media rules in the stylesheet.
|
* Handles changes to the list of @media rules in the stylesheet.
|
||||||
* Emits 'media-rules-changed' if the list has changed.
|
* Emits 'media-rules-changed' if the list has changed.
|
||||||
@ -489,6 +530,11 @@ StyleSheetEditor.prototype = {
|
|||||||
return; // TODO: do we want to do this?
|
return; // TODO: do we want to do this?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._justSetText) {
|
||||||
|
this._justSetText = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._updateTask = null; // reset only if we actually perform an update
|
this._updateTask = null; // reset only if we actually perform an update
|
||||||
// (stylesheet is enabled) so that 'missed' updates
|
// (stylesheet is enabled) so that 'missed' updates
|
||||||
// while the stylesheet is disabled can be performed
|
// while the stylesheet is disabled can be performed
|
||||||
@ -498,6 +544,7 @@ StyleSheetEditor.prototype = {
|
|||||||
this._state.text = this.sourceEditor.getText();
|
this._state.text = this.sourceEditor.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._isUpdating = true;
|
||||||
this.styleSheet.update(this._state.text, this.transitionsEnabled)
|
this.styleSheet.update(this._state.text, this.transitionsEnabled)
|
||||||
.then(null, Cu.reportError);
|
.then(null, Cu.reportError);
|
||||||
},
|
},
|
||||||
@ -726,10 +773,11 @@ StyleSheetEditor.prototype = {
|
|||||||
}
|
}
|
||||||
this.cssSheet.off("property-change", this._onPropertyChange);
|
this.cssSheet.off("property-change", this._onPropertyChange);
|
||||||
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
|
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
|
||||||
|
this.cssSheet.off("style-applied", this._onStyleApplied);
|
||||||
this.styleSheet.off("error", this._onError);
|
this.styleSheet.off("error", this._onError);
|
||||||
this._isDestroyed = true;
|
this._isDestroyed = true;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a path on disk for a file given it's hosted uri, the uri of the
|
* Find a path on disk for a file given it's hosted uri, the uri of the
|
||||||
|
@ -48,6 +48,7 @@ support-files =
|
|||||||
doc_uncached.css
|
doc_uncached.css
|
||||||
doc_uncached.html
|
doc_uncached.html
|
||||||
doc_xulpage.xul
|
doc_xulpage.xul
|
||||||
|
sync.html
|
||||||
|
|
||||||
[browser_styleeditor_autocomplete.js]
|
[browser_styleeditor_autocomplete.js]
|
||||||
[browser_styleeditor_autocomplete-disabled.js]
|
[browser_styleeditor_autocomplete-disabled.js]
|
||||||
@ -83,5 +84,10 @@ skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
|
|||||||
[browser_styleeditor_sourcemap_large.js]
|
[browser_styleeditor_sourcemap_large.js]
|
||||||
[browser_styleeditor_sourcemap_watching.js]
|
[browser_styleeditor_sourcemap_watching.js]
|
||||||
skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
|
skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
|
||||||
|
[browser_styleeditor_sync.js]
|
||||||
|
[browser_styleeditor_syncAddRule.js]
|
||||||
|
[browser_styleeditor_syncAlreadyOpen.js]
|
||||||
|
[browser_styleeditor_syncEditSelector.js]
|
||||||
|
[browser_styleeditor_syncIntoRuleView.js]
|
||||||
[browser_styleeditor_transition_rule.js]
|
[browser_styleeditor_transition_rule.js]
|
||||||
[browser_styleeditor_xul.js]
|
[browser_styleeditor_xul.js]
|
||||||
|
74
devtools/client/styleeditor/test/browser_styleeditor_sync.js
Normal file
74
devtools/client/styleeditor/test/browser_styleeditor_sync.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/* 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 changes in the style inspector are synchronized into the
|
||||||
|
// style editor.
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleinspector/test/head.js", this);
|
||||||
|
|
||||||
|
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||||
|
|
||||||
|
const expectedText = `
|
||||||
|
body {
|
||||||
|
border-width: 15px;
|
||||||
|
/*! color: red; */
|
||||||
|
}
|
||||||
|
|
||||||
|
#testid {
|
||||||
|
/*! font-size: 4em; */
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function* closeAndReopenToolbox() {
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
yield gDevTools.closeToolbox(target);
|
||||||
|
let { ui: newui } = yield openStyleEditor();
|
||||||
|
return newui;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab(TESTCASE_URI);
|
||||||
|
let { inspector, view } = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
|
||||||
|
// Disable the "font-size" property.
|
||||||
|
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||||
|
let onModification = view.once("ruleview-changed");
|
||||||
|
propEditor.enable.click();
|
||||||
|
yield onModification;
|
||||||
|
|
||||||
|
// Disable the "color" property. Note that this property is in a
|
||||||
|
// rule that also contains a non-inherited property -- so this test
|
||||||
|
// is also testing that property editing works properly in this
|
||||||
|
// situation.
|
||||||
|
ruleEditor = getRuleViewRuleEditor(view, 3);
|
||||||
|
propEditor = ruleEditor.rule.textProps[1].editor;
|
||||||
|
onModification = view.once("ruleview-changed");
|
||||||
|
propEditor.enable.click();
|
||||||
|
yield onModification;
|
||||||
|
|
||||||
|
let { ui } = yield openStyleEditor();
|
||||||
|
|
||||||
|
let editor = yield ui.editors[0].getSourceEditor();
|
||||||
|
let text = editor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "style inspector changes are synced");
|
||||||
|
|
||||||
|
// Close and reopen the toolbox, to see that the edited text remains
|
||||||
|
// available.
|
||||||
|
ui = yield closeAndReopenToolbox();
|
||||||
|
editor = yield ui.editors[0].getSourceEditor();
|
||||||
|
text = editor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "changes remain after close and reopen");
|
||||||
|
|
||||||
|
// For the time being, the actor does not update the style's owning
|
||||||
|
// node's textContent. See bug 1205380.
|
||||||
|
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
|
||||||
|
let style = content.document.querySelector("style");
|
||||||
|
return style.textContent;
|
||||||
|
}).then((textContent) => {
|
||||||
|
isnot(textContent, expectedText, "changes not written back to style node");
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
/* 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.
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleinspector/test/head.js", this);
|
||||||
|
|
||||||
|
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||||
|
|
||||||
|
const expectedText = `
|
||||||
|
#testid {
|
||||||
|
}`;
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab(TESTCASE_URI);
|
||||||
|
let { inspector, view } = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
|
||||||
|
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||||
|
view.addRuleButton.click();
|
||||||
|
yield onRuleViewChanged;
|
||||||
|
|
||||||
|
let { ui } = yield openStyleEditor();
|
||||||
|
|
||||||
|
info("Selecting the second editor");
|
||||||
|
yield ui.selectStyleSheet(ui.editors[1].styleSheet);
|
||||||
|
|
||||||
|
let editor = ui.editors[1];
|
||||||
|
let text = editor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "selector edits are synced");
|
||||||
|
});
|
@ -0,0 +1,52 @@
|
|||||||
|
/* 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 changes in the style inspector are synchronized into the
|
||||||
|
// style editor.
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleinspector/test/head.js", this);
|
||||||
|
|
||||||
|
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||||
|
|
||||||
|
const expectedText = `
|
||||||
|
body {
|
||||||
|
border-width: 15px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#testid {
|
||||||
|
/*! font-size: 4em; */
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab(TESTCASE_URI);
|
||||||
|
|
||||||
|
let { inspector, view } = yield openRuleView();
|
||||||
|
|
||||||
|
// In this test, make sure the style editor is open before making
|
||||||
|
// changes in the inspector.
|
||||||
|
let { ui } = yield openStyleEditor();
|
||||||
|
let editor = yield ui.editors[0].getSourceEditor();
|
||||||
|
|
||||||
|
let onEditorChange = promise.defer();
|
||||||
|
editor.sourceEditor.on("change", onEditorChange.resolve);
|
||||||
|
|
||||||
|
yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
|
||||||
|
// Disable the "font-size" property.
|
||||||
|
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||||
|
let onModification = view.once("ruleview-changed");
|
||||||
|
propEditor.enable.click();
|
||||||
|
yield onModification;
|
||||||
|
|
||||||
|
yield openStyleEditor();
|
||||||
|
yield onEditorChange.promise;
|
||||||
|
|
||||||
|
let text = editor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "style inspector changes are synced");
|
||||||
|
});
|
@ -0,0 +1,41 @@
|
|||||||
|
/* 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 changes in the style inspector are synchronized into the
|
||||||
|
// style editor.
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleinspector/test/head.js", this);
|
||||||
|
|
||||||
|
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||||
|
|
||||||
|
const expectedText = `
|
||||||
|
body {
|
||||||
|
border-width: 15px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#testid, span {
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab(TESTCASE_URI);
|
||||||
|
let { inspector, view } = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
|
||||||
|
let editor = yield focusEditableField(view, ruleEditor.selectorText);
|
||||||
|
editor.input.value = "#testid, span";
|
||||||
|
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||||
|
yield onRuleViewChanged;
|
||||||
|
|
||||||
|
let { ui } = yield openStyleEditor();
|
||||||
|
|
||||||
|
editor = yield ui.editors[0].getSourceEditor();
|
||||||
|
let text = editor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "selector edits are synced");
|
||||||
|
});
|
@ -0,0 +1,51 @@
|
|||||||
|
/* 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 changes in the style editor are synchronized into the
|
||||||
|
// style inspector.
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleinspector/test/head.js", this);
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
div { background-color: seagreen; }
|
||||||
|
</style>
|
||||||
|
<div id='testid' class='testclass'>Styled Node</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TESTCASE_CSS_SOURCE = "#testid { color: chartreuse; }";
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
|
||||||
|
let {inspector, view} = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
|
||||||
|
let { panel, ui } = yield openStyleEditor();
|
||||||
|
|
||||||
|
let editor = yield ui.editors[0].getSourceEditor();
|
||||||
|
|
||||||
|
let waitForRuleView = view.once("ruleview-refreshed");
|
||||||
|
yield typeInEditor(editor, panel.panelWindow);
|
||||||
|
yield waitForRuleView;
|
||||||
|
|
||||||
|
let value = getRuleViewPropertyValue(view, "#testid", "color");
|
||||||
|
is(value, "chartreuse", "check that edits were synced to rule view");
|
||||||
|
});
|
||||||
|
|
||||||
|
function typeInEditor(aEditor, panelWindow) {
|
||||||
|
let deferred = promise.defer();
|
||||||
|
|
||||||
|
waitForFocus(function() {
|
||||||
|
for (let c of TESTCASE_CSS_SOURCE) {
|
||||||
|
EventUtils.synthesizeKey(c, {}, panelWindow);
|
||||||
|
}
|
||||||
|
ok(aEditor.unsaved, "new editor has unsaved flag");
|
||||||
|
|
||||||
|
deferred.resolve();
|
||||||
|
}, panelWindow);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
@ -71,17 +71,29 @@ function* cleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new tab in specified window navigates it to the given URL and
|
* Open the style editor for the current tab.
|
||||||
* opens style editor in it.
|
|
||||||
*/
|
*/
|
||||||
var openStyleEditorForURL = Task.async(function* (url, win) {
|
var openStyleEditor = Task.async(function*(tab) {
|
||||||
let tab = yield addTab(url, win);
|
if (!tab) {
|
||||||
|
tab = gBrowser.selectedTab;
|
||||||
|
}
|
||||||
let target = TargetFactory.forTab(tab);
|
let target = TargetFactory.forTab(tab);
|
||||||
let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
|
let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
|
||||||
let panel = toolbox.getPanel("styleeditor");
|
let panel = toolbox.getPanel("styleeditor");
|
||||||
let ui = panel.UI;
|
let ui = panel.UI;
|
||||||
|
|
||||||
return { tab, toolbox, panel, ui };
|
return { toolbox, panel, ui };
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tab in specified window navigates it to the given URL and
|
||||||
|
* opens style editor in it.
|
||||||
|
*/
|
||||||
|
var openStyleEditorForURL = Task.async(function* (url, win) {
|
||||||
|
let tab = yield addTab(url, win);
|
||||||
|
let result = yield openStyleEditor(tab);
|
||||||
|
result.tab = tab;
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
20
devtools/client/styleeditor/test/sync.html
Normal file
20
devtools/client/styleeditor/test/sync.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>simple testcase</title>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
border-width: 15px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#testid {
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="testid">simple testcase</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -109,7 +109,7 @@ function createDummyDocument() {
|
|||||||
* Responsible for applying changes to the properties in a rule.
|
* Responsible for applying changes to the properties in a rule.
|
||||||
* Maintains a list of TextProperty objects.
|
* Maintains a list of TextProperty objects.
|
||||||
* TextProperty:
|
* TextProperty:
|
||||||
* Manages a single property from the cssText attribute of the
|
* Manages a single property from the authoredText attribute of the
|
||||||
* relevant declaration.
|
* relevant declaration.
|
||||||
* Maintains a list of computed properties that come from this
|
* Maintains a list of computed properties that come from this
|
||||||
* property declaration.
|
* property declaration.
|
||||||
@ -183,6 +183,12 @@ ElementStyle.prototype = {
|
|||||||
}
|
}
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
|
|
||||||
|
for (let rule of this.rules) {
|
||||||
|
if (rule.editor) {
|
||||||
|
rule.editor.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.dummyElement = null;
|
this.dummyElement = null;
|
||||||
this.dummyElementPromise.then(dummyElement => {
|
this.dummyElementPromise.then(dummyElement => {
|
||||||
dummyElement.remove();
|
dummyElement.remove();
|
||||||
@ -226,12 +232,12 @@ ElementStyle.prototype = {
|
|||||||
|
|
||||||
// Store the current list of rules (if any) during the population
|
// Store the current list of rules (if any) during the population
|
||||||
// process. They will be reused if possible.
|
// process. They will be reused if possible.
|
||||||
this._refreshRules = this.rules;
|
let existingRules = this.rules;
|
||||||
|
|
||||||
this.rules = [];
|
this.rules = [];
|
||||||
|
|
||||||
for (let entry of entries) {
|
for (let entry of entries) {
|
||||||
this._maybeAddRule(entry);
|
this._maybeAddRule(entry, existingRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark overridden computed styles.
|
// Mark overridden computed styles.
|
||||||
@ -240,7 +246,11 @@ ElementStyle.prototype = {
|
|||||||
this._sortRulesForPseudoElement();
|
this._sortRulesForPseudoElement();
|
||||||
|
|
||||||
// We're done with the previous list of rules.
|
// We're done with the previous list of rules.
|
||||||
delete this._refreshRules;
|
for (let r of existingRules) {
|
||||||
|
if (r && r.editor) {
|
||||||
|
r.editor.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}).then(null, e => {
|
}).then(null, e => {
|
||||||
// populate is often called after a setTimeout,
|
// populate is often called after a setTimeout,
|
||||||
@ -269,9 +279,12 @@ ElementStyle.prototype = {
|
|||||||
*
|
*
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* Options for creating the Rule, see the Rule constructor.
|
* Options for creating the Rule, see the Rule constructor.
|
||||||
|
* @param {Array} existingRules
|
||||||
|
* Rules to reuse if possible. If a rule is reused, then it
|
||||||
|
* it will be deleted from this array.
|
||||||
* @return {Boolean} true if we added the rule.
|
* @return {Boolean} true if we added the rule.
|
||||||
*/
|
*/
|
||||||
_maybeAddRule: function(options) {
|
_maybeAddRule: function(options, existingRules) {
|
||||||
// If we've already included this domRule (for example, when a
|
// If we've already included this domRule (for example, when a
|
||||||
// common selector is inherited), ignore it.
|
// common selector is inherited), ignore it.
|
||||||
if (options.rule &&
|
if (options.rule &&
|
||||||
@ -287,13 +300,12 @@ ElementStyle.prototype = {
|
|||||||
|
|
||||||
// If we're refreshing and the rule previously existed, reuse the
|
// If we're refreshing and the rule previously existed, reuse the
|
||||||
// Rule object.
|
// Rule object.
|
||||||
if (this._refreshRules) {
|
if (existingRules) {
|
||||||
for (let r of this._refreshRules) {
|
let ruleIndex = existingRules.findIndex((r) => r.matches(options));
|
||||||
if (r.matches(options)) {
|
if (ruleIndex >= 0) {
|
||||||
rule = r;
|
rule = existingRules[ruleIndex];
|
||||||
rule.refresh(options);
|
rule.refresh(options);
|
||||||
break;
|
existingRules.splice(ruleIndex, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,8 +314,8 @@ ElementStyle.prototype = {
|
|||||||
rule = new Rule(this, options);
|
rule = new Rule(this, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore inherited rules with no properties.
|
// Ignore inherited rules with no visible properties.
|
||||||
if (options.inherited && rule.textProps.length === 0) {
|
if (options.inherited && !rule.hasAnyVisibleProperties()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +384,16 @@ ElementStyle.prototype = {
|
|||||||
let taken = {};
|
let taken = {};
|
||||||
for (let computedProp of computedProps) {
|
for (let computedProp of computedProps) {
|
||||||
let earlier = taken[computedProp.name];
|
let earlier = taken[computedProp.name];
|
||||||
|
|
||||||
|
// Prevent -webkit-gradient from being selected after unchecking
|
||||||
|
// linear-gradient in this case:
|
||||||
|
// -moz-linear-gradient: ...;
|
||||||
|
// -webkit-linear-gradient: ...;
|
||||||
|
// linear-gradient: ...;
|
||||||
|
if (!computedProp.textProp.isValid()) {
|
||||||
|
computedProp.overridden = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let overridden;
|
let overridden;
|
||||||
if (earlier &&
|
if (earlier &&
|
||||||
computedProp.priority === "important" &&
|
computedProp.priority === "important" &&
|
||||||
@ -463,7 +485,7 @@ function Rule(elementStyle, options) {
|
|||||||
this.mediaText = this.domRule.mediaText;
|
this.mediaText = this.domRule.mediaText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate the text properties with the style's current cssText
|
// Populate the text properties with the style's current authoredText
|
||||||
// value, and add in any disabled properties from the store.
|
// value, and add in any disabled properties from the store.
|
||||||
this.textProps = this._getTextProperties();
|
this.textProps = this._getTextProperties();
|
||||||
this.textProps = this.textProps.concat(this._getDisabledProperties());
|
this.textProps = this.textProps.concat(this._getDisabledProperties());
|
||||||
@ -473,17 +495,12 @@ Rule.prototype = {
|
|||||||
mediaText: "",
|
mediaText: "",
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
if (this._title) {
|
let title = CssLogic.shortSource(this.sheet);
|
||||||
return this._title;
|
|
||||||
}
|
|
||||||
this._title = CssLogic.shortSource(this.sheet);
|
|
||||||
if (this.domRule.type !== ELEMENT_STYLE && this.ruleLine > 0) {
|
if (this.domRule.type !== ELEMENT_STYLE && this.ruleLine > 0) {
|
||||||
this._title += ":" + this.ruleLine;
|
title += ":" + this.ruleLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._title = this._title +
|
return title + (this.mediaText ? " @media " + this.mediaText : "");
|
||||||
(this.mediaText ? " @media " + this.mediaText : "");
|
|
||||||
return this._title;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get inheritedSource() {
|
get inheritedSource() {
|
||||||
@ -551,10 +568,6 @@ Rule.prototype = {
|
|||||||
* both the full and short version of the source string.
|
* both the full and short version of the source string.
|
||||||
*/
|
*/
|
||||||
getOriginalSourceStrings: function() {
|
getOriginalSourceStrings: function() {
|
||||||
if (this._originalSourceStrings) {
|
|
||||||
return promise.resolve(this._originalSourceStrings);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.domRule.getOriginalLocation().then(({href, line, mediaText}) => {
|
return this.domRule.getOriginalLocation().then(({href, line, mediaText}) => {
|
||||||
let mediaString = mediaText ? " @" + mediaText : "";
|
let mediaString = mediaText ? " @" + mediaText : "";
|
||||||
|
|
||||||
@ -564,7 +577,6 @@ Rule.prototype = {
|
|||||||
short: CssLogic.shortSource({href: href}) + ":" + line + mediaString
|
short: CssLogic.shortSource({href: href}) + ":" + line + mediaString
|
||||||
};
|
};
|
||||||
|
|
||||||
this._originalSourceStrings = sourceStrings;
|
|
||||||
return sourceStrings;
|
return sourceStrings;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -595,31 +607,35 @@ Rule.prototype = {
|
|||||||
createProperty: function(name, value, priority, siblingProp) {
|
createProperty: function(name, value, priority, siblingProp) {
|
||||||
let prop = new TextProperty(this, name, value, priority);
|
let prop = new TextProperty(this, name, value, priority);
|
||||||
|
|
||||||
|
let ind;
|
||||||
if (siblingProp) {
|
if (siblingProp) {
|
||||||
let ind = this.textProps.indexOf(siblingProp);
|
ind = this.textProps.indexOf(siblingProp) + 1;
|
||||||
this.textProps.splice(ind + 1, 0, prop);
|
this.textProps.splice(ind, 0, prop);
|
||||||
} else {
|
} else {
|
||||||
|
ind = this.textProps.length;
|
||||||
this.textProps.push(prop);
|
this.textProps.push(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.applyProperties();
|
this.applyProperties((modifications) => {
|
||||||
|
modifications.createProperty(ind, name, value, priority);
|
||||||
|
});
|
||||||
return prop;
|
return prop;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reapply all the properties in this rule, and update their
|
* Helper function for applyProperties that is called when the actor
|
||||||
* computed styles. Store disabled properties in the element
|
* does not support as-authored styles. Store disabled properties
|
||||||
* style's store. Will re-mark overridden properties.
|
* in the element style's store.
|
||||||
*/
|
*/
|
||||||
applyProperties: function(modifications) {
|
_applyPropertiesNoAuthored: function(modifications) {
|
||||||
this.elementStyle.markOverriddenAll();
|
this.elementStyle.markOverriddenAll();
|
||||||
|
|
||||||
if (!modifications) {
|
|
||||||
modifications = this.style.startModifyingProperties();
|
|
||||||
}
|
|
||||||
let disabledProps = [];
|
let disabledProps = [];
|
||||||
|
|
||||||
for (let prop of this.textProps) {
|
for (let prop of this.textProps) {
|
||||||
|
if (prop.invisible) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!prop.enabled) {
|
if (!prop.enabled) {
|
||||||
disabledProps.push({
|
disabledProps.push({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
@ -632,7 +648,7 @@ Rule.prototype = {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifications.setProperty(prop.name, prop.value, prop.priority);
|
modifications.setProperty(-1, prop.name, prop.value, prop.priority);
|
||||||
|
|
||||||
prop.updateComputed();
|
prop.updateComputed();
|
||||||
}
|
}
|
||||||
@ -645,9 +661,9 @@ Rule.prototype = {
|
|||||||
disabled.delete(this.style);
|
disabled.delete(this.style);
|
||||||
}
|
}
|
||||||
|
|
||||||
let modificationsPromise = modifications.apply().then(() => {
|
return modifications.apply().then(() => {
|
||||||
let cssProps = {};
|
let cssProps = {};
|
||||||
for (let cssProp of parseDeclarations(this.style.cssText)) {
|
for (let cssProp of parseDeclarations(this.style.authoredText)) {
|
||||||
cssProps[cssProp.name] = cssProp;
|
cssProps[cssProp.name] = cssProp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,18 +683,67 @@ Rule.prototype = {
|
|||||||
|
|
||||||
textProp.priority = cssProp.priority;
|
textProp.priority = cssProp.priority;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
this.elementStyle.markOverriddenAll();
|
/**
|
||||||
|
* A helper for applyProperties that applies properties in the "as
|
||||||
if (modificationsPromise === this._applyingModifications) {
|
* authored" case; that is, when the StyleRuleActor supports
|
||||||
this._applyingModifications = null;
|
* setRuleText.
|
||||||
|
*/
|
||||||
|
_applyPropertiesAuthored: function(modifications) {
|
||||||
|
return modifications.apply().then(() => {
|
||||||
|
// The rewriting may have required some other property values to
|
||||||
|
// change, e.g., to insert some needed terminators. Update the
|
||||||
|
// relevant properties here.
|
||||||
|
for (let index in modifications.changedDeclarations) {
|
||||||
|
let newValue = modifications.changedDeclarations[index];
|
||||||
|
this.textProps[index].noticeNewValue(newValue);
|
||||||
}
|
}
|
||||||
|
// Recompute and redisplay the computed properties.
|
||||||
|
for (let prop of this.textProps) {
|
||||||
|
if (!prop.invisible && prop.enabled) {
|
||||||
|
prop.updateComputed();
|
||||||
|
prop.updateEditor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
this.elementStyle._changed();
|
/**
|
||||||
}).then(null, promiseWarn);
|
* Reapply all the properties in this rule, and update their
|
||||||
|
* computed styles. Will re-mark overridden properties. Sets the
|
||||||
|
* |_applyingModifications| property to a promise which will resolve
|
||||||
|
* when the edit has completed.
|
||||||
|
*
|
||||||
|
* @param {Function} modifier a function that takes a RuleModificationList
|
||||||
|
* (or RuleRewriter) as an argument and that modifies it
|
||||||
|
* to apply the desired edit
|
||||||
|
* @return {Promise} a promise which will resolve when the edit
|
||||||
|
* is complete
|
||||||
|
*/
|
||||||
|
applyProperties: function(modifier) {
|
||||||
|
// If there is already a pending modification, we have to wait
|
||||||
|
// until it settles before applying the next modification.
|
||||||
|
let resultPromise =
|
||||||
|
promise.resolve(this._applyingModifications).then(() => {
|
||||||
|
let modifications = this.style.startModifyingProperties();
|
||||||
|
modifier(modifications);
|
||||||
|
if (this.style.canSetRuleText) {
|
||||||
|
return this._applyPropertiesAuthored(modifications);
|
||||||
|
}
|
||||||
|
return this._applyPropertiesNoAuthored(modifications);
|
||||||
|
}).then(() => {
|
||||||
|
this.elementStyle.markOverriddenAll();
|
||||||
|
|
||||||
this._applyingModifications = modificationsPromise;
|
if (resultPromise === this._applyingModifications) {
|
||||||
return modificationsPromise;
|
this._applyingModifications = null;
|
||||||
|
this.elementStyle._changed();
|
||||||
|
}
|
||||||
|
}).catch(promiseWarn);
|
||||||
|
|
||||||
|
this._applyingModifications = resultPromise;
|
||||||
|
return resultPromise;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -693,10 +758,12 @@ Rule.prototype = {
|
|||||||
if (name === property.name) {
|
if (name === property.name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let modifications = this.style.startModifyingProperties();
|
|
||||||
modifications.removeProperty(property.name);
|
|
||||||
property.name = name;
|
property.name = name;
|
||||||
this.applyProperties(modifications, name);
|
let index = this.textProps.indexOf(property);
|
||||||
|
this.applyProperties((modifications) => {
|
||||||
|
modifications.renameProperty(index, property.name, name);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -716,7 +783,11 @@ Rule.prototype = {
|
|||||||
|
|
||||||
property.value = value;
|
property.value = value;
|
||||||
property.priority = priority;
|
property.priority = priority;
|
||||||
this.applyProperties(null, property.name);
|
|
||||||
|
let index = this.textProps.indexOf(property);
|
||||||
|
this.applyProperties((modifications) => {
|
||||||
|
modifications.setProperty(index, property.name, value, priority);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -732,7 +803,8 @@ Rule.prototype = {
|
|||||||
*/
|
*/
|
||||||
previewPropertyValue: function(property, value, priority) {
|
previewPropertyValue: function(property, value, priority) {
|
||||||
let modifications = this.style.startModifyingProperties();
|
let modifications = this.style.startModifyingProperties();
|
||||||
modifications.setProperty(property.name, value, priority);
|
modifications.setProperty(this.textProps.indexOf(property),
|
||||||
|
property.name, value, priority);
|
||||||
modifications.apply().then(() => {
|
modifications.apply().then(() => {
|
||||||
// Ensure dispatching a ruleview-changed event
|
// Ensure dispatching a ruleview-changed event
|
||||||
// also for previews
|
// also for previews
|
||||||
@ -748,12 +820,14 @@ Rule.prototype = {
|
|||||||
* @param {Boolean} value
|
* @param {Boolean} value
|
||||||
*/
|
*/
|
||||||
setPropertyEnabled: function(property, value) {
|
setPropertyEnabled: function(property, value) {
|
||||||
property.enabled = !!value;
|
if (property.enabled === !!value) {
|
||||||
let modifications = this.style.startModifyingProperties();
|
return;
|
||||||
if (!property.enabled) {
|
|
||||||
modifications.removeProperty(property.name);
|
|
||||||
}
|
}
|
||||||
this.applyProperties(modifications);
|
property.enabled = !!value;
|
||||||
|
let index = this.textProps.indexOf(property);
|
||||||
|
this.applyProperties((modifications) => {
|
||||||
|
modifications.setPropertyEnabled(index, property.name, property.enabled);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -764,30 +838,35 @@ Rule.prototype = {
|
|||||||
* The property to be removed
|
* The property to be removed
|
||||||
*/
|
*/
|
||||||
removeProperty: function(property) {
|
removeProperty: function(property) {
|
||||||
this.textProps = this.textProps.filter(prop => prop !== property);
|
let index = this.textProps.indexOf(property);
|
||||||
let modifications = this.style.startModifyingProperties();
|
this.textProps.splice(index, 1);
|
||||||
modifications.removeProperty(property.name);
|
|
||||||
// Need to re-apply properties in case removing this TextProperty
|
// Need to re-apply properties in case removing this TextProperty
|
||||||
// exposes another one.
|
// exposes another one.
|
||||||
this.applyProperties(modifications);
|
this.applyProperties((modifications) => {
|
||||||
|
modifications.removeProperty(index, property.name);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of TextProperties from the style. Needs
|
* Get the list of TextProperties from the style. Needs
|
||||||
* to parse the style's cssText.
|
* to parse the style's authoredText.
|
||||||
*/
|
*/
|
||||||
_getTextProperties: function() {
|
_getTextProperties: function() {
|
||||||
let textProps = [];
|
let textProps = [];
|
||||||
let store = this.elementStyle.store;
|
let store = this.elementStyle.store;
|
||||||
let props = parseDeclarations(this.style.cssText);
|
let props = parseDeclarations(this.style.authoredText, true);
|
||||||
for (let prop of props) {
|
for (let prop of props) {
|
||||||
let name = prop.name;
|
let name = prop.name;
|
||||||
if (this.inherited && !domUtils.isInheritedProperty(name)) {
|
// In an inherited rule, we only show inherited properties.
|
||||||
continue;
|
// However, we must keep all properties in order for rule
|
||||||
}
|
// rewriting to work properly. So, compute the "invisible"
|
||||||
|
// property here.
|
||||||
|
let invisible = this.inherited && !domUtils.isInheritedProperty(name);
|
||||||
let value = store.userProperties.getProperty(this.style, name,
|
let value = store.userProperties.getProperty(this.style, name,
|
||||||
prop.value);
|
prop.value);
|
||||||
let textProp = new TextProperty(this, name, value, prop.priority);
|
let textProp = new TextProperty(this, name, value, prop.priority,
|
||||||
|
!("commentOffsets" in prop),
|
||||||
|
invisible);
|
||||||
textProps.push(textProp);
|
textProps.push(textProp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -864,7 +943,7 @@ Rule.prototype = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current TextProperties that match a given property
|
* Update the current TextProperties that match a given property
|
||||||
* from the cssText. Will choose one existing TextProperty to update
|
* from the authoredText. Will choose one existing TextProperty to update
|
||||||
* with the new property's value, and will disable all others.
|
* with the new property's value, and will disable all others.
|
||||||
*
|
*
|
||||||
* When choosing the best match to reuse, properties will be chosen
|
* When choosing the best match to reuse, properties will be chosen
|
||||||
@ -880,7 +959,7 @@ Rule.prototype = {
|
|||||||
*
|
*
|
||||||
* @param {TextProperty} newProp
|
* @param {TextProperty} newProp
|
||||||
* The current version of the property, as parsed from the
|
* The current version of the property, as parsed from the
|
||||||
* cssText in Rule._getTextProperties().
|
* authoredText in Rule._getTextProperties().
|
||||||
* @return {Boolean} true if a property was updated, false if no properties
|
* @return {Boolean} true if a property was updated, false if no properties
|
||||||
* were updated.
|
* were updated.
|
||||||
*/
|
*/
|
||||||
@ -953,18 +1032,26 @@ Rule.prototype = {
|
|||||||
let index = this.textProps.indexOf(textProperty);
|
let index = this.textProps.indexOf(textProperty);
|
||||||
|
|
||||||
if (direction === Ci.nsIFocusManager.MOVEFOCUS_FORWARD) {
|
if (direction === Ci.nsIFocusManager.MOVEFOCUS_FORWARD) {
|
||||||
if (index === this.textProps.length - 1) {
|
for (++index; index < this.textProps.length; ++index) {
|
||||||
|
if (!this.textProps[index].invisible) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index === this.textProps.length) {
|
||||||
textProperty.rule.editor.closeBrace.click();
|
textProperty.rule.editor.closeBrace.click();
|
||||||
} else {
|
} else {
|
||||||
let nextProp = this.textProps[index + 1];
|
this.textProps[index].editor.nameSpan.click();
|
||||||
nextProp.editor.nameSpan.click();
|
|
||||||
}
|
}
|
||||||
} else if (direction === Ci.nsIFocusManager.MOVEFOCUS_BACKWARD) {
|
} else if (direction === Ci.nsIFocusManager.MOVEFOCUS_BACKWARD) {
|
||||||
if (index === 0) {
|
for (--index; index >= 0; --index) {
|
||||||
|
if (!this.textProps[index].invisible) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index < 0) {
|
||||||
textProperty.editor.ruleEditor.selectorText.click();
|
textProperty.editor.ruleEditor.selectorText.click();
|
||||||
} else {
|
} else {
|
||||||
let prevProp = this.textProps[index - 1];
|
this.textProps[index].editor.valueSpan.click();
|
||||||
prevProp.editor.valueSpan.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -978,15 +1065,31 @@ Rule.prototype = {
|
|||||||
let terminator = osString === "WINNT" ? "\r\n" : "\n";
|
let terminator = osString === "WINNT" ? "\r\n" : "\n";
|
||||||
|
|
||||||
for (let textProp of this.textProps) {
|
for (let textProp of this.textProps) {
|
||||||
cssText += "\t" + textProp.stringifyProperty() + terminator;
|
if (!textProp.invisible) {
|
||||||
|
cssText += "\t" + textProp.stringifyProperty() + terminator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectorText + " {" + terminator + cssText + "}";
|
return selectorText + " {" + terminator + cssText + "}";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See whether this rule has any non-invisible properties.
|
||||||
|
* @return {Boolean} true if there is any visible property, or false
|
||||||
|
* if all properties are invisible
|
||||||
|
*/
|
||||||
|
hasAnyVisibleProperties: function() {
|
||||||
|
for (let prop of this.textProps) {
|
||||||
|
if (!prop.invisible) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single property in a rule's cssText.
|
* A single property in a rule's authoredText.
|
||||||
*
|
*
|
||||||
* @param {Rule} rule
|
* @param {Rule} rule
|
||||||
* The rule this TextProperty came from.
|
* The rule this TextProperty came from.
|
||||||
@ -996,13 +1099,22 @@ Rule.prototype = {
|
|||||||
* The property's value (not including priority).
|
* The property's value (not including priority).
|
||||||
* @param {String} priority
|
* @param {String} priority
|
||||||
* The property's priority (either "important" or an empty string).
|
* The property's priority (either "important" or an empty string).
|
||||||
|
* @param {Boolean} enabled
|
||||||
|
* Whether the property is enabled.
|
||||||
|
* @param {Boolean} invisible
|
||||||
|
* Whether the property is invisible. An invisible property
|
||||||
|
* does not show up in the UI; these are needed so that the
|
||||||
|
* index of a property in Rule.textProps is the same as the index
|
||||||
|
* coming from parseDeclarations.
|
||||||
*/
|
*/
|
||||||
function TextProperty(rule, name, value, priority) {
|
function TextProperty(rule, name, value, priority, enabled = true,
|
||||||
|
invisible = false) {
|
||||||
this.rule = rule;
|
this.rule = rule;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.enabled = true;
|
this.enabled = !!enabled;
|
||||||
|
this.invisible = invisible;
|
||||||
this.updateComputed();
|
this.updateComputed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1087,6 +1199,17 @@ TextProperty.prototype = {
|
|||||||
this.updateEditor();
|
this.updateEditor();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the property's value has been updated externally, and
|
||||||
|
* the property and editor should update.
|
||||||
|
*/
|
||||||
|
noticeNewValue: function(value) {
|
||||||
|
if (value !== this.value) {
|
||||||
|
this.value = value;
|
||||||
|
this.updateEditor();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setName: function(name) {
|
setName: function(name) {
|
||||||
let store = this.rule.elementStyle.store;
|
let store = this.rule.elementStyle.store;
|
||||||
|
|
||||||
@ -1122,6 +1245,33 @@ TextProperty.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return declaration;
|
return declaration;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See whether this property's name is known.
|
||||||
|
*
|
||||||
|
* @return {Boolean} true if the property name is known, false otherwise.
|
||||||
|
*/
|
||||||
|
isKnownProperty: function() {
|
||||||
|
try {
|
||||||
|
// If the property name is invalid, the cssPropertyIsShorthand
|
||||||
|
// will throw an exception. But if it is valid, no exception will
|
||||||
|
// be thrown; so we just ignore the return value.
|
||||||
|
domUtils.cssPropertyIsShorthand(this.name);
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* @return {Boolean} true if the property value is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
isValid: function() {
|
||||||
|
return domUtils.cssPropertyIsValid(this.name, this.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1485,19 +1635,15 @@ CssRuleView.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new rule to the current element.
|
* A helper for _onAddRule that handles the case where the actor
|
||||||
|
* does not support as-authored styles.
|
||||||
*/
|
*/
|
||||||
_onAddRule: function() {
|
_onAddNewRuleNonAuthored: function() {
|
||||||
let elementStyle = this._elementStyle;
|
let elementStyle = this._elementStyle;
|
||||||
let element = elementStyle.element;
|
let element = elementStyle.element;
|
||||||
let rules = elementStyle.rules;
|
let rules = elementStyle.rules;
|
||||||
let client = this.inspector.toolbox._target.client;
|
|
||||||
let pseudoClasses = element.pseudoClassLocks;
|
let pseudoClasses = element.pseudoClassLocks;
|
||||||
|
|
||||||
if (!client.traits.addNewRule) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pageStyle.addNewRule(element, pseudoClasses).then(options => {
|
this.pageStyle.addNewRule(element, pseudoClasses).then(options => {
|
||||||
let newRule = new Rule(elementStyle, options);
|
let newRule = new Rule(elementStyle, options);
|
||||||
rules.push(newRule);
|
rules.push(newRule);
|
||||||
@ -1523,6 +1669,44 @@ CssRuleView.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new rule to the current element.
|
||||||
|
*/
|
||||||
|
_onAddRule: function() {
|
||||||
|
let elementStyle = this._elementStyle;
|
||||||
|
let element = elementStyle.element;
|
||||||
|
let client = this.inspector.toolbox._target.client;
|
||||||
|
let pseudoClasses = element.pseudoClassLocks;
|
||||||
|
|
||||||
|
if (!client.traits.addNewRule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.pageStyle.supportsAuthoredStyles) {
|
||||||
|
// We're talking to an old server.
|
||||||
|
this._onAddNewRuleNonAuthored();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a new rule with authored styles will cause the actor to
|
||||||
|
// emit an event, which will in turn cause the rule view to be
|
||||||
|
// updated. So, we wait for this update and for the rule creation
|
||||||
|
// request to complete, and then focus the new rule's selector.
|
||||||
|
let eventPromise = this.once("ruleview-refreshed");
|
||||||
|
let newRulePromise = this.pageStyle.addNewRule(element, pseudoClasses);
|
||||||
|
promise.all([eventPromise, newRulePromise]).then((values) => {
|
||||||
|
let options = values[1];
|
||||||
|
// Be sure the reference the correct |rules| here.
|
||||||
|
for (let rule of this._elementStyle.rules) {
|
||||||
|
if (options.rule === rule.domRule) {
|
||||||
|
rule.editor.selectorText.click();
|
||||||
|
elementStyle._changed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables add rule button when needed
|
* Disables add rule button when needed
|
||||||
*/
|
*/
|
||||||
@ -2146,7 +2330,7 @@ CssRuleView.prototype = {
|
|||||||
|
|
||||||
// Highlight search matches in the rule properties
|
// Highlight search matches in the rule properties
|
||||||
for (let textProp of rule.textProps) {
|
for (let textProp of rule.textProps) {
|
||||||
if (this._highlightProperty(textProp.editor)) {
|
if (!textProp.invisible && this._highlightProperty(textProp.editor)) {
|
||||||
isHighlighted = true;
|
isHighlighted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2435,11 +2619,18 @@ function RuleEditor(aRuleView, aRule) {
|
|||||||
this._onNewProperty = this._onNewProperty.bind(this);
|
this._onNewProperty = this._onNewProperty.bind(this);
|
||||||
this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
|
this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
|
||||||
this._onSelectorDone = this._onSelectorDone.bind(this);
|
this._onSelectorDone = this._onSelectorDone.bind(this);
|
||||||
|
this._locationChanged = this._locationChanged.bind(this);
|
||||||
|
|
||||||
|
this.rule.domRule.on("location-changed", this._locationChanged);
|
||||||
|
|
||||||
this._create();
|
this._create();
|
||||||
}
|
}
|
||||||
|
|
||||||
RuleEditor.prototype = {
|
RuleEditor.prototype = {
|
||||||
|
destroy: function() {
|
||||||
|
this.rule.domRule.off("location-changed");
|
||||||
|
},
|
||||||
|
|
||||||
get isSelectorEditable() {
|
get isSelectorEditable() {
|
||||||
let toolbox = this.ruleView.inspector.toolbox;
|
let toolbox = this.ruleView.inspector.toolbox;
|
||||||
let trait = this.isEditable &&
|
let trait = this.isEditable &&
|
||||||
@ -2554,17 +2745,26 @@ RuleEditor.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler called when a property changes on the
|
||||||
|
* StyleRuleActor.
|
||||||
|
*/
|
||||||
|
_locationChanged: function(line, column) {
|
||||||
|
this.updateSourceLink();
|
||||||
|
},
|
||||||
|
|
||||||
updateSourceLink: function() {
|
updateSourceLink: function() {
|
||||||
let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
|
let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
|
||||||
|
let title = this.rule.title;
|
||||||
let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
|
let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
|
||||||
this.rule.sheet.href : this.rule.title;
|
this.rule.sheet.href : title;
|
||||||
let sourceLine = this.rule.ruleLine > 0 ? ":" + this.rule.ruleLine : "";
|
let sourceLine = this.rule.ruleLine > 0 ? ":" + this.rule.ruleLine : "";
|
||||||
|
|
||||||
sourceLabel.setAttribute("tooltiptext", sourceHref + sourceLine);
|
sourceLabel.setAttribute("tooltiptext", sourceHref + sourceLine);
|
||||||
|
|
||||||
if (this.rule.isSystem) {
|
if (this.rule.isSystem) {
|
||||||
let uaLabel = _strings.GetStringFromName("rule.userAgentStyles");
|
let uaLabel = _strings.GetStringFromName("rule.userAgentStyles");
|
||||||
sourceLabel.setAttribute("value", uaLabel + " " + this.rule.title);
|
sourceLabel.setAttribute("value", uaLabel + " " + title);
|
||||||
|
|
||||||
// Special case about:PreferenceStyleSheet, as it is generated on the
|
// Special case about:PreferenceStyleSheet, as it is generated on the
|
||||||
// fly and the URI is not registered with the about: handler.
|
// fly and the URI is not registered with the about: handler.
|
||||||
@ -2575,7 +2775,7 @@ RuleEditor.prototype = {
|
|||||||
sourceLabel.removeAttribute("tooltiptext");
|
sourceLabel.removeAttribute("tooltiptext");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sourceLabel.setAttribute("value", this.rule.title);
|
sourceLabel.setAttribute("value", title);
|
||||||
if (this.rule.ruleLine === -1 && this.rule.domRule.parentStyleSheet) {
|
if (this.rule.ruleLine === -1 && this.rule.domRule.parentStyleSheet) {
|
||||||
sourceLabel.parentNode.setAttribute("unselectable", "true");
|
sourceLabel.parentNode.setAttribute("unselectable", "true");
|
||||||
}
|
}
|
||||||
@ -2654,7 +2854,7 @@ RuleEditor.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let prop of this.rule.textProps) {
|
for (let prop of this.rule.textProps) {
|
||||||
if (!prop.editor) {
|
if (!prop.editor && !prop.invisible) {
|
||||||
let editor = new TextPropertyEditor(this, prop);
|
let editor = new TextPropertyEditor(this, prop);
|
||||||
this.propertyList.appendChild(editor.element);
|
this.propertyList.appendChild(editor.element);
|
||||||
}
|
}
|
||||||
@ -2862,13 +3062,13 @@ RuleEditor.prototype = {
|
|||||||
rules.splice(rules.indexOf(this.rule), 1);
|
rules.splice(rules.indexOf(this.rule), 1);
|
||||||
rules.push(newRule);
|
rules.push(newRule);
|
||||||
elementStyle._changed();
|
elementStyle._changed();
|
||||||
|
elementStyle.markOverriddenAll();
|
||||||
|
|
||||||
editor.element.setAttribute("unmatched", !isMatching);
|
editor.element.setAttribute("unmatched", !isMatching);
|
||||||
this.element.parentNode.replaceChild(editor.element, this.element);
|
this.element.parentNode.replaceChild(editor.element, this.element);
|
||||||
|
|
||||||
// Remove highlight for modified selector
|
// Remove highlight for modified selector
|
||||||
if (ruleView.highlightedSelector &&
|
if (ruleView.highlightedSelector) {
|
||||||
ruleView.highlightedSelector === this.rule.selectorText) {
|
|
||||||
ruleView.toggleSelectorHighlighter(ruleView.lastSelectorIcon,
|
ruleView.toggleSelectorHighlighter(ruleView.lastSelectorIcon,
|
||||||
ruleView.highlightedSelector);
|
ruleView.highlightedSelector);
|
||||||
}
|
}
|
||||||
@ -3172,7 +3372,8 @@ TextPropertyEditor.prototype = {
|
|||||||
!this.isValid() ||
|
!this.isValid() ||
|
||||||
!this.prop.overridden;
|
!this.prop.overridden;
|
||||||
|
|
||||||
if (this.prop.overridden || !this.prop.enabled) {
|
if (this.prop.overridden || !this.prop.enabled ||
|
||||||
|
!this.prop.isKnownProperty()) {
|
||||||
this.element.classList.add("ruleview-overridden");
|
this.element.classList.add("ruleview-overridden");
|
||||||
} else {
|
} else {
|
||||||
this.element.classList.remove("ruleview-overridden");
|
this.element.classList.remove("ruleview-overridden");
|
||||||
@ -3502,8 +3703,10 @@ TextPropertyEditor.prototype = {
|
|||||||
this.committed.value === val.value &&
|
this.committed.value === val.value &&
|
||||||
this.committed.priority === val.priority;
|
this.committed.priority === val.priority;
|
||||||
// If the value is not empty and unchanged, revert the property back to
|
// If the value is not empty and unchanged, revert the property back to
|
||||||
// its original enabled or disabled state
|
// its original value and enabled or disabled state
|
||||||
if (value.trim() && isValueUnchanged) {
|
if (value.trim() && isValueUnchanged) {
|
||||||
|
this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
|
||||||
|
val.priority);
|
||||||
this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
|
this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3555,7 +3758,7 @@ TextPropertyEditor.prototype = {
|
|||||||
* value of this property before editing.
|
* value of this property before editing.
|
||||||
*/
|
*/
|
||||||
_onSwatchRevert: function() {
|
_onSwatchRevert: function() {
|
||||||
this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
|
this._previewValue(this.prop.value);
|
||||||
this.update();
|
this.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -3631,7 +3834,7 @@ TextPropertyEditor.prototype = {
|
|||||||
* @return {Boolean} true if the property value is valid, false otherwise.
|
* @return {Boolean} true if the property value is valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
isValid: function() {
|
isValid: function() {
|
||||||
return domUtils.cssPropertyIsValid(this.prop.name, this.prop.value);
|
return this.prop.isValid();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ function RuleViewTool(inspector, window) {
|
|||||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||||
this.inspector.target.on("navigate", this.clearUserProperties);
|
this.inspector.target.on("navigate", this.clearUserProperties);
|
||||||
this.inspector.sidebar.on("ruleview-selected", this.onPanelSelected);
|
this.inspector.sidebar.on("ruleview-selected", this.onPanelSelected);
|
||||||
|
this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
|
||||||
|
|
||||||
this.onSelected();
|
this.onSelected();
|
||||||
}
|
}
|
||||||
@ -152,6 +153,9 @@ RuleViewTool.prototype = {
|
|||||||
this.inspector.selection.off("new-node-front", this.onSelected);
|
this.inspector.selection.off("new-node-front", this.onSelected);
|
||||||
this.inspector.target.off("navigate", this.clearUserProperties);
|
this.inspector.target.off("navigate", this.clearUserProperties);
|
||||||
this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
|
this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
|
||||||
|
if (this.inspector.pageStyle) {
|
||||||
|
this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
|
||||||
|
}
|
||||||
|
|
||||||
this.view.off("ruleview-linked-clicked", this.onLinkClicked);
|
this.view.off("ruleview-linked-clicked", this.onLinkClicked);
|
||||||
this.view.off("ruleview-changed", this.onPropertyChanged);
|
this.view.off("ruleview-changed", this.onPropertyChanged);
|
||||||
@ -179,6 +183,7 @@ function ComputedViewTool(inspector, window) {
|
|||||||
this.inspector.on("layout-change", this.refresh);
|
this.inspector.on("layout-change", this.refresh);
|
||||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||||
this.inspector.sidebar.on("computedview-selected", this.onPanelSelected);
|
this.inspector.sidebar.on("computedview-selected", this.onPanelSelected);
|
||||||
|
this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
|
||||||
|
|
||||||
this.view.selectElement(null);
|
this.view.selectElement(null);
|
||||||
|
|
||||||
@ -244,6 +249,9 @@ ComputedViewTool.prototype = {
|
|||||||
this.inspector.selection.off("pseudoclass", this.refresh);
|
this.inspector.selection.off("pseudoclass", this.refresh);
|
||||||
this.inspector.selection.off("new-node-front", this.onSelected);
|
this.inspector.selection.off("new-node-front", this.onSelected);
|
||||||
this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
|
this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
|
||||||
|
if (this.inspector.pageStyle) {
|
||||||
|
this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
|
||||||
|
}
|
||||||
|
|
||||||
this.view.destroy();
|
this.view.destroy();
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ support-files =
|
|||||||
doc_matched_selectors.html
|
doc_matched_selectors.html
|
||||||
doc_media_queries.html
|
doc_media_queries.html
|
||||||
doc_pseudoelement.html
|
doc_pseudoelement.html
|
||||||
|
doc_ruleLineNumbers.html
|
||||||
doc_sourcemaps.css
|
doc_sourcemaps.css
|
||||||
doc_sourcemaps.css.map
|
doc_sourcemaps.css.map
|
||||||
doc_sourcemaps.html
|
doc_sourcemaps.html
|
||||||
@ -60,6 +61,7 @@ support-files =
|
|||||||
[browser_ruleview_add-rule_03.js]
|
[browser_ruleview_add-rule_03.js]
|
||||||
[browser_ruleview_add-rule_04.js]
|
[browser_ruleview_add-rule_04.js]
|
||||||
[browser_ruleview_add-rule_pseudo_class.js]
|
[browser_ruleview_add-rule_pseudo_class.js]
|
||||||
|
[browser_ruleview_authored.js]
|
||||||
[browser_ruleview_colorpicker-and-image-tooltip_01.js]
|
[browser_ruleview_colorpicker-and-image-tooltip_01.js]
|
||||||
[browser_ruleview_colorpicker-and-image-tooltip_02.js]
|
[browser_ruleview_colorpicker-and-image-tooltip_02.js]
|
||||||
[browser_ruleview_colorpicker-appears-on-swatch-click.js]
|
[browser_ruleview_colorpicker-appears-on-swatch-click.js]
|
||||||
@ -118,18 +120,21 @@ skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work wit
|
|||||||
[browser_ruleview_filtereditor-commit-on-ENTER.js]
|
[browser_ruleview_filtereditor-commit-on-ENTER.js]
|
||||||
[browser_ruleview_filtereditor-revert-on-ESC.js]
|
[browser_ruleview_filtereditor-revert-on-ESC.js]
|
||||||
skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
|
skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
|
||||||
|
[browser_ruleview_guessIndentation.js]
|
||||||
[browser_ruleview_inherited-properties_01.js]
|
[browser_ruleview_inherited-properties_01.js]
|
||||||
[browser_ruleview_inherited-properties_02.js]
|
[browser_ruleview_inherited-properties_02.js]
|
||||||
[browser_ruleview_inherited-properties_03.js]
|
[browser_ruleview_inherited-properties_03.js]
|
||||||
[browser_ruleview_keybindings.js]
|
[browser_ruleview_keybindings.js]
|
||||||
[browser_ruleview_keyframes-rule_01.js]
|
[browser_ruleview_keyframes-rule_01.js]
|
||||||
[browser_ruleview_keyframes-rule_02.js]
|
[browser_ruleview_keyframes-rule_02.js]
|
||||||
|
[browser_ruleview_lineNumbers.js]
|
||||||
[browser_ruleview_livepreview.js]
|
[browser_ruleview_livepreview.js]
|
||||||
[browser_ruleview_mark_overridden_01.js]
|
[browser_ruleview_mark_overridden_01.js]
|
||||||
[browser_ruleview_mark_overridden_02.js]
|
[browser_ruleview_mark_overridden_02.js]
|
||||||
[browser_ruleview_mark_overridden_03.js]
|
[browser_ruleview_mark_overridden_03.js]
|
||||||
[browser_ruleview_mark_overridden_04.js]
|
[browser_ruleview_mark_overridden_04.js]
|
||||||
[browser_ruleview_mark_overridden_05.js]
|
[browser_ruleview_mark_overridden_05.js]
|
||||||
|
[browser_ruleview_mark_overridden_06.js]
|
||||||
[browser_ruleview_mark_overridden_07.js]
|
[browser_ruleview_mark_overridden_07.js]
|
||||||
[browser_ruleview_mathml-element.js]
|
[browser_ruleview_mathml-element.js]
|
||||||
[browser_ruleview_media-queries.js]
|
[browser_ruleview_media-queries.js]
|
||||||
|
@ -34,7 +34,13 @@ function checkColorCycling(container, inspector) {
|
|||||||
let valueNode = container.querySelector(".computedview-color");
|
let valueNode = container.querySelector(".computedview-color");
|
||||||
let win = inspector.sidebar.getWindowForTab("computedview");
|
let win = inspector.sidebar.getWindowForTab("computedview");
|
||||||
|
|
||||||
// Hex (default)
|
// "Authored" (default; currently the computed value)
|
||||||
|
is(valueNode.textContent, "rgb(255, 0, 0)",
|
||||||
|
"Color displayed as an RGB value.");
|
||||||
|
|
||||||
|
// Hex
|
||||||
|
EventUtils.synthesizeMouseAtCenter(swatch,
|
||||||
|
{type: "mousedown", shiftKey: true}, win);
|
||||||
is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
|
is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
|
||||||
|
|
||||||
// HSL
|
// HSL
|
||||||
@ -55,15 +61,9 @@ function checkColorCycling(container, inspector) {
|
|||||||
is(valueNode.textContent, "red",
|
is(valueNode.textContent, "red",
|
||||||
"Color displayed as a color name.");
|
"Color displayed as a color name.");
|
||||||
|
|
||||||
// "Authored" (currently the computed value)
|
// Back to "Authored"
|
||||||
EventUtils.synthesizeMouseAtCenter(swatch,
|
EventUtils.synthesizeMouseAtCenter(swatch,
|
||||||
{type: "mousedown", shiftKey: true}, win);
|
{type: "mousedown", shiftKey: true}, win);
|
||||||
is(valueNode.textContent, "rgb(255, 0, 0)",
|
is(valueNode.textContent, "rgb(255, 0, 0)",
|
||||||
"Color displayed as an RGB value.");
|
"Color displayed as an RGB value.");
|
||||||
|
|
||||||
// Back to hex
|
|
||||||
EventUtils.synthesizeMouseAtCenter(swatch,
|
|
||||||
{type: "mousedown", shiftKey: true}, win);
|
|
||||||
is(valueNode.textContent, "#F00",
|
|
||||||
"Color displayed as hex again.");
|
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ const TEST_DATA = [
|
|||||||
ok("property" in nodeInfo.value);
|
ok("property" in nodeInfo.value);
|
||||||
ok("value" in nodeInfo.value);
|
ok("value" in nodeInfo.value);
|
||||||
is(nodeInfo.value.property, "color");
|
is(nodeInfo.value.property, "color");
|
||||||
is(nodeInfo.value.value, "#F00");
|
is(nodeInfo.value.value, "rgb(255, 0, 0)");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,7 +88,7 @@ const TEST_DATA = [
|
|||||||
ok("property" in nodeInfo.value);
|
ok("property" in nodeInfo.value);
|
||||||
ok("value" in nodeInfo.value);
|
ok("value" in nodeInfo.value);
|
||||||
is(nodeInfo.value.property, "color");
|
is(nodeInfo.value.property, "color");
|
||||||
is(nodeInfo.value.value, "#F00");
|
is(nodeInfo.value.value, "rgb(255, 0, 0)");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -149,7 +149,7 @@ const TEST_DATA = [
|
|||||||
assertNodeInfo: function(nodeInfo) {
|
assertNodeInfo: function(nodeInfo) {
|
||||||
is(nodeInfo.type, VIEW_NODE_VALUE_TYPE);
|
is(nodeInfo.type, VIEW_NODE_VALUE_TYPE);
|
||||||
is(nodeInfo.value.property, "color");
|
is(nodeInfo.value.property, "color");
|
||||||
is(nodeInfo.value.value, "#F00");
|
is(nodeInfo.value.value, "red");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -25,5 +25,5 @@ add_task(function*() {
|
|||||||
fontSize = getComputedViewPropertyValue(view, "font-size");
|
fontSize = getComputedViewPropertyValue(view, "font-size");
|
||||||
is(fontSize, "15px", "The computed view shows the updated font-size");
|
is(fontSize, "15px", "The computed view shows the updated font-size");
|
||||||
let color = getComputedViewPropertyValue(view, "color");
|
let color = getComputedViewPropertyValue(view, "color");
|
||||||
is(color, "#F00", "The computed view also shows the color now");
|
is(color, "rgb(255, 0, 0)", "The computed view also shows the color now");
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ function checkSelectAll(view) {
|
|||||||
info("Checking that _onSelectAll() then copy returns the correct " +
|
info("Checking that _onSelectAll() then copy returns the correct " +
|
||||||
"clipboard value");
|
"clipboard value");
|
||||||
view._contextmenu._onSelectAll();
|
view._contextmenu._onSelectAll();
|
||||||
let expectedPattern = "color: #FF0;[\\r\\n]+" +
|
let expectedPattern = "color: rgb\\(255, 255, 0\\);[\\r\\n]+" +
|
||||||
"font-family: helvetica,sans-serif;[\\r\\n]+" +
|
"font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||||
"font-size: 16px;[\\r\\n]+" +
|
"font-size: 16px;[\\r\\n]+" +
|
||||||
"font-variant-caps: small-caps;[\\r\\n]*";
|
"font-variant-caps: small-caps;[\\r\\n]*";
|
||||||
|
@ -69,6 +69,6 @@ function* testCreateNew(view) {
|
|||||||
yield onModifications;
|
yield onModifications;
|
||||||
|
|
||||||
is(textProp.value, "#XYZ", "Text prop should have been changed.");
|
is(textProp.value, "#XYZ", "Text prop should have been changed.");
|
||||||
is(textProp.overridden, false, "Property should not be overridden");
|
is(textProp.overridden, true, "Property should be overridden");
|
||||||
is(textProp.editor.isValid(), false, "#XYZ should not be a valid entry");
|
is(textProp.editor.isValid(), false, "#XYZ should not be a valid entry");
|
||||||
}
|
}
|
||||||
|
127
devtools/client/styleinspector/test/browser_ruleview_authored.js
Normal file
127
devtools/client/styleinspector/test/browser_ruleview_authored.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/* 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 for as-authored styles.
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield basicTest();
|
||||||
|
yield overrideTest();
|
||||||
|
yield colorEditingTest();
|
||||||
|
});
|
||||||
|
|
||||||
|
function* createTestContent(style) {
|
||||||
|
let content = `<style type="text/css">
|
||||||
|
${style}
|
||||||
|
</style>
|
||||||
|
<div id="testid" class="testclass">Styled Node</div>`;
|
||||||
|
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(content));
|
||||||
|
|
||||||
|
let {inspector, view} = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* basicTest() {
|
||||||
|
let view = yield createTestContent("#testid {" +
|
||||||
|
// Invalid property.
|
||||||
|
" something: random;" +
|
||||||
|
// Invalid value.
|
||||||
|
" color: orang;" +
|
||||||
|
// Override.
|
||||||
|
" background-color: blue;" +
|
||||||
|
" background-color: #f0c;" +
|
||||||
|
"} ");
|
||||||
|
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
|
let expected = [
|
||||||
|
{name: "something", overridden: true},
|
||||||
|
{name: "color", overridden: true},
|
||||||
|
{name: "background-color", overridden: true},
|
||||||
|
{name: "background-color", overridden: false}
|
||||||
|
];
|
||||||
|
|
||||||
|
let rule = elementStyle.rules[1];
|
||||||
|
|
||||||
|
for (let i = 0; i < expected.length; ++i) {
|
||||||
|
let prop = rule.textProps[i];
|
||||||
|
is(prop.name, expected[i].name, "test name for prop " + i);
|
||||||
|
is(prop.overridden, expected[i].overridden,
|
||||||
|
"test overridden for prop " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function* overrideTest() {
|
||||||
|
let gradientText = "(45deg, rgba(255,255,255,0.2) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.2) 75%, transparent 75%, transparent);";
|
||||||
|
|
||||||
|
let view =
|
||||||
|
yield createTestContent("#testid {" +
|
||||||
|
" background-image: -moz-linear-gradient" +
|
||||||
|
gradientText +
|
||||||
|
" background-image: -webkit-linear-gradient" +
|
||||||
|
gradientText +
|
||||||
|
" background-image: linear-gradient" +
|
||||||
|
gradientText +
|
||||||
|
"} ");
|
||||||
|
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
let rule = elementStyle.rules[1];
|
||||||
|
|
||||||
|
// Initially the last property should be active.
|
||||||
|
for (let i = 0; i < 3; ++i) {
|
||||||
|
let prop = rule.textProps[i];
|
||||||
|
is(prop.name, "background-image", "check the property name");
|
||||||
|
is(prop.overridden, i !== 2, "check overridden for " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.textProps[2].setEnabled(false);
|
||||||
|
yield rule._applyingModifications;
|
||||||
|
|
||||||
|
// Now the first property should be active.
|
||||||
|
for (let i = 0; i < 3; ++i) {
|
||||||
|
let prop = rule.textProps[i];
|
||||||
|
is(prop.overridden || !prop.enabled, i !== 0,
|
||||||
|
"post-change check overridden for " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function* colorEditingTest() {
|
||||||
|
let colors = [
|
||||||
|
{name: "hex", text: "#f0c", result: "#0F0"},
|
||||||
|
{name: "rgb", text: "rgb(0,128,250)", result: "rgb(0, 255, 0)"}
|
||||||
|
];
|
||||||
|
|
||||||
|
Services.prefs.setCharPref("devtools.defaultColorUnit", "authored");
|
||||||
|
|
||||||
|
for (let color of colors) {
|
||||||
|
let view = yield createTestContent("#testid {" +
|
||||||
|
" color: " + color.text + ";" +
|
||||||
|
"} ");
|
||||||
|
|
||||||
|
let cPicker = view.tooltips.colorPicker;
|
||||||
|
let swatch = getRuleViewProperty(view, "#testid", "color").valueSpan
|
||||||
|
.querySelector(".ruleview-colorswatch");
|
||||||
|
let onShown = cPicker.tooltip.once("shown");
|
||||||
|
swatch.click();
|
||||||
|
yield onShown;
|
||||||
|
|
||||||
|
let testNode = yield getNode("#testid");
|
||||||
|
|
||||||
|
yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], {
|
||||||
|
element: testNode,
|
||||||
|
name: "color",
|
||||||
|
value: "rgb(0, 255, 0)"
|
||||||
|
});
|
||||||
|
|
||||||
|
let spectrum = yield cPicker.spectrum;
|
||||||
|
let onHidden = cPicker.tooltip.once("hidden");
|
||||||
|
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);
|
||||||
|
yield onHidden;
|
||||||
|
|
||||||
|
is(getRuleViewPropertyValue(view, "#testid", "color"), color.result,
|
||||||
|
"changing the color preserved the unit for " + color.name);
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ add_task(function*() {
|
|||||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
let {view} = yield openRuleView();
|
let {view} = yield openRuleView();
|
||||||
let value = getRuleViewProperty(view, "body", "background").valueSpan;
|
let value = getRuleViewProperty(view, "body", "background").valueSpan;
|
||||||
let swatch = value.querySelectorAll(".ruleview-colorswatch")[1];
|
let swatch = value.querySelectorAll(".ruleview-colorswatch")[0];
|
||||||
let url = value.querySelector(".theme-link");
|
let url = value.querySelector(".theme-link");
|
||||||
yield testImageTooltipAfterColorChange(swatch, url, view);
|
yield testImageTooltipAfterColorChange(swatch, url, view);
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@ function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
|
|||||||
|
|
||||||
info("Image tooltip is shown, verify that the swatch is still correct");
|
info("Image tooltip is shown, verify that the swatch is still correct");
|
||||||
swatch = value.querySelector(".ruleview-colorswatch");
|
swatch = value.querySelector(".ruleview-colorswatch");
|
||||||
is(swatch.style.backgroundColor, "rgb(0, 0, 0)",
|
is(swatch.style.backgroundColor, "black",
|
||||||
"The swatch's color is correct");
|
"The swatch's color is correct");
|
||||||
is(swatch.nextSibling.textContent, "#000", "The color name is correct");
|
is(swatch.nextSibling.textContent, "black", "The color name is correct");
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ function testColorParsing(view) {
|
|||||||
ok(colorEls, "The color elements were found");
|
ok(colorEls, "The color elements were found");
|
||||||
is(colorEls.length, 3, "There are 3 color values");
|
is(colorEls.length, 3, "There are 3 color values");
|
||||||
|
|
||||||
let colors = ["#F06", "#333", "#000"];
|
let colors = ["#f06", "#333", "#000"];
|
||||||
for (let i = 0; i < colors.length; i++) {
|
for (let i = 0; i < colors.length; i++) {
|
||||||
is(colorEls[i].textContent, colors[i], "The right color value was found");
|
is(colorEls[i].textContent, colors[i], "The right color value was found");
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ function checkColorCycling(container, inspector) {
|
|||||||
let valueNode = container.querySelector(".ruleview-color");
|
let valueNode = container.querySelector(".ruleview-color");
|
||||||
let win = inspector.sidebar.getWindowForTab("ruleview");
|
let win = inspector.sidebar.getWindowForTab("ruleview");
|
||||||
|
|
||||||
// Hex (default)
|
// Hex
|
||||||
is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
|
is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
|
||||||
|
|
||||||
// HSL
|
// HSL
|
||||||
@ -48,15 +48,16 @@ function checkColorCycling(container, inspector) {
|
|||||||
is(valueNode.textContent, "red",
|
is(valueNode.textContent, "red",
|
||||||
"Color displayed as a color name.");
|
"Color displayed as a color name.");
|
||||||
|
|
||||||
// "Authored" (currently the computed value)
|
// "Authored"
|
||||||
EventUtils.synthesizeMouseAtCenter(swatch,
|
|
||||||
{type: "mousedown", shiftKey: true}, win);
|
|
||||||
is(valueNode.textContent, "rgb(255, 0, 0)",
|
|
||||||
"Color displayed as an RGB value.");
|
|
||||||
|
|
||||||
// Back to hex
|
|
||||||
EventUtils.synthesizeMouseAtCenter(swatch,
|
EventUtils.synthesizeMouseAtCenter(swatch,
|
||||||
{type: "mousedown", shiftKey: true}, win);
|
{type: "mousedown", shiftKey: true}, win);
|
||||||
is(valueNode.textContent, "#F00",
|
is(valueNode.textContent, "#F00",
|
||||||
"Color displayed as hex again.");
|
"Color displayed as an authored value.");
|
||||||
|
|
||||||
|
// One more click skips hex, because it is the same as authored, and
|
||||||
|
// instead goes back to HSL.
|
||||||
|
EventUtils.synthesizeMouseAtCenter(swatch,
|
||||||
|
{type: "mousedown", shiftKey: true}, win);
|
||||||
|
is(valueNode.textContent, "hsl(0, 100%, 50%)",
|
||||||
|
"Color displayed as an HSL value again.");
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,10 @@ function* testEditPropertyAndRemove(inspector, view) {
|
|||||||
"Focus should have moved to the next property name");
|
"Focus should have moved to the next property name");
|
||||||
|
|
||||||
info("Focus the property value and remove the property");
|
info("Focus the property value and remove the property");
|
||||||
|
let onChanged = view.once("ruleview-changed");
|
||||||
yield sendKeysAndWaitForFocus(view, ruleEditor.element,
|
yield sendKeysAndWaitForFocus(view, ruleEditor.element,
|
||||||
["VK_TAB", "VK_DELETE", "VK_RETURN"]);
|
["VK_TAB", "VK_DELETE", "VK_RETURN"]);
|
||||||
yield ruleEditor.rule._applyingModifications;
|
yield onChanged;
|
||||||
|
|
||||||
newValue = yield executeInContent("Test:GetRulePropertyValue", {
|
newValue = yield executeInContent("Test:GetRulePropertyValue", {
|
||||||
styleSheetIndex: 0,
|
styleSheetIndex: 0,
|
||||||
|
@ -59,7 +59,7 @@ function* testEditProperty(ruleEditor, name, value, isValid) {
|
|||||||
info("Entering a new property name, including : to commit and " +
|
info("Entering a new property name, including : to commit and " +
|
||||||
"focus the value");
|
"focus the value");
|
||||||
let onValueFocus = once(ruleEditor.element, "focus", true);
|
let onValueFocus = once(ruleEditor.element, "focus", true);
|
||||||
let onModifications = ruleEditor.rule._applyingModifications;
|
let onModifications = ruleEditor.ruleView.once("ruleview-changed");
|
||||||
EventUtils.sendString(name + ":", doc.defaultView);
|
EventUtils.sendString(name + ":", doc.defaultView);
|
||||||
yield onValueFocus;
|
yield onValueFocus;
|
||||||
yield onModifications;
|
yield onModifications;
|
||||||
@ -71,10 +71,9 @@ function* testEditProperty(ruleEditor, name, value, isValid) {
|
|||||||
|
|
||||||
info("Entering a new value, including ; to commit and blur the value");
|
info("Entering a new value, including ; to commit and blur the value");
|
||||||
let onBlur = once(input, "blur");
|
let onBlur = once(input, "blur");
|
||||||
onModifications = ruleEditor.rule._applyingModifications;
|
|
||||||
EventUtils.sendString(value + ";", doc.defaultView);
|
EventUtils.sendString(value + ";", doc.defaultView);
|
||||||
yield onBlur;
|
yield onBlur;
|
||||||
yield onModifications;
|
yield ruleEditor.rule._applyingModifications;
|
||||||
|
|
||||||
is(propEditor.isValid(), isValid,
|
is(propEditor.isValid(), isValid,
|
||||||
value + " is " + isValid ? "valid" : "invalid");
|
value + " is " + isValid ? "valid" : "invalid");
|
||||||
|
@ -33,8 +33,10 @@ add_task(function*() {
|
|||||||
yield focusEditableField(view, propEditor.valueSpan);
|
yield focusEditableField(view, propEditor.valueSpan);
|
||||||
|
|
||||||
info("Deleting all the text out of a value field");
|
info("Deleting all the text out of a value field");
|
||||||
|
let waitForUpdates = view.once("ruleview-changed");
|
||||||
yield sendCharsAndWaitForFocus(view, ruleEditor.element,
|
yield sendCharsAndWaitForFocus(view, ruleEditor.element,
|
||||||
["VK_DELETE", "VK_RETURN"]);
|
["VK_DELETE", "VK_RETURN"]);
|
||||||
|
yield waitForUpdates;
|
||||||
|
|
||||||
info("Pressing enter a couple times to cycle through editors");
|
info("Pressing enter a couple times to cycle through editors");
|
||||||
yield sendCharsAndWaitForFocus(view, ruleEditor.element, ["VK_RETURN"]);
|
yield sendCharsAndWaitForFocus(view, ruleEditor.element, ["VK_RETURN"]);
|
||||||
@ -48,9 +50,7 @@ add_task(function*() {
|
|||||||
function* sendCharsAndWaitForFocus(view, element, chars) {
|
function* sendCharsAndWaitForFocus(view, element, chars) {
|
||||||
let onFocus = once(element, "focus", true);
|
let onFocus = once(element, "focus", true);
|
||||||
for (let ch of chars) {
|
for (let ch of chars) {
|
||||||
let onRuleViewChanged = view.once("ruleview-changed");
|
|
||||||
EventUtils.sendChar(ch, element.ownerDocument.defaultView);
|
EventUtils.sendChar(ch, element.ownerDocument.defaultView);
|
||||||
yield onRuleViewChanged;
|
|
||||||
}
|
}
|
||||||
yield onFocus;
|
yield onFocus;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
const TEST_URI = `
|
const TEST_URI = `
|
||||||
<style type='text/css'>
|
<style type='text/css'>
|
||||||
#testid {
|
#testid {
|
||||||
background-color: red;
|
background-color: #f00;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id='testid'>Styled Node</div>
|
<div id='testid'>Styled Node</div>
|
||||||
|
@ -98,7 +98,7 @@ function* testAddProperty(view) {
|
|||||||
info("Entering a value and bluring the field to expect a rule change");
|
info("Entering a value and bluring the field to expect a rule change");
|
||||||
editor.input.value = "center";
|
editor.input.value = "center";
|
||||||
let onBlur = once(editor.input, "blur");
|
let onBlur = once(editor.input, "blur");
|
||||||
onRuleViewChanged = view.once("ruleview-changed");
|
onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2);
|
||||||
editor.input.blur();
|
editor.input.blur();
|
||||||
yield onBlur;
|
yield onBlur;
|
||||||
yield onRuleViewChanged;
|
yield onRuleViewChanged;
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
/* 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 that we can guess indentation from a style sheet, not just a
|
||||||
|
// rule.
|
||||||
|
|
||||||
|
// Needed for openStyleEditor.
|
||||||
|
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/styleeditor/test/head.js", this);
|
||||||
|
|
||||||
|
// Use a weird indentation depth to avoid accidental success.
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
div {
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id='testid' class='testclass'>Styled Node</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const expectedText = `
|
||||||
|
div {
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
color: chartreuse;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
let {inspector, view} = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
yield testIndentation(inspector, view);
|
||||||
|
});
|
||||||
|
|
||||||
|
function* testIndentation(inspector, view) {
|
||||||
|
let ruleEditor = getRuleViewRuleEditor(view, 2);
|
||||||
|
|
||||||
|
info("Focusing a new property name in the rule-view");
|
||||||
|
let editor = yield focusEditableField(view, ruleEditor.closeBrace);
|
||||||
|
|
||||||
|
let input = editor.input;
|
||||||
|
|
||||||
|
info("Entering color in the property name editor");
|
||||||
|
input.value = "color";
|
||||||
|
|
||||||
|
info("Pressing return to commit and focus the new value field");
|
||||||
|
let onValueFocus = once(ruleEditor.element, "focus", true);
|
||||||
|
let onModifications = ruleEditor.rule._applyingModifications;
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
||||||
|
yield onValueFocus;
|
||||||
|
yield onModifications;
|
||||||
|
|
||||||
|
// Getting the new value editor after focus
|
||||||
|
editor = inplaceEditor(view.styleDocument.activeElement);
|
||||||
|
info("Entering a value and bluring the field to expect a rule change");
|
||||||
|
editor.input.value = "chartreuse";
|
||||||
|
let onBlur = once(editor.input, "blur");
|
||||||
|
onModifications = ruleEditor.rule._applyingModifications;
|
||||||
|
editor.input.blur();
|
||||||
|
yield onBlur;
|
||||||
|
yield onModifications;
|
||||||
|
|
||||||
|
let { ui } = yield openStyleEditor();
|
||||||
|
|
||||||
|
let styleEditor = yield ui.editors[0].getSourceEditor();
|
||||||
|
let text = styleEditor.sourceEditor.getText();
|
||||||
|
is(text, expectedText, "style inspector changes are synced");
|
||||||
|
}
|
@ -38,8 +38,12 @@ function* simpleInherit(inspector, view) {
|
|||||||
is(inheritRule.selectorText, "#test2",
|
is(inheritRule.selectorText, "#test2",
|
||||||
"Inherited rule should be the one that includes inheritable properties.");
|
"Inherited rule should be the one that includes inheritable properties.");
|
||||||
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
||||||
is(inheritRule.textProps.length, 1,
|
is(inheritRule.textProps.length, 2,
|
||||||
"Should only display one inherited style");
|
"Rule should have two styles");
|
||||||
let inheritProp = inheritRule.textProps[0];
|
let bgcProp = inheritRule.textProps[0];
|
||||||
|
is(bgcProp.name, "background-color",
|
||||||
|
"background-color property should exist");
|
||||||
|
ok(bgcProp.invisible, "background-color property should be invisible");
|
||||||
|
let inheritProp = inheritRule.textProps[1];
|
||||||
is(inheritProp.name, "color", "color should have been inherited.");
|
is(inheritProp.name, "color", "color should have been inherited.");
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/* 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 that editing a rule will update the line numbers of subsequent
|
||||||
|
// rules in the rule view.
|
||||||
|
|
||||||
|
const TESTCASE_URI = TEST_URL_ROOT + "doc_ruleLineNumbers.html";
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
yield addTab(TESTCASE_URI);
|
||||||
|
let { inspector, view } = yield openRuleView();
|
||||||
|
yield selectNode("#testid", inspector);
|
||||||
|
let elementRuleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
|
||||||
|
let bodyRuleEditor = getRuleViewRuleEditor(view, 3);
|
||||||
|
let value = getRuleViewLinkTextByIndex(view, 2);
|
||||||
|
// Note that this is relative to the <style>.
|
||||||
|
is(value.slice(-2), ":6", "initial rule line number is 6");
|
||||||
|
|
||||||
|
info("Focusing a new property name in the rule-view");
|
||||||
|
let editor = yield focusEditableField(view, elementRuleEditor.closeBrace);
|
||||||
|
|
||||||
|
is(inplaceEditor(elementRuleEditor.newPropSpan), editor,
|
||||||
|
"The new property editor got focused");
|
||||||
|
let input = editor.input;
|
||||||
|
|
||||||
|
info("Entering font-size in the property name editor");
|
||||||
|
input.value = "font-size";
|
||||||
|
|
||||||
|
info("Pressing return to commit and focus the new value field");
|
||||||
|
let onLocationChanged = once(bodyRuleEditor.rule.domRule, "location-changed");
|
||||||
|
let onValueFocus = once(elementRuleEditor.element, "focus", true);
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
||||||
|
yield onValueFocus;
|
||||||
|
yield elementRuleEditor.rule._applyingModifications;
|
||||||
|
|
||||||
|
// Getting the new value editor after focus
|
||||||
|
editor = inplaceEditor(view.styleDocument.activeElement);
|
||||||
|
info("Entering a value and bluring the field to expect a rule change");
|
||||||
|
editor.input.value = "23px";
|
||||||
|
editor.input.blur();
|
||||||
|
yield elementRuleEditor.rule._applyingModifications;
|
||||||
|
|
||||||
|
yield onLocationChanged;
|
||||||
|
|
||||||
|
let newBodyTitle = getRuleViewLinkTextByIndex(view, 2);
|
||||||
|
// Note that this is relative to the <style>.
|
||||||
|
is(newBodyTitle.slice(-2), ":7", "updated rule line number is 7");
|
||||||
|
});
|
@ -27,6 +27,7 @@ function* testMarkOverridden(inspector, view) {
|
|||||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
|
||||||
yield createNewRuleViewProperty(ruleEditor, "background-color: red;");
|
yield createNewRuleViewProperty(ruleEditor, "background-color: red;");
|
||||||
|
yield ruleEditor.rule._applyingModifications;
|
||||||
|
|
||||||
let firstProp = ruleEditor.rule.textProps[0];
|
let firstProp = ruleEditor.rule.textProps[0];
|
||||||
let secondProp = ruleEditor.rule.textProps[1];
|
let secondProp = ruleEditor.rule.textProps[1];
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/* 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 that the rule view marks overridden rules correctly after
|
||||||
|
// editing the selector.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
div {
|
||||||
|
background-color: blue;
|
||||||
|
background-color: chartreuse;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id='testid' class='testclass'>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);
|
||||||
|
yield testMarkOverridden(inspector, view);
|
||||||
|
});
|
||||||
|
|
||||||
|
function* testMarkOverridden(inspector, view) {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
let rule = elementStyle.rules[1];
|
||||||
|
checkProperties(rule);
|
||||||
|
|
||||||
|
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||||
|
info("Focusing an existing selector name in the rule-view");
|
||||||
|
let editor = yield focusEditableField(view, ruleEditor.selectorText);
|
||||||
|
|
||||||
|
info("Entering a new selector name and committing");
|
||||||
|
editor.input.value = "div[class]";
|
||||||
|
|
||||||
|
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||||
|
info("Entering the commit key");
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||||
|
yield onRuleViewChanged;
|
||||||
|
|
||||||
|
view.searchField.focus();
|
||||||
|
checkProperties(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper to perform a repeated set of checks.
|
||||||
|
function checkProperties(rule) {
|
||||||
|
let prop = rule.textProps[0];
|
||||||
|
is(prop.name, "background-color",
|
||||||
|
"First property should be background-color");
|
||||||
|
is(prop.value, "blue", "First property value should be blue");
|
||||||
|
ok(prop.overridden, "prop should be overridden.");
|
||||||
|
prop = rule.textProps[1];
|
||||||
|
is(prop.name, "background-color",
|
||||||
|
"Second property should be background-color");
|
||||||
|
is(prop.value, "chartreuse", "First property value should be chartreuse");
|
||||||
|
ok(!prop.overridden, "prop should not be overridden.");
|
||||||
|
}
|
@ -51,7 +51,8 @@ function* testMarkOverridden(inspector, view) {
|
|||||||
[{name: "margin-right", value: "23px", overridden: false},
|
[{name: "margin-right", value: "23px", overridden: false},
|
||||||
{name: "margin-left", value: "1px", overridden: false}],
|
{name: "margin-left", value: "1px", overridden: false}],
|
||||||
[{name: "font-size", value: "12px", overridden: false}],
|
[{name: "font-size", value: "12px", overridden: false}],
|
||||||
[{name: "font-size", value: "79px", overridden: true}]
|
[{name: "margin-right", value: "1px", overridden: true},
|
||||||
|
{name: "font-size", value: "79px", overridden: true}]
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 1; i < RESULTS.length; ++i) {
|
for (let i = 1; i < RESULTS.length; ++i) {
|
||||||
|
@ -24,7 +24,6 @@ add_task(function*() {
|
|||||||
is(elementStyle.rules.length, 3, "Should have 3 rules.");
|
is(elementStyle.rules.length, 3, "Should have 3 rules.");
|
||||||
is(elementStyle.rules[0].title, inline, "check rule 0 title");
|
is(elementStyle.rules[0].title, inline, "check rule 0 title");
|
||||||
is(elementStyle.rules[1].title, inline +
|
is(elementStyle.rules[1].title, inline +
|
||||||
":15 @media screen and (min-width: 1px)", "check rule 1 title");
|
":9 @media screen and (min-width: 1px)", "check rule 1 title");
|
||||||
is(elementStyle.rules[2].title, inline + ":8", "check rule 2 title");
|
is(elementStyle.rules[2].title, inline + ":2", "check rule 2 title");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Test that the rule-view behaves correctly when entering mutliple and/or
|
// Test that the rule-view behaves correctly when entering multiple and/or
|
||||||
// unfinished properties/values in inplace-editors
|
// unfinished properties/values in inplace-editors
|
||||||
|
|
||||||
const TEST_URI = "<div>Test Element</div>";
|
const TEST_URI = "<div>Test Element</div>";
|
||||||
@ -16,24 +16,12 @@ add_task(function*() {
|
|||||||
yield testCreateNewMultiUnfinished(inspector, view);
|
yield testCreateNewMultiUnfinished(inspector, view);
|
||||||
});
|
});
|
||||||
|
|
||||||
function waitRuleViewChanged(view, n) {
|
|
||||||
let deferred = promise.defer();
|
|
||||||
let count = 0;
|
|
||||||
let listener = function() {
|
|
||||||
if (++count == n) {
|
|
||||||
view.off("ruleview-changed", listener);
|
|
||||||
deferred.resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
view.on("ruleview-changed", listener);
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
function* testCreateNewMultiUnfinished(inspector, view) {
|
function* testCreateNewMultiUnfinished(inspector, view) {
|
||||||
let ruleEditor = getRuleViewRuleEditor(view, 0);
|
let ruleEditor = getRuleViewRuleEditor(view, 0);
|
||||||
let onMutation = inspector.once("markupmutation");
|
let onMutation = inspector.once("markupmutation");
|
||||||
// There is 5 rule-view updates, one for the rule view creation,
|
// There are 2 rule-view updates: one for the preview and one for
|
||||||
// one for each new property
|
// the final commit.
|
||||||
let onRuleViewChanged = waitRuleViewChanged(view, 5);
|
let onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2);
|
||||||
yield createNewRuleViewProperty(ruleEditor,
|
yield createNewRuleViewProperty(ruleEditor,
|
||||||
"color:blue;background : orange ; text-align:center; border-color: ");
|
"color:blue;background : orange ; text-align:center; border-color: ");
|
||||||
yield onMutation;
|
yield onMutation;
|
||||||
|
@ -166,7 +166,7 @@ function* testParagraph(inspector, view) {
|
|||||||
|
|
||||||
let elementFirstLineRule = rules.firstLineRules[0];
|
let elementFirstLineRule = rules.firstLineRules[0];
|
||||||
is(convertTextPropsToString(elementFirstLineRule.textProps),
|
is(convertTextPropsToString(elementFirstLineRule.textProps),
|
||||||
"background: blue none repeat scroll 0% 0%",
|
"background: blue",
|
||||||
"Paragraph first-line properties are correct");
|
"Paragraph first-line properties are correct");
|
||||||
|
|
||||||
let elementFirstLetterRule = rules.firstLetterRules[0];
|
let elementFirstLetterRule = rules.firstLetterRules[0];
|
||||||
@ -176,7 +176,7 @@ function* testParagraph(inspector, view) {
|
|||||||
|
|
||||||
let elementSelectionRule = rules.selectionRules[0];
|
let elementSelectionRule = rules.selectionRules[0];
|
||||||
is(convertTextPropsToString(elementSelectionRule.textProps),
|
is(convertTextPropsToString(elementSelectionRule.textProps),
|
||||||
"color: white; background: black none repeat scroll 0% 0%",
|
"color: white; background: black",
|
||||||
"Paragraph first-letter properties are correct");
|
"Paragraph first-letter properties are correct");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +131,7 @@ function* testPropertyChange6(inspector, ruleView, testElement) {
|
|||||||
"Added a property");
|
"Added a property");
|
||||||
validateTextProp(rule.textProps[4], true, "background",
|
validateTextProp(rule.textProps[4], true, "background",
|
||||||
"red url(\"chrome://branding/content/about-logo.png\") repeat scroll 0% 0%",
|
"red url(\"chrome://branding/content/about-logo.png\") repeat scroll 0% 0%",
|
||||||
"shortcut property correctly set",
|
"shortcut property correctly set");
|
||||||
"#F00 url(\"chrome://branding/content/about-logo.png\") repeat scroll 0% 0%");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* changeElementStyle(testElement, style, inspector) {
|
function* changeElementStyle(testElement, style, inspector) {
|
||||||
@ -141,8 +140,7 @@ function* changeElementStyle(testElement, style, inspector) {
|
|||||||
yield onRefreshed;
|
yield onRefreshed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc,
|
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc) {
|
||||||
valueSpanText) {
|
|
||||||
is(aProp.enabled, aEnabled, aDesc + ": enabled.");
|
is(aProp.enabled, aEnabled, aDesc + ": enabled.");
|
||||||
is(aProp.name, aName, aDesc + ": name.");
|
is(aProp.name, aName, aDesc + ": name.");
|
||||||
is(aProp.value, aValue, aDesc + ": value.");
|
is(aProp.value, aValue, aDesc + ": value.");
|
||||||
@ -151,5 +149,5 @@ function validateTextProp(aProp, aEnabled, aName, aValue, aDesc,
|
|||||||
aDesc + ": enabled checkbox.");
|
aDesc + ": enabled checkbox.");
|
||||||
is(aProp.editor.nameSpan.textContent, aName, aDesc + ": name span.");
|
is(aProp.editor.nameSpan.textContent, aName, aDesc + ": name span.");
|
||||||
is(aProp.editor.valueSpan.textContent,
|
is(aProp.editor.valueSpan.textContent,
|
||||||
valueSpanText || aValue, aDesc + ": value span.");
|
aValue, aDesc + ": value span.");
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
// Tests that the rule view search filter works properly in the computed list
|
// Tests that the rule view search filter works properly in the computed list
|
||||||
// for color values.
|
// for color values.
|
||||||
|
|
||||||
const SEARCH = "background-color: #F3F3F3";
|
// The color format here is chosen to match the default returned by
|
||||||
|
// CssColor.toString.
|
||||||
|
const SEARCH = "background-color: rgb(243, 243, 243)";
|
||||||
|
|
||||||
const TEST_URI = `
|
const TEST_URI = `
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.testclass {
|
.testclass {
|
||||||
background: #F3F3F3 none repeat scroll 0% 0%;
|
background: rgb(243, 243, 243) none repeat scroll 0% 0%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="testclass">Styled Node</h1>
|
<div class="testclass">Styled Node</h1>
|
||||||
|
@ -43,7 +43,7 @@ function* testModifyPropertyValueFilter(inspector, view) {
|
|||||||
"top text property is correctly highlighted.");
|
"top text property is correctly highlighted.");
|
||||||
|
|
||||||
let onBlur = once(editor.input, "blur");
|
let onBlur = once(editor.input, "blur");
|
||||||
let onModification = rule._applyingModifications;
|
let onModification = view.once("ruleview-changed");
|
||||||
EventUtils.sendString("4px 0px", view.styleWindow);
|
EventUtils.sendString("4px 0px", view.styleWindow);
|
||||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||||
yield onBlur;
|
yield onBlur;
|
||||||
|
@ -64,10 +64,10 @@ function* checkCopySelection(view) {
|
|||||||
let expectedPattern = " margin: 10em;[\\r\\n]+" +
|
let expectedPattern = " margin: 10em;[\\r\\n]+" +
|
||||||
" font-size: 14pt;[\\r\\n]+" +
|
" font-size: 14pt;[\\r\\n]+" +
|
||||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||||
" color: #AAA;[\\r\\n]+" +
|
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||||
"}[\\r\\n]+" +
|
"}[\\r\\n]+" +
|
||||||
"html {[\\r\\n]+" +
|
"html {[\\r\\n]+" +
|
||||||
" color: #000;[\\r\\n]*";
|
" color: #000000;[\\r\\n]*";
|
||||||
|
|
||||||
let onPopup = once(view._contextmenu._menupopup, "popupshown");
|
let onPopup = once(view._contextmenu._menupopup, "popupshown");
|
||||||
EventUtils.synthesizeMouseAtCenter(prop,
|
EventUtils.synthesizeMouseAtCenter(prop,
|
||||||
@ -102,10 +102,10 @@ function* checkSelectAll(view) {
|
|||||||
" margin: 10em;[\\r\\n]+" +
|
" margin: 10em;[\\r\\n]+" +
|
||||||
" font-size: 14pt;[\\r\\n]+" +
|
" font-size: 14pt;[\\r\\n]+" +
|
||||||
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
" font-family: helvetica,sans-serif;[\\r\\n]+" +
|
||||||
" color: #AAA;[\\r\\n]+" +
|
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
|
||||||
"}[\\r\\n]+" +
|
"}[\\r\\n]+" +
|
||||||
"html {[\\r\\n]+" +
|
"html {[\\r\\n]+" +
|
||||||
" color: #000;[\\r\\n]+" +
|
" color: #000000;[\\r\\n]+" +
|
||||||
"}[\\r\\n]*";
|
"}[\\r\\n]*";
|
||||||
|
|
||||||
let onPopup = once(view._contextmenu._menupopup, "popupshown");
|
let onPopup = once(view._contextmenu._menupopup, "popupshown");
|
||||||
|
@ -40,9 +40,11 @@ function* userAgentStylesUneditable(inspector, view) {
|
|||||||
ok(rule.editor.element.hasAttribute("uneditable"),
|
ok(rule.editor.element.hasAttribute("uneditable"),
|
||||||
"UA rules have uneditable attribute");
|
"UA rules have uneditable attribute");
|
||||||
|
|
||||||
ok(!rule.textProps[0].editor.nameSpan._editable,
|
let firstProp = rule.textProps.filter(p => !p.invisible)[0];
|
||||||
|
|
||||||
|
ok(!firstProp.editor.nameSpan._editable,
|
||||||
"nameSpan is not editable");
|
"nameSpan is not editable");
|
||||||
ok(!rule.textProps[0].editor.valueSpan._editable,
|
ok(!firstProp.editor.valueSpan._editable,
|
||||||
"valueSpan is not editable");
|
"valueSpan is not editable");
|
||||||
ok(!rule.editor.closeBrace._editable, "closeBrace is not editable");
|
ok(!rule.editor.closeBrace._editable, "closeBrace is not editable");
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ function testIsColorPopupOnNode(view, node) {
|
|||||||
let correct = isColorValueNode(node);
|
let correct = isColorValueNode(node);
|
||||||
|
|
||||||
is(result, correct, "_isColorPopup returned the expected value " + correct);
|
is(result, correct, "_isColorPopup returned the expected value " + correct);
|
||||||
is(view._contextmenu._colorToCopy, (correct) ? "#123ABC" : "",
|
is(view._contextmenu._colorToCopy, (correct) ? "rgb(18, 58, 188)" : "",
|
||||||
"_colorToCopy was set to the expected value");
|
"_colorToCopy was set to the expected value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ add_task(function*() {
|
|||||||
let {inspector, view} = yield openRuleView();
|
let {inspector, view} = yield openRuleView();
|
||||||
yield selectNode("#one", inspector);
|
yield selectNode("#one", inspector);
|
||||||
|
|
||||||
is(getRuleViewPropertyValue(view, "element", "color"), "#F00",
|
is(getRuleViewPropertyValue(view, "element", "color"), "red",
|
||||||
"The rule-view shows the properties for test node one");
|
"The rule-view shows the properties for test node one");
|
||||||
|
|
||||||
let cView = inspector.sidebar.getWindowForTab("computedview")
|
let cView = inspector.sidebar.getWindowForTab("computedview")
|
||||||
@ -38,6 +38,6 @@ add_task(function*() {
|
|||||||
ok(getComputedViewPropertyValue(cView, "color"), "#00F",
|
ok(getComputedViewPropertyValue(cView, "color"), "#00F",
|
||||||
"The computed-view shows the properties for test node two");
|
"The computed-view shows the properties for test node two");
|
||||||
|
|
||||||
is(getRuleViewPropertyValue(view, "element", "color"), "#F00",
|
is(getRuleViewPropertyValue(view, "element", "color"), "red",
|
||||||
"The rule-view doesn't the properties for test node two");
|
"The rule-view doesn't the properties for test node two");
|
||||||
});
|
});
|
||||||
|
19
devtools/client/styleinspector/test/doc_ruleLineNumbers.html
Normal file
19
devtools/client/styleinspector/test/doc_ruleLineNumbers.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>simple testcase</title>
|
||||||
|
<style type="text/css">
|
||||||
|
#testid {
|
||||||
|
background-color: seagreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: chartreuse;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="testid">simple testcase</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -306,6 +306,44 @@ function openRuleView() {
|
|||||||
return openInspectorSideBar("ruleview");
|
return openInspectorSideBar("ruleview");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for eventName on target to be delivered a number of times.
|
||||||
|
*
|
||||||
|
* @param {Object} target
|
||||||
|
* An observable object that either supports on/off or
|
||||||
|
* addEventListener/removeEventListener
|
||||||
|
* @param {String} eventName
|
||||||
|
* @param {Number} numTimes
|
||||||
|
* Number of deliveries to wait for.
|
||||||
|
* @param {Boolean} useCapture
|
||||||
|
* Optional, for addEventListener/removeEventListener
|
||||||
|
* @return A promise that resolves when the event has been handled
|
||||||
|
*/
|
||||||
|
function waitForNEvents(target, eventName, numTimes, useCapture = false) {
|
||||||
|
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||||
|
|
||||||
|
let deferred = promise.defer();
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let [add, remove] of [
|
||||||
|
["addEventListener", "removeEventListener"],
|
||||||
|
["addListener", "removeListener"],
|
||||||
|
["on", "off"]
|
||||||
|
]) {
|
||||||
|
if ((add in target) && (remove in target)) {
|
||||||
|
target[add](eventName, function onEvent(...aArgs) {
|
||||||
|
if (++count == numTimes) {
|
||||||
|
target[remove](eventName, onEvent, useCapture);
|
||||||
|
deferred.resolve.apply(deferred, aArgs);
|
||||||
|
}
|
||||||
|
}, useCapture);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for eventName on target.
|
* Wait for eventName on target.
|
||||||
*
|
*
|
||||||
@ -318,25 +356,7 @@ function openRuleView() {
|
|||||||
* @return A promise that resolves when the event has been handled
|
* @return A promise that resolves when the event has been handled
|
||||||
*/
|
*/
|
||||||
function once(target, eventName, useCapture=false) {
|
function once(target, eventName, useCapture=false) {
|
||||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
return waitForNEvents(target, eventName, 1, useCapture);
|
||||||
|
|
||||||
let deferred = promise.defer();
|
|
||||||
|
|
||||||
for (let [add, remove] of [
|
|
||||||
["addEventListener", "removeEventListener"],
|
|
||||||
["addListener", "removeListener"],
|
|
||||||
["on", "off"]
|
|
||||||
]) {
|
|
||||||
if ((add in target) && (remove in target)) {
|
|
||||||
target[add](eventName, function onEvent(...aArgs) {
|
|
||||||
target[remove](eventName, onEvent, useCapture);
|
|
||||||
deferred.resolve.apply(deferred, aArgs);
|
|
||||||
}, useCapture);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,8 +14,9 @@ const {Class} = require("sdk/core/heritage");
|
|||||||
const {LongStringActor} = require("devtools/server/actors/string");
|
const {LongStringActor} = require("devtools/server/actors/string");
|
||||||
const {PSEUDO_ELEMENT_SET} = require("devtools/shared/styleinspector/css-logic");
|
const {PSEUDO_ELEMENT_SET} = require("devtools/shared/styleinspector/css-logic");
|
||||||
|
|
||||||
// This will add the "stylesheet" actor type for protocol.js to recognize
|
// This will also add the "stylesheet" actor type for protocol.js to recognize
|
||||||
require("devtools/server/actors/stylesheets");
|
const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} =
|
||||||
|
require("devtools/server/actors/stylesheets");
|
||||||
|
|
||||||
loader.lazyGetter(this, "CssLogic", () => {
|
loader.lazyGetter(this, "CssLogic", () => {
|
||||||
return require("devtools/shared/styleinspector/css-logic").CssLogic;
|
return require("devtools/shared/styleinspector/css-logic").CssLogic;
|
||||||
@ -24,6 +25,10 @@ loader.lazyGetter(this, "DOMUtils", () => {
|
|||||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loader.lazyGetter(this, "RuleRewriter", () => {
|
||||||
|
return require("devtools/client/styleinspector/css-parsing-utils").RuleRewriter;
|
||||||
|
});
|
||||||
|
|
||||||
// The PageStyle actor flattens the DOM CSS objects a little bit, merging
|
// 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
|
// 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.
|
// but no associated rule) we fake a rule with the following style id.
|
||||||
@ -128,6 +133,13 @@ types.addDictType("fontface", {
|
|||||||
var PageStyleActor = protocol.ActorClass({
|
var PageStyleActor = protocol.ActorClass({
|
||||||
typeName: "pagestyle",
|
typeName: "pagestyle",
|
||||||
|
|
||||||
|
events: {
|
||||||
|
"stylesheet-updated": {
|
||||||
|
type: "styleSheetUpdated",
|
||||||
|
styleSheet: Arg(0, "stylesheet")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a PageStyleActor.
|
* Create a PageStyleActor.
|
||||||
*
|
*
|
||||||
@ -151,9 +163,12 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
|
|
||||||
this.onFrameUnload = this.onFrameUnload.bind(this);
|
this.onFrameUnload = this.onFrameUnload.bind(this);
|
||||||
events.on(this.inspector.tabActor, "will-navigate", this.onFrameUnload);
|
events.on(this.inspector.tabActor, "will-navigate", this.onFrameUnload);
|
||||||
|
|
||||||
|
this._styleApplied = this._styleApplied.bind(this);
|
||||||
|
this._watchedSheets = new Set();
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function () {
|
destroy: function() {
|
||||||
if (!this.walker) {
|
if (!this.walker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -164,6 +179,11 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
this.refMap = null;
|
this.refMap = null;
|
||||||
this.cssLogic = null;
|
this.cssLogic = null;
|
||||||
this._styleElement = null;
|
this._styleElement = null;
|
||||||
|
|
||||||
|
for (let sheet of this._watchedSheets) {
|
||||||
|
sheet.off("style-applied", this._styleApplied);
|
||||||
|
}
|
||||||
|
this._watchedSheets.clear();
|
||||||
},
|
},
|
||||||
|
|
||||||
get conn() {
|
get conn() {
|
||||||
@ -182,11 +202,22 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
// getApplied method calls cssLogic.highlight(node) to recreate the
|
// getApplied method calls cssLogic.highlight(node) to recreate the
|
||||||
// style cache. Clients requesting getApplied from actors that have not
|
// style cache. Clients requesting getApplied from actors that have not
|
||||||
// been fixed must make sure cssLogic.highlight(node) was called before.
|
// been fixed must make sure cssLogic.highlight(node) was called before.
|
||||||
getAppliedCreatesStyleCache: true
|
getAppliedCreatesStyleCache: true,
|
||||||
|
// Whether addNewRule accepts the editAuthored argument.
|
||||||
|
authoredStyles: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a style sheet is updated.
|
||||||
|
*/
|
||||||
|
_styleApplied: function(kind, styleSheet) {
|
||||||
|
if (kind === UPDATE_GENERAL) {
|
||||||
|
events.emit(this, "stylesheet-updated", styleSheet);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return or create a StyleRuleActor for the given item.
|
* Return or create a StyleRuleActor for the given item.
|
||||||
* @param item Either a CSSStyleRule or a DOM element.
|
* @param item Either a CSSStyleRule or a DOM element.
|
||||||
@ -202,6 +233,21 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
return actor;
|
return actor;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the association between a StyleRuleActor and its
|
||||||
|
* corresponding item. This is used when a StyleRuleActor updates
|
||||||
|
* as style sheet and starts using a new rule.
|
||||||
|
*
|
||||||
|
* @param oldItem The old association; either a CSSStyleRule or a
|
||||||
|
* DOM element.
|
||||||
|
* @param item Either a CSSStyleRule or a DOM element.
|
||||||
|
* @param actor a StyleRuleActor
|
||||||
|
*/
|
||||||
|
updateStyleRef: function(oldItem, item, actor) {
|
||||||
|
this.refMap.delete(oldItem);
|
||||||
|
this.refMap.set(item, actor);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return or create a StyleSheetActor for the given nsIDOMCSSStyleSheet.
|
* Return or create a StyleSheetActor for the given nsIDOMCSSStyleSheet.
|
||||||
* @param {DOMStyleSheet} sheet
|
* @param {DOMStyleSheet} sheet
|
||||||
@ -212,6 +258,10 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
_sheetRef: function(sheet) {
|
_sheetRef: function(sheet) {
|
||||||
let tabActor = this.inspector.tabActor;
|
let tabActor = this.inspector.tabActor;
|
||||||
let actor = tabActor.createStyleSheetActor(sheet);
|
let actor = tabActor.createStyleSheetActor(sheet);
|
||||||
|
if (!this._watchedSheets.has(actor)) {
|
||||||
|
this._watchedSheets.add(actor);
|
||||||
|
actor.on("style-applied", this._styleApplied);
|
||||||
|
}
|
||||||
return actor;
|
return actor;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -524,7 +574,7 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
* `matchedSelectors`: Include an array of specific selectors that
|
* `matchedSelectors`: Include an array of specific selectors that
|
||||||
* caused this rule to match its node.
|
* caused this rule to match its node.
|
||||||
*/
|
*/
|
||||||
getApplied: method(function(node, options) {
|
getApplied: method(Task.async(function*(node, options) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return {entries: [], rules: [], sheets: []};
|
return {entries: [], rules: [], sheets: []};
|
||||||
}
|
}
|
||||||
@ -532,8 +582,14 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
this.cssLogic.highlight(node.rawNode);
|
this.cssLogic.highlight(node.rawNode);
|
||||||
let entries = [];
|
let entries = [];
|
||||||
entries = entries.concat(this._getAllElementRules(node, undefined, options));
|
entries = entries.concat(this._getAllElementRules(node, undefined, options));
|
||||||
return this.getAppliedProps(node, entries, options);
|
|
||||||
}, {
|
let result = this.getAppliedProps(node, entries, options);
|
||||||
|
for (let rule of result.rules) {
|
||||||
|
// See the comment in |form| to understand this.
|
||||||
|
yield rule.getAuthoredCssText();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}), {
|
||||||
request: {
|
request: {
|
||||||
node: Arg(0, "domnode"),
|
node: Arg(0, "domnode"),
|
||||||
inherited: Option(1, "boolean"),
|
inherited: Option(1, "boolean"),
|
||||||
@ -905,12 +961,17 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new rule, and returns the new StyleRuleActor.
|
* 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
|
* @param {String} pseudoClasses The list of pseudo classes to append to the
|
||||||
* new selector.
|
* new selector.
|
||||||
* @returns StyleRuleActor of the new rule
|
* @param {Boolean} editAuthored
|
||||||
|
* True if the selector should be updated by editing the
|
||||||
|
* authored text; false if the selector should be updated via
|
||||||
|
* CSSOM.
|
||||||
|
* @returns {StyleRuleActor} the new rule
|
||||||
*/
|
*/
|
||||||
addNewRule: method(function(node, pseudoClasses) {
|
addNewRule: method(Task.async(function*(node, pseudoClasses,
|
||||||
|
editAuthored = false) {
|
||||||
let style = this.styleElement;
|
let style = this.styleElement;
|
||||||
let sheet = style.sheet;
|
let sheet = style.sheet;
|
||||||
let cssRules = sheet.cssRules;
|
let cssRules = sheet.cssRules;
|
||||||
@ -930,11 +991,22 @@ var PageStyleActor = protocol.ActorClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let index = sheet.insertRule(selector + " {}", cssRules.length);
|
let index = sheet.insertRule(selector + " {}", cssRules.length);
|
||||||
return this.getNewAppliedProps(node, cssRules.item(index));
|
|
||||||
}, {
|
// If inserting the rule succeeded, go ahead and edit the source
|
||||||
|
// text if requested.
|
||||||
|
if (editAuthored) {
|
||||||
|
let sheetActor = this._sheetRef(sheet);
|
||||||
|
let {str: authoredText} = yield sheetActor.getText();
|
||||||
|
authoredText += "\n" + selector + " {\n" + "}";
|
||||||
|
yield sheetActor.update(authoredText, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getNewAppliedProps(node, sheet.cssRules.item(index));
|
||||||
|
}), {
|
||||||
request: {
|
request: {
|
||||||
node: Arg(0, "domnode"),
|
node: Arg(0, "domnode"),
|
||||||
pseudoClasses: Arg(1, "nullable:array:string")
|
pseudoClasses: Arg(1, "nullable:array:string"),
|
||||||
|
editAuthored: Arg(2, "boolean")
|
||||||
},
|
},
|
||||||
response: RetVal("appliedStylesReturn")
|
response: RetVal("appliedStylesReturn")
|
||||||
}),
|
}),
|
||||||
@ -966,6 +1038,10 @@ var PageStyleFront = protocol.FrontClass(PageStyleActor, {
|
|||||||
return this.inspector.walker;
|
return this.inspector.walker;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get supportsAuthoredStyles() {
|
||||||
|
return this._form.traits && this._form.traits.authoredStyles;
|
||||||
|
},
|
||||||
|
|
||||||
getMatchedSelectors: protocol.custom(function(node, property, options) {
|
getMatchedSelectors: protocol.custom(function(node, property, options) {
|
||||||
return this._getMatchedSelectors(node, property, options).then(ret => {
|
return this._getMatchedSelectors(node, property, options).then(ret => {
|
||||||
return ret.matched;
|
return ret.matched;
|
||||||
@ -989,7 +1065,13 @@ var PageStyleFront = protocol.FrontClass(PageStyleActor, {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
addNewRule: protocol.custom(function(node, pseudoClasses) {
|
addNewRule: protocol.custom(function(node, pseudoClasses) {
|
||||||
return this._addNewRule(node, pseudoClasses).then(ret => {
|
let addPromise;
|
||||||
|
if (this.supportsAuthoredStyles) {
|
||||||
|
addPromise = this._addNewRule(node, pseudoClasses, true);
|
||||||
|
} else {
|
||||||
|
addPromise = this._addNewRule(node, pseudoClasses);
|
||||||
|
}
|
||||||
|
return addPromise.then(ret => {
|
||||||
return ret.entries[0];
|
return ret.entries[0];
|
||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
@ -1007,19 +1089,34 @@ var PageStyleFront = protocol.FrontClass(PageStyleActor, {
|
|||||||
*/
|
*/
|
||||||
var StyleRuleActor = protocol.ActorClass({
|
var StyleRuleActor = protocol.ActorClass({
|
||||||
typeName: "domstylerule",
|
typeName: "domstylerule",
|
||||||
|
|
||||||
|
events: {
|
||||||
|
"location-changed": {
|
||||||
|
type: "locationChanged",
|
||||||
|
line: Arg(0, "number"),
|
||||||
|
column: Arg(1, "number")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
initialize: function(pageStyle, item) {
|
initialize: function(pageStyle, item) {
|
||||||
protocol.Actor.prototype.initialize.call(this, null);
|
protocol.Actor.prototype.initialize.call(this, null);
|
||||||
this.pageStyle = pageStyle;
|
this.pageStyle = pageStyle;
|
||||||
this.rawStyle = item.style;
|
this.rawStyle = item.style;
|
||||||
|
this._parentSheet = null;
|
||||||
|
this._onStyleApplied = this._onStyleApplied.bind(this);
|
||||||
|
|
||||||
if (item instanceof (Ci.nsIDOMCSSRule)) {
|
if (item instanceof (Ci.nsIDOMCSSRule)) {
|
||||||
this.type = item.type;
|
this.type = item.type;
|
||||||
this.rawRule = item;
|
this.rawRule = item;
|
||||||
if ((this.rawRule instanceof Ci.nsIDOMCSSStyleRule ||
|
if ((this.rawRule instanceof Ci.nsIDOMCSSStyleRule ||
|
||||||
this.rawRule instanceof Ci.nsIDOMMozCSSKeyframeRule) &&
|
this.rawRule instanceof Ci.nsIDOMMozCSSKeyframeRule) &&
|
||||||
this.rawRule.parentStyleSheet) {
|
this.rawRule.parentStyleSheet) {
|
||||||
this.line = DOMUtils.getRuleLine(this.rawRule);
|
this.line = DOMUtils.getRelativeRuleLine(this.rawRule);
|
||||||
this.column = DOMUtils.getRuleColumn(this.rawRule);
|
this.column = DOMUtils.getRuleColumn(this.rawRule);
|
||||||
|
this._parentSheet = this.rawRule.parentStyleSheet;
|
||||||
|
this._computeRuleIndex();
|
||||||
|
this.sheetActor = this.pageStyle._sheetRef(this._parentSheet);
|
||||||
|
this.sheetActor.on("style-applied", this._onStyleApplied);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fake a rule
|
// Fake a rule
|
||||||
@ -1047,6 +1144,9 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
this.pageStyle = null;
|
this.pageStyle = null;
|
||||||
this.rawNode = null;
|
this.rawNode = null;
|
||||||
this.rawRule = null;
|
this.rawRule = null;
|
||||||
|
if (this.sheetActor) {
|
||||||
|
this.sheetActor.off("style-applied", this._onStyleApplied);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Objects returned by this actor are owned by the PageStyleActor
|
// Objects returned by this actor are owned by the PageStyleActor
|
||||||
@ -1055,6 +1155,17 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
return this.pageStyle;
|
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 &&
|
||||||
|
this._parentSheet.href !== "about:PreferenceStyleSheet");
|
||||||
|
},
|
||||||
|
|
||||||
getDocument: function(sheet) {
|
getDocument: function(sheet) {
|
||||||
let document;
|
let document;
|
||||||
|
|
||||||
@ -1085,6 +1196,9 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
// Whether the style rule actor implements the modifySelector2 method
|
// Whether the style rule actor implements the modifySelector2 method
|
||||||
// that allows for unmatched rule to be added
|
// that allows for unmatched rule to be added
|
||||||
modifySelectorUnmatched: true,
|
modifySelectorUnmatched: true,
|
||||||
|
// Whether the style rule actor implements the setRuleText
|
||||||
|
// method.
|
||||||
|
canSetRuleText: this.canSetRuleText,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1102,10 +1216,17 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.rawRule.parentStyleSheet) {
|
if (this._parentSheet) {
|
||||||
form.parentStyleSheet = this.pageStyle._sheetRef(this.rawRule.parentStyleSheet).actorID;
|
form.parentStyleSheet = this.pageStyle._sheetRef(this._parentSheet).actorID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// One tricky thing here is that other methods in this actor must
|
||||||
|
// ensure that authoredText has been set before |form| is called.
|
||||||
|
// This has to be treated specially, for now, because we cannot
|
||||||
|
// synchronously compute the authored text, but |form| also cannot
|
||||||
|
// return a promise. See bug 1205868.
|
||||||
|
form.authoredText = this.authoredText;
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case Ci.nsIDOMCSSRule.STYLE_RULE:
|
case Ci.nsIDOMCSSRule.STYLE_RULE:
|
||||||
form.selectors = CssLogic.getSelectors(this.rawRule);
|
form.selectors = CssLogic.getSelectors(this.rawRule);
|
||||||
@ -1139,7 +1260,125 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify a rule's properties. Passed an array of modifications:
|
* Send an event notifying that the location of the rule has
|
||||||
|
* changed.
|
||||||
|
*
|
||||||
|
* @param {Number} line the new line number
|
||||||
|
* @param {Number} column the new column number
|
||||||
|
*/
|
||||||
|
_notifyLocationChanged: function(line, column) {
|
||||||
|
events.emit(this, "location-changed", line, column);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the index of this actor's raw rule in its parent style
|
||||||
|
* sheet.
|
||||||
|
*/
|
||||||
|
_computeRuleIndex: function() {
|
||||||
|
let rule = this.rawRule;
|
||||||
|
let cssRules = this._parentSheet.cssRules;
|
||||||
|
this._ruleIndex = -1;
|
||||||
|
for (let i = 0; i < cssRules.length; i++) {
|
||||||
|
if (rule === cssRules.item(i)) {
|
||||||
|
this._ruleIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is attached to the parent style sheet actor's
|
||||||
|
* "style-applied" event.
|
||||||
|
*/
|
||||||
|
_onStyleApplied: function(kind) {
|
||||||
|
if (kind === UPDATE_GENERAL) {
|
||||||
|
// A general change means that the rule actors are invalidated,
|
||||||
|
// so stop listening to events now.
|
||||||
|
if (this.sheetActor) {
|
||||||
|
this.sheetActor.off("style-applied", this._onStyleApplied);
|
||||||
|
}
|
||||||
|
} else if (this._ruleIndex >= 0) {
|
||||||
|
// The sheet was updated by this actor, in a way that preserves
|
||||||
|
// the rules. Now, recompute our new rule from the style sheet,
|
||||||
|
// so that we aren't left with a reference to a dangling rule.
|
||||||
|
let oldRule = this.rawRule;
|
||||||
|
this.rawRule = this._parentSheet.cssRules[this._ruleIndex];
|
||||||
|
// Also tell the page style so that future calls to _styleRef
|
||||||
|
// return the same StyleRuleActor.
|
||||||
|
this.pageStyle.updateStyleRef(oldRule, this.rawRule, this);
|
||||||
|
let line = DOMUtils.getRelativeRuleLine(this.rawRule);
|
||||||
|
let column = DOMUtils.getRuleColumn(this.rawRule);
|
||||||
|
if (line !== this.line || column !== this.column) {
|
||||||
|
this._notifyLocationChanged(line, column);
|
||||||
|
}
|
||||||
|
this.line = line;
|
||||||
|
this.column = column;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a promise that resolves to the authored form of a rule's
|
||||||
|
* text, if available. If the authored form is not available, the
|
||||||
|
* returned promise simply resolves to the empty string. If the
|
||||||
|
* authored form is available, this also sets |this.authoredText|.
|
||||||
|
* The authored text will include invalid and otherwise ignored
|
||||||
|
* properties.
|
||||||
|
*/
|
||||||
|
getAuthoredCssText: function() {
|
||||||
|
if (!this.canSetRuleText ||
|
||||||
|
(this.type !== Ci.nsIDOMCSSRule.STYLE_RULE &&
|
||||||
|
this.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE)) {
|
||||||
|
return promise.resolve("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.authoredText === "string") {
|
||||||
|
return promise.resolve(this.authoredText);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parentStyleSheet =
|
||||||
|
this.pageStyle._sheetRef(this._parentSheet);
|
||||||
|
return parentStyleSheet.getText().then((longStr) => {
|
||||||
|
let cssText = longStr.str;
|
||||||
|
let {text} = getRuleText(cssText, this.line, this.column);
|
||||||
|
|
||||||
|
// Cache the result on the rule actor to avoid parsing again next time
|
||||||
|
this.authoredText = text;
|
||||||
|
return this.authoredText;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)) {
|
||||||
|
throw new Error("invalid call to setRuleText");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
this.authoredText = newText;
|
||||||
|
yield parentStyleSheet.update(cssText, false, UPDATE_PRESERVING_RULES);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}), {
|
||||||
|
request: { modification: Arg(0, "string") },
|
||||||
|
response: { rule: RetVal("domstylerule") }
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify a rule's properties. Passed an array of modifications:
|
||||||
* {
|
* {
|
||||||
* type: "set",
|
* type: "set",
|
||||||
* name: <string>,
|
* name: <string>,
|
||||||
@ -1163,7 +1402,7 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
if (this.rawNode) {
|
if (this.rawNode) {
|
||||||
document = this.rawNode.ownerDocument;
|
document = this.rawNode.ownerDocument;
|
||||||
} else {
|
} else {
|
||||||
let parentStyleSheet = this.rawRule.parentStyleSheet;
|
let parentStyleSheet = this._parentSheet;
|
||||||
while (parentStyleSheet.ownerRule &&
|
while (parentStyleSheet.ownerRule &&
|
||||||
parentStyleSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) {
|
parentStyleSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) {
|
||||||
parentStyleSheet = parentStyleSheet.ownerRule.parentStyleSheet;
|
parentStyleSheet = parentStyleSheet.ownerRule.parentStyleSheet;
|
||||||
@ -1196,39 +1435,61 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
* current rule. Returns the newly inserted css rule or null if the rule is
|
* current rule. Returns the newly inserted css rule or null if the rule is
|
||||||
* unsuccessfully inserted to the parent style sheet.
|
* unsuccessfully inserted to the parent style sheet.
|
||||||
*
|
*
|
||||||
* @param string value
|
* @param {String} value
|
||||||
* The new selector value
|
* The new selector value
|
||||||
|
* @param {Boolean} editAuthored
|
||||||
|
* True if the selector should be updated by editing the
|
||||||
|
* authored text; false if the selector should be updated via
|
||||||
|
* CSSOM.
|
||||||
*
|
*
|
||||||
* @returns CSSRule
|
* @returns {CSSRule}
|
||||||
* The new CSS rule added
|
* The new CSS rule added
|
||||||
*/
|
*/
|
||||||
_addNewSelector: function(value) {
|
_addNewSelector: Task.async(function*(value, editAuthored) {
|
||||||
let rule = this.rawRule;
|
let rule = this.rawRule;
|
||||||
let parentStyleSheet = rule.parentStyleSheet;
|
let parentStyleSheet = this._parentSheet;
|
||||||
let cssRules = parentStyleSheet.cssRules;
|
|
||||||
let cssText = rule.cssText;
|
|
||||||
let selectorText = rule.selectorText;
|
|
||||||
|
|
||||||
for (let i = 0; i < cssRules.length; i++) {
|
// We know the selector modification is ok, so if the client asked
|
||||||
if (rule === cssRules.item(i)) {
|
// for the authored text to be edited, do it now.
|
||||||
try {
|
if (editAuthored) {
|
||||||
// Inserts the new style rule into the current style sheet and
|
let document = this.getDocument(this._parentSheet);
|
||||||
// delete the current rule
|
try {
|
||||||
let ruleText = cssText.slice(selectorText.length).trim();
|
document.querySelector(value);
|
||||||
parentStyleSheet.insertRule(value + " " + ruleText, i);
|
} catch (e) {
|
||||||
parentStyleSheet.deleteRule(i + 1);
|
return null;
|
||||||
return cssRules.item(i);
|
}
|
||||||
} catch(e) {
|
|
||||||
// The selector could be invalid, or the rule could fail to insert.
|
let sheetActor = this.pageStyle._sheetRef(parentStyleSheet);
|
||||||
// If that happens, the method returns null.
|
let {str: authoredText} = yield sheetActor.getText();
|
||||||
|
let [startOffset, endOffset] = getSelectorOffsets(authoredText, this.line,
|
||||||
|
this.column);
|
||||||
|
authoredText = authoredText.substring(0, startOffset) + value +
|
||||||
|
authoredText.substring(endOffset);
|
||||||
|
yield sheetActor.update(authoredText, false, UPDATE_PRESERVING_RULES);
|
||||||
|
} else {
|
||||||
|
let cssRules = parentStyleSheet.cssRules;
|
||||||
|
let cssText = rule.cssText;
|
||||||
|
let selectorText = rule.selectorText;
|
||||||
|
|
||||||
|
for (let i = 0; i < cssRules.length; i++) {
|
||||||
|
if (rule === cssRules.item(i)) {
|
||||||
|
try {
|
||||||
|
// Inserts the new style rule into the current style sheet and
|
||||||
|
// delete the current rule
|
||||||
|
let ruleText = cssText.slice(selectorText.length).trim();
|
||||||
|
parentStyleSheet.insertRule(value + " " + ruleText, i);
|
||||||
|
parentStyleSheet.deleteRule(i + 1);
|
||||||
|
break;
|
||||||
|
} catch(e) {
|
||||||
|
// The selector could be invalid, or the rule could fail to insert.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return parentStyleSheet.cssRules[this._ruleIndex];
|
||||||
},
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify the current rule's selector by inserting a new rule with the new
|
* Modify the current rule's selector by inserting a new rule with the new
|
||||||
@ -1243,12 +1504,12 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
* Returns a boolean if the selector in the stylesheet was modified,
|
* Returns a boolean if the selector in the stylesheet was modified,
|
||||||
* and false otherwise
|
* and false otherwise
|
||||||
*/
|
*/
|
||||||
modifySelector: method(function(value) {
|
modifySelector: method(Task.async(function*(value) {
|
||||||
if (this.type === ELEMENT_STYLE) {
|
if (this.type === ELEMENT_STYLE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let document = this.getDocument(this.rawRule.parentStyleSheet);
|
let document = this.getDocument(this._parentSheet);
|
||||||
// Extract the selector, and pseudo elements and classes
|
// Extract the selector, and pseudo elements and classes
|
||||||
let [selector, pseudoProp] = value.split(/(:{1,2}.+$)/);
|
let [selector, pseudoProp] = value.split(/(:{1,2}.+$)/);
|
||||||
let selectorElement;
|
let selectorElement;
|
||||||
@ -1262,11 +1523,11 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
// Check if the selector is valid and not the same as the original
|
// Check if the selector is valid and not the same as the original
|
||||||
// selector
|
// selector
|
||||||
if (selectorElement && this.rawRule.selectorText !== value) {
|
if (selectorElement && this.rawRule.selectorText !== value) {
|
||||||
this._addNewSelector(value);
|
yield this._addNewSelector(value, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, {
|
}), {
|
||||||
request: { selector: Arg(0, "string") },
|
request: { selector: Arg(0, "string") },
|
||||||
response: { isModified: RetVal("boolean") },
|
response: { isModified: RetVal("boolean") },
|
||||||
}),
|
}),
|
||||||
@ -1281,17 +1542,20 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
* selector matches the current element without having to refresh the whole
|
* selector matches the current element without having to refresh the whole
|
||||||
* list.
|
* list.
|
||||||
*
|
*
|
||||||
* @param DOMNode node
|
* @param {DOMNode} node
|
||||||
* The current selected element
|
* The current selected element
|
||||||
* @param string value
|
* @param {String} value
|
||||||
* The new selector value
|
* The new selector value
|
||||||
* @returns Object
|
* @param {Boolean} editAuthored
|
||||||
|
* True if the selector should be updated by editing the
|
||||||
|
* authored text; false if the selector should be updated via
|
||||||
|
* CSSOM.
|
||||||
|
* @returns {Object}
|
||||||
* Returns an object that contains the applied style properties of the
|
* Returns an object that contains the applied style properties of the
|
||||||
* new rule and a boolean indicating whether or not the new selector
|
* new rule and a boolean indicating whether or not the new selector
|
||||||
* matches the current selected element
|
* matches the current selected element
|
||||||
*/
|
*/
|
||||||
modifySelector2: method(function(node, value) {
|
modifySelector2: method(function(node, value, editAuthored = false) {
|
||||||
let isMatching = false;
|
|
||||||
let ruleProps = null;
|
let ruleProps = null;
|
||||||
|
|
||||||
if (this.type === ELEMENT_STYLE ||
|
if (this.type === ELEMENT_STYLE ||
|
||||||
@ -1299,23 +1563,40 @@ var StyleRuleActor = protocol.ActorClass({
|
|||||||
return { ruleProps, isMatching: true };
|
return { ruleProps, isMatching: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
let newCssRule = this._addNewSelector(value);
|
let selectorPromise = this._addNewSelector(value, editAuthored);
|
||||||
if (newCssRule) {
|
|
||||||
ruleProps = this.pageStyle.getNewAppliedProps(node, newCssRule);
|
if (editAuthored) {
|
||||||
|
selectorPromise = selectorPromise.then((newCssRule) => {
|
||||||
|
if (newCssRule) {
|
||||||
|
let style = this.pageStyle._styleRef(newCssRule);
|
||||||
|
// See the comment in |form| to understand this.
|
||||||
|
return style.getAuthoredCssText().then(() => newCssRule);
|
||||||
|
}
|
||||||
|
return newCssRule;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the new selector value matches the current selected element
|
return selectorPromise.then((newCssRule) => {
|
||||||
try {
|
if (newCssRule) {
|
||||||
isMatching = node.rawNode.matches(value);
|
ruleProps = this.pageStyle.getNewAppliedProps(node, newCssRule);
|
||||||
} catch(e) {
|
}
|
||||||
// This fails when value is an invalid selector.
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ruleProps, isMatching };
|
// Determine if the new selector value matches the current
|
||||||
|
// selected element
|
||||||
|
let isMatching = false;
|
||||||
|
try {
|
||||||
|
isMatching = node.rawNode.matches(value);
|
||||||
|
} catch(e) {
|
||||||
|
// This fails when value is an invalid selector.
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ruleProps, isMatching };
|
||||||
|
});
|
||||||
}, {
|
}, {
|
||||||
request: {
|
request: {
|
||||||
node: Arg(0, "domnode"),
|
node: Arg(0, "domnode"),
|
||||||
value: Arg(1, "string")
|
value: Arg(1, "string"),
|
||||||
|
editAuthored: Arg(2, "boolean")
|
||||||
},
|
},
|
||||||
response: RetVal("modifiedStylesReturn")
|
response: RetVal("modifiedStylesReturn")
|
||||||
})
|
})
|
||||||
@ -1346,9 +1627,25 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a new RuleModificationList for this node.
|
* Ensure _form is updated when location-changed is emitted.
|
||||||
|
*/
|
||||||
|
_locationChangedPre: protocol.preEvent("location-changed", function(line,
|
||||||
|
column) {
|
||||||
|
this._clearOriginalLocation();
|
||||||
|
this._form.line = line;
|
||||||
|
this._form.column = column;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new RuleModificationList or RuleRewriter for this node.
|
||||||
|
* A RuleRewriter will be returned when the rule's canSetRuleText
|
||||||
|
* trait is true; otherwise a RuleModificationList will be
|
||||||
|
* returned.
|
||||||
*/
|
*/
|
||||||
startModifyingProperties: function() {
|
startModifyingProperties: function() {
|
||||||
|
if (this.canSetRuleText) {
|
||||||
|
return new RuleRewriter(this, this.authoredText);
|
||||||
|
}
|
||||||
return new RuleModificationList(this);
|
return new RuleModificationList(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1364,6 +1661,9 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
get cssText() {
|
get cssText() {
|
||||||
return this._form.cssText;
|
return this._form.cssText;
|
||||||
},
|
},
|
||||||
|
get authoredText() {
|
||||||
|
return this._form.authoredText || this._form.cssText;
|
||||||
|
},
|
||||||
get keyText() {
|
get keyText() {
|
||||||
return this._form.keyText;
|
return this._form.keyText;
|
||||||
},
|
},
|
||||||
@ -1416,6 +1716,10 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
return this._form.traits && this._form.traits.modifySelectorUnmatched;
|
return this._form.traits && this._form.traits.modifySelectorUnmatched;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get canSetRuleText() {
|
||||||
|
return this._form.traits && this._form.traits.canSetRuleText;
|
||||||
|
},
|
||||||
|
|
||||||
get location() {
|
get location() {
|
||||||
return {
|
return {
|
||||||
source: this.parentStyleSheet,
|
source: this.parentStyleSheet,
|
||||||
@ -1425,6 +1729,10 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_clearOriginalLocation: function() {
|
||||||
|
this._originalLocation = null;
|
||||||
|
},
|
||||||
|
|
||||||
getOriginalLocation: function() {
|
getOriginalLocation: function() {
|
||||||
if (this._originalLocation) {
|
if (this._originalLocation) {
|
||||||
return promise.resolve(this._originalLocation);
|
return promise.resolve(this._originalLocation);
|
||||||
@ -1458,7 +1766,11 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
let response;
|
let response;
|
||||||
if (this.supportsModifySelectorUnmatched) {
|
if (this.supportsModifySelectorUnmatched) {
|
||||||
// If the debugee supports adding unmatched rules (post FF41)
|
// If the debugee supports adding unmatched rules (post FF41)
|
||||||
response = yield this.modifySelector2(node, value);
|
if (this.canSetRuleText) {
|
||||||
|
response = yield this.modifySelector2(node, value, true);
|
||||||
|
} else {
|
||||||
|
response = yield this.modifySelector2(node, value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
response = yield this._modifySelector(value);
|
response = yield this._modifySelector(value);
|
||||||
}
|
}
|
||||||
@ -1469,6 +1781,13 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
return response;
|
return response;
|
||||||
}), {
|
}), {
|
||||||
impl: "_modifySelector"
|
impl: "_modifySelector"
|
||||||
|
}),
|
||||||
|
|
||||||
|
setRuleText: protocol.custom(function(newText) {
|
||||||
|
this._form.authoredText = newText;
|
||||||
|
return this._setRuleText(newText);
|
||||||
|
}, {
|
||||||
|
impl: "_setRuleText"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1478,6 +1797,10 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||||||
* list of modifications that will be applied to a StyleRuleActor.
|
* list of modifications that will be applied to a StyleRuleActor.
|
||||||
* The modifications are processed in the order in which they are
|
* The modifications are processed in the order in which they are
|
||||||
* added to the RuleModificationList.
|
* added to the RuleModificationList.
|
||||||
|
*
|
||||||
|
* Objects of this type expose the same API as @see RuleRewriter.
|
||||||
|
* This lets the inspector use (mostly) the same code, regardless of
|
||||||
|
* whether the server implements setRuleText.
|
||||||
*/
|
*/
|
||||||
var RuleModificationList = Class({
|
var RuleModificationList = Class({
|
||||||
/**
|
/**
|
||||||
@ -1502,12 +1825,17 @@ var RuleModificationList = Class({
|
|||||||
/**
|
/**
|
||||||
* Add a "set" entry to the modification list.
|
* Add a "set" entry to the modification list.
|
||||||
*
|
*
|
||||||
* @param {string} name the property's name
|
* @param {Number} index index of the property in the rule.
|
||||||
* @param {string} value the property's value
|
* This can be -1 in the case where
|
||||||
* @param {string} priority the property's priority, either the empty
|
* the rule does not support setRuleText;
|
||||||
|
* generally for setting properties
|
||||||
|
* on an element's style.
|
||||||
|
* @param {String} name the property's name
|
||||||
|
* @param {String} value the property's value
|
||||||
|
* @param {String} priority the property's priority, either the empty
|
||||||
* string or "important"
|
* string or "important"
|
||||||
*/
|
*/
|
||||||
setProperty: function(name, value, priority) {
|
setProperty: function(index, name, value, priority) {
|
||||||
this.modifications.push({
|
this.modifications.push({
|
||||||
type: "set",
|
type: "set",
|
||||||
name: name,
|
name: name,
|
||||||
@ -1519,14 +1847,73 @@ var RuleModificationList = Class({
|
|||||||
/**
|
/**
|
||||||
* Add a "remove" entry to the modification list.
|
* Add a "remove" entry to the modification list.
|
||||||
*
|
*
|
||||||
* @param {string} name the name of the property to remove
|
* @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 the name of the property to remove
|
||||||
*/
|
*/
|
||||||
removeProperty: function(name) {
|
removeProperty: function(index, name) {
|
||||||
this.modifications.push({
|
this.modifications.push({
|
||||||
type: "remove",
|
type: "remove",
|
||||||
name: name
|
name: name
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a property. This implementation acts like
|
||||||
|
* |removeProperty|, because |setRuleText| is not available.
|
||||||
|
*
|
||||||
|
* @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 current name of the property
|
||||||
|
* @param {String} newName new name of the property
|
||||||
|
*/
|
||||||
|
renameProperty: function(index, name, newName) {
|
||||||
|
this.removeProperty(index, name);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable a property. This implementation acts like
|
||||||
|
* |removeProperty| when disabling, or a no-op when enabling,
|
||||||
|
* because |setRuleText| is not available.
|
||||||
|
*
|
||||||
|
* @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 current name of the property
|
||||||
|
* @param {Boolean} isEnabled true if the property should be enabled;
|
||||||
|
* false if it should be disabled
|
||||||
|
*/
|
||||||
|
setPropertyEnabled: function(index, name, isEnabled) {
|
||||||
|
if (!isEnabled) {
|
||||||
|
this.removeProperty(index, name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new property. This implementation does nothing, because
|
||||||
|
* |setRuleText| is not available.
|
||||||
|
*
|
||||||
|
* @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"
|
||||||
|
*/
|
||||||
|
createProperty: function(index, name, value, priority) {
|
||||||
|
// Nothing.
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1659,6 +2046,47 @@ function getRuleText(initialText, line, column) {
|
|||||||
|
|
||||||
exports.getRuleText = getRuleText;
|
exports.getRuleText = getRuleText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the start and end offsets of a rule's selector text, given
|
||||||
|
* the CSS text and the line and column at which the rule begins.
|
||||||
|
* @param {String} initialText
|
||||||
|
* @param {Number} line (1-indexed)
|
||||||
|
* @param {Number} column (1-indexed)
|
||||||
|
* @return {array} An array with two elements: [startOffset, endOffset].
|
||||||
|
* The elements mark the bounds in |initialText| of
|
||||||
|
* the CSS rule's selector.
|
||||||
|
*/
|
||||||
|
function getSelectorOffsets(initialText, line, column) {
|
||||||
|
if (typeof line === "undefined" || typeof column === "undefined") {
|
||||||
|
throw new Error("Location information is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
let {offset: textOffset, text} =
|
||||||
|
getTextAtLineColumn(initialText, line, column);
|
||||||
|
let lexer = DOMUtils.getCSSLexer(text);
|
||||||
|
|
||||||
|
// Search forward for the opening brace.
|
||||||
|
let endOffset;
|
||||||
|
while (true) {
|
||||||
|
let token = lexer.nextToken();
|
||||||
|
if (!token) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (token.tokenType === "symbol" && token.text === "{") {
|
||||||
|
if (endOffset === undefined) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return [textOffset, textOffset + endOffset];
|
||||||
|
}
|
||||||
|
// Preserve comments and whitespace just before the "{".
|
||||||
|
if (token.tokenType !== "comment" && token.tokenType !== "whitespace") {
|
||||||
|
endOffset = token.endOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("could not find bounds of rule");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the offset and substring of |text| that starts at the given
|
* Return the offset and substring of |text| that starts at the given
|
||||||
* line and column.
|
* line and column.
|
||||||
|
@ -23,6 +23,11 @@ const {SourceMapConsumer} = require("source-map");
|
|||||||
|
|
||||||
loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/styleinspector/css-logic").CssLogic);
|
loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/styleinspector/css-logic").CssLogic);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getIndentationFromPrefs,
|
||||||
|
getIndentationFromString
|
||||||
|
} = require("devtools/shared/shared/indentation");
|
||||||
|
|
||||||
var TRANSITION_CLASS = "moz-styleeditor-transitioning";
|
var TRANSITION_CLASS = "moz-styleeditor-transitioning";
|
||||||
var TRANSITION_DURATION_MS = 500;
|
var TRANSITION_DURATION_MS = 500;
|
||||||
var TRANSITION_BUFFER_MS = 1000;
|
var TRANSITION_BUFFER_MS = 1000;
|
||||||
@ -40,6 +45,22 @@ var LOAD_ERROR = "error-load";
|
|||||||
types.addActorType("stylesheet");
|
types.addActorType("stylesheet");
|
||||||
types.addActorType("originalsource");
|
types.addActorType("originalsource");
|
||||||
|
|
||||||
|
// The possible kinds of style-applied events.
|
||||||
|
// UPDATE_PRESERVING_RULES means that the update is guaranteed to
|
||||||
|
// preserve the number and order of rules on the style sheet.
|
||||||
|
// UPDATE_GENERAL covers any other kind of change to the style sheet.
|
||||||
|
const UPDATE_PRESERVING_RULES = 0;
|
||||||
|
exports.UPDATE_PRESERVING_RULES = UPDATE_PRESERVING_RULES;
|
||||||
|
const UPDATE_GENERAL = 1;
|
||||||
|
exports.UPDATE_GENERAL = UPDATE_GENERAL;
|
||||||
|
|
||||||
|
// If the user edits a style sheet, we stash a copy of the edited text
|
||||||
|
// here, keyed by the style sheet. This way, if the tools are closed
|
||||||
|
// and then reopened, the edited text will be available. A weak map
|
||||||
|
// is used so that navigation by the user will eventually cause the
|
||||||
|
// edited text to be collected.
|
||||||
|
let modifiedStyleSheets = new WeakMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
|
* Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
|
||||||
* stylesheets of a document.
|
* stylesheets of a document.
|
||||||
@ -385,7 +406,9 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
value: Arg(1, "json")
|
value: Arg(1, "json")
|
||||||
},
|
},
|
||||||
"style-applied" : {
|
"style-applied" : {
|
||||||
type: "styleApplied"
|
type: "styleApplied",
|
||||||
|
kind: Arg(0, "number"),
|
||||||
|
styleSheet: Arg(1, "stylesheet")
|
||||||
},
|
},
|
||||||
"media-rules-changed" : {
|
"media-rules-changed" : {
|
||||||
type: "mediaRulesChanged",
|
type: "mediaRulesChanged",
|
||||||
@ -597,6 +620,12 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
return promise.resolve(this.text);
|
return promise.resolve(this.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cssText = modifiedStyleSheets.get(this.rawSheet);
|
||||||
|
if (cssText !== undefined) {
|
||||||
|
this.text = cssText;
|
||||||
|
return promise.resolve(cssText);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.href) {
|
if (!this.href) {
|
||||||
// this is an inline <style> sheet
|
// this is an inline <style> sheet
|
||||||
let content = this.ownerNode.textContent;
|
let content = this.ownerNode.textContent;
|
||||||
@ -872,19 +901,22 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
* @param {object} request
|
* @param {object} request
|
||||||
* 'text' - new text
|
* 'text' - new text
|
||||||
* 'transition' - whether to do CSS transition for change.
|
* 'transition' - whether to do CSS transition for change.
|
||||||
|
* 'kind' - either UPDATE_PRESERVING_RULES or UPDATE_GENERAL
|
||||||
*/
|
*/
|
||||||
update: method(function(text, transition) {
|
update: method(function(text, transition, kind = UPDATE_GENERAL) {
|
||||||
DOMUtils.parseStyleSheet(this.rawSheet, text);
|
DOMUtils.parseStyleSheet(this.rawSheet, text);
|
||||||
|
|
||||||
|
modifiedStyleSheets.set(this.rawSheet, text);
|
||||||
|
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
|
||||||
this._notifyPropertyChanged("ruleCount");
|
this._notifyPropertyChanged("ruleCount");
|
||||||
|
|
||||||
if (transition) {
|
if (transition) {
|
||||||
this._insertTransistionRule();
|
this._insertTransistionRule(kind);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
events.emit(this, "style-applied");
|
events.emit(this, "style-applied", kind, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getMediaRules().then((rules) => {
|
this._getMediaRules().then((rules) => {
|
||||||
@ -901,7 +933,7 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
* Insert a catch-all transition rule into the document. Set a timeout
|
* Insert a catch-all transition rule into the document. Set a timeout
|
||||||
* to remove the rule after a certain time.
|
* to remove the rule after a certain time.
|
||||||
*/
|
*/
|
||||||
_insertTransistionRule: function() {
|
_insertTransistionRule: function(kind) {
|
||||||
this.document.documentElement.classList.add(TRANSITION_CLASS);
|
this.document.documentElement.classList.add(TRANSITION_CLASS);
|
||||||
|
|
||||||
// We always add the rule since we've just reset all the rules
|
// We always add the rule since we've just reset all the rules
|
||||||
@ -910,7 +942,7 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
// Set up clean up and commit after transition duration (+buffer)
|
// Set up clean up and commit after transition duration (+buffer)
|
||||||
// @see _onTransitionEnd
|
// @see _onTransitionEnd
|
||||||
this.window.clearTimeout(this._transitionTimeout);
|
this.window.clearTimeout(this._transitionTimeout);
|
||||||
this._transitionTimeout = this.window.setTimeout(this._onTransitionEnd.bind(this),
|
this._transitionTimeout = this.window.setTimeout(this._onTransitionEnd.bind(this, kind),
|
||||||
TRANSITION_DURATION_MS + TRANSITION_BUFFER_MS);
|
TRANSITION_DURATION_MS + TRANSITION_BUFFER_MS);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -918,7 +950,7 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
* This cleans up class and rule added for transition effect and then
|
* This cleans up class and rule added for transition effect and then
|
||||||
* notifies that the style has been applied.
|
* notifies that the style has been applied.
|
||||||
*/
|
*/
|
||||||
_onTransitionEnd: function()
|
_onTransitionEnd: function(kind)
|
||||||
{
|
{
|
||||||
this.document.documentElement.classList.remove(TRANSITION_CLASS);
|
this.document.documentElement.classList.remove(TRANSITION_CLASS);
|
||||||
|
|
||||||
@ -928,7 +960,7 @@ var StyleSheetActor = protocol.ActorClass({
|
|||||||
this.rawSheet.deleteRule(index);
|
this.rawSheet.deleteRule(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
events.emit(this, "style-applied");
|
events.emit(this, "style-applied", kind, this);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -981,6 +1013,29 @@ var StyleSheetFront = protocol.FrontClass(StyleSheetActor, {
|
|||||||
},
|
},
|
||||||
get ruleCount() {
|
get ruleCount() {
|
||||||
return this._form.ruleCount;
|
return this._form.ruleCount;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the indentation to use for edits to this style sheet.
|
||||||
|
*
|
||||||
|
* @return {Promise} A promise that will resolve to a string that
|
||||||
|
* should be used to indent a block in this style sheet.
|
||||||
|
*/
|
||||||
|
guessIndentation: function() {
|
||||||
|
let prefIndent = getIndentationFromPrefs();
|
||||||
|
if (prefIndent) {
|
||||||
|
let {indentUnit, indentWithTabs} = prefIndent;
|
||||||
|
return promise.resolve(indentWithTabs ? "\t" : " ".repeat(indentUnit));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.spawn(function*() {
|
||||||
|
let longStr = yield this.getText();
|
||||||
|
let source = yield longStr.string();
|
||||||
|
|
||||||
|
let {indentUnit, indentWithTabs} = getIndentationFromString(source);
|
||||||
|
|
||||||
|
return indentWithTabs ? "\t" : " ".repeat(indentUnit);
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,13 +52,13 @@ addTest(function modifyProperties() {
|
|||||||
let changes = elementStyle.startModifyingProperties();
|
let changes = elementStyle.startModifyingProperties();
|
||||||
|
|
||||||
// Change an existing property...
|
// Change an existing property...
|
||||||
changes.setProperty("color", "black");
|
changes.setProperty(-1, "color", "black");
|
||||||
// Create a new property
|
// Create a new property
|
||||||
changes.setProperty("background-color", "green");
|
changes.setProperty(-1, "background-color", "green");
|
||||||
|
|
||||||
// Create a new property and then change it immediately.
|
// Create a new property and then change it immediately.
|
||||||
changes.setProperty("border", "1px solid black");
|
changes.setProperty(-1, "border", "1px solid black");
|
||||||
changes.setProperty("border", "2px solid black");
|
changes.setProperty(-1, "border", "2px solid black");
|
||||||
|
|
||||||
return changes.apply();
|
return changes.apply();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@ -67,9 +67,9 @@ addTest(function modifyProperties() {
|
|||||||
|
|
||||||
// Remove all the properties
|
// Remove all the properties
|
||||||
let changes = elementStyle.startModifyingProperties();
|
let changes = elementStyle.startModifyingProperties();
|
||||||
changes.removeProperty("color");
|
changes.removeProperty(-1, "color");
|
||||||
changes.removeProperty("background-color");
|
changes.removeProperty(-1, "background-color");
|
||||||
changes.removeProperty("border");
|
changes.removeProperty(-1, "border");
|
||||||
|
|
||||||
return changes.apply();
|
return changes.apply();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
@ -853,7 +853,7 @@ pref("devtools.debugger.forbid-certified-apps", true);
|
|||||||
pref("devtools.apps.forbidden-permissions", "embed-apps,engineering-mode,embed-widgets");
|
pref("devtools.apps.forbidden-permissions", "embed-apps,engineering-mode,embed-widgets");
|
||||||
|
|
||||||
// DevTools default color unit
|
// DevTools default color unit
|
||||||
pref("devtools.defaultColorUnit", "hex");
|
pref("devtools.defaultColorUnit", "authored");
|
||||||
|
|
||||||
// Used for devtools debugging
|
// Used for devtools debugging
|
||||||
pref("devtools.dump.emit", false);
|
pref("devtools.dump.emit", false);
|
||||||
|
Loading…
Reference in New Issue
Block a user