Bug 740603 - "Copy Rule" in the rule view includes expanded shorthand properties; r=dcamp

This commit is contained in:
Michael Ratcliffe 2012-04-16 12:20:46 +01:00
parent ea474aecfc
commit 46b4e30246
4 changed files with 257 additions and 61 deletions

View File

@ -552,6 +552,10 @@ CssHtmlTree.prototype = {
menuitem.disabled = disable;
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("property-view")) {
while (node = node.parentElement) {
if (node.classList.contains("property-view")) {
@ -599,6 +603,10 @@ CssHtmlTree.prototype = {
computedViewCopyDeclaration: function si_computedViewCopyDeclaration(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("property-view")) {
while (node = node.parentElement) {
if (node.classList.contains("property-view")) {
@ -622,6 +630,10 @@ CssHtmlTree.prototype = {
computedViewCopyProperty: function si_computedViewCopyProperty(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("property-view")) {
while (node = node.parentElement) {
if (node.classList.contains("property-view")) {
@ -643,6 +655,10 @@ CssHtmlTree.prototype = {
computedViewCopyPropertyValue: function si_computedViewCopyPropertyValue(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("property-view")) {
while (node = node.parentElement) {
if (node.classList.contains("property-view")) {

View File

@ -929,6 +929,10 @@ CssRuleView.prototype = {
// Copy property, copy property name & copy property value.
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
@ -1007,45 +1011,50 @@ CssRuleView.prototype = {
*/
_onCopyRule: function CssRuleView_onCopyRule(aEvent)
{
let terminator;
let node = this.doc.popupNode;
if (node.className != "ruleview-code") {
if (node.className == "ruleview-rule-source") {
node = node.nextElementSibling;
} else {
while (node = node.parentElement) {
if (node.className == "ruleview-code") {
break;
}
if (!node) {
return;
}
if (node.className != "rule-view-row") {
while (node = node.parentElement) {
if (node.className == "rule-view-row") {
break;
}
}
}
node = node.cloneNode();
if (node.className == "ruleview-code") {
// We need to strip expanded properties from the node because we use
// node.textContent below, which also gets text from hidden nodes. The
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
}
let computedLists = node.querySelectorAll(".ruleview-computedlist");
for (let computedList of computedLists) {
computedList.parentNode.removeChild(computedList);
}
let text = node.textContent;
let autosizers = node.querySelectorAll(".autosizer");
for (let autosizer of autosizers) {
autosizer.parentNode.removeChild(autosizer);
}
let selector = node.querySelector(".ruleview-selector").textContent;
let propertyNames = node.querySelectorAll(".ruleview-propertyname");
let propertyValues = node.querySelectorAll(".ruleview-propertyvalue");
// Format the rule
if (osString == "WINNT") {
text = text.replace(/{/g, "{\r\n ");
text = text.replace(/;/g, ";\r\n ");
text = text.replace(/\s*}/g, "\r\n}");
terminator = "\r\n";
} else {
text = text.replace(/{/g, "{\n ");
text = text.replace(/;/g, ";\n ");
text = text.replace(/\s*}/g, "\n}");
terminator = "\n";
}
clipboardHelper.copyString(text);
let out = selector + " {" + terminator;
for (let i = 0; i < propertyNames.length; i++) {
let name = propertyNames[i].textContent;
let value = propertyValues[i].textContent;
out += " " + name + ": " + value + ";" + terminator;
}
out += "}" + terminator;
clipboardHelper.copyString(out);
},
/**
@ -1056,6 +1065,10 @@ CssRuleView.prototype = {
_onCopyDeclaration: function CssRuleView_onCopyDeclaration(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
@ -1071,11 +1084,16 @@ CssRuleView.prototype = {
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
let computedLists = node.querySelectorAll(".ruleview-computedlist");
for (let computedList of computedLists) {
computedList.parentNode.removeChild(computedList);
}
clipboardHelper.copyString(node.textContent);
let propertyName = node.querySelector(".ruleview-propertyname").textContent;
let propertyValue = node.querySelector(".ruleview-propertyvalue").textContent;
let out = propertyName + ": " + propertyValue + ";";
clipboardHelper.copyString(out);
},
/**
@ -1086,6 +1104,9 @@ CssRuleView.prototype = {
_onCopyProperty: function CssRuleView_onCopyProperty(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("ruleview-propertyname")) {
node = node.querySelector(".ruleview-propertyname");
@ -1104,6 +1125,9 @@ CssRuleView.prototype = {
_onCopyPropertyValue: function CssRuleView_onCopyPropertyValue(aEvent)
{
let node = this.doc.popupNode;
if (!node) {
return;
}
if (!node.classList.contains("ruleview-propertyvalue")) {
node = node.querySelector(".ruleview-propertyvalue");
@ -1140,6 +1164,7 @@ RuleEditor.prototype = {
_create: function RuleEditor_create()
{
this.element = this.doc.createElementNS(HTML_NS, "div");
this.element.className = "rule-view-row";
this.element._ruleEditor = this;
// Give a relative position for the inplace editor's measurement

View File

@ -8,6 +8,10 @@ let doc;
let stylePanel;
let cssHtmlTree;
XPCOMUtils.defineLazyGetter(this, "osString", function() {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});
function createDocument()
{
doc.body.innerHTML = '<style type="text/css"> ' +
@ -63,13 +67,14 @@ function checkCopyProperty()
info("Checking that cssHtmlTree.siBoundCopyDeclaration() returns the " +
"correct clipboard value");
let expectedPattern = "color: rgb\\(255, 255, 0\\);";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function CS_boundCopyPropCheck() {
return checkClipboardData(expectedPattern);
},
cssHtmlTree.siBoundCopyDeclaration,
checkCopyPropertyName, checkCopyPropertyName);
checkCopyPropertyName, function() {
failedClipboard(expectedPattern, checkCopyPropertyName);
});
}
function checkCopyPropertyName()
@ -77,13 +82,14 @@ function checkCopyPropertyName()
info("Checking that cssHtmlTree.siBoundCopyProperty() returns the " +
"correct clipboard value");
let expectedPattern = "color";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function CS_boundCopyPropNameCheck() {
return checkClipboardData(expectedPattern);
},
cssHtmlTree.siBoundCopyProperty,
checkCopyPropertyValue, checkCopyPropertyValue);
checkCopyPropertyValue, function() {
failedClipboard(expectedPattern, checkCopyPropertyValue);
});
}
function checkCopyPropertyValue()
@ -91,13 +97,14 @@ function checkCopyPropertyValue()
info("Checking that cssHtmlTree.siBoundCopyPropertyValue() returns the " +
"correct clipboard value");
let expectedPattern = "rgb\\(255, 255, 0\\)";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function CS_boundCopyPropValueCheck() {
return checkClipboardData(expectedPattern);
},
cssHtmlTree.siBoundCopyPropertyValue,
checkCopySelection, checkCopySelection);
checkCopySelection, function() {
failedClipboard(expectedPattern, checkCopySelection);
});
}
function checkCopySelection()
@ -119,12 +126,13 @@ function checkCopySelection()
"font-family: helvetica,sans-serif[\\r\\n]+" +
"font-size: 16px[\\r\\n]+" +
"font-variant: small-caps[\\r\\n]*";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function CS_boundCopyCheck() {
return checkClipboardData(expectedPattern);
},
cssHtmlTree.siBoundCopy, closeStyleInspector, closeStyleInspector);
cssHtmlTree.siBoundCopy, closeStyleInspector, function() {
failedClipboard(expectedPattern, closeStyleInspector);
});
}
function checkClipboardData(aExpectedPattern)
@ -134,6 +142,28 @@ function checkClipboardData(aExpectedPattern)
return expectedRegExp.test(actual);
}
function failedClipboard(aExpectedPattern, aCallback)
{
// Format expected text for comparison
let terminator = osString == "WINNT" ? "\r\n" : "\n";
aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
let actual = SpecialPowers.getClipboardData("text/unicode");
// Trim the right hand side of our strings. This is because expectedPattern
// accounts for windows sometimes adding a newline to our copied data.
aExpectedPattern = aExpectedPattern.trimRight();
actual = actual.trimRight();
dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
"results (escaped for accurate comparison):\n");
info("Actual: " + escape(actual));
info("Expected: " + escape(aExpectedPattern));
aCallback();
}
function closeStyleInspector()
{
Services.obs.addObserver(finishUp, "StyleInspector-closed", false);

View File

@ -3,6 +3,13 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
let doc;
let tempScope = {};
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
let inplaceEditor = tempScope._getInplaceEditorForSpan;
XPCOMUtils.defineLazyGetter(this, "osString", function() {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});
function createDocument()
{
@ -39,6 +46,30 @@ function openInspector()
InspectorUI.openInspectorUI();
}
function waitForEditorFocus(aParent, aCallback)
{
aParent.addEventListener("focus", function onFocus(evt) {
if (inplaceEditor(evt.target)) {
aParent.removeEventListener("focus", onFocus, true);
let editor = inplaceEditor(evt.target);
executeSoon(function() {
aCallback(editor);
});
}
}, true);
}
function waitForEditorBlur(aEditor, aCallback)
{
let input = aEditor.input;
input.addEventListener("blur", function onBlur() {
input.removeEventListener("blur", onBlur, false);
executeSoon(function() {
aCallback();
});
}, false);
}
function inspectorUIOpen()
{
Services.obs.removeObserver(inspectorUIOpen,
@ -78,12 +109,13 @@ function testClip()
" font-family: helvetica,sans-serif;[\\r\\n]+" +
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
"}[\\r\\n]*";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
return checkClipboardData(expectedPattern);
},
checkCopyRule, checkCopyProperty, checkCopyProperty);
checkCopyRule, checkCopyRuleWithEditorSelected, function() {
failedClipboard(expectedPattern, checkCopyRuleWithEditorSelected);
});
});
}
@ -107,20 +139,86 @@ function checkCopyRule() {
ruleView.contentWindow);
InspectorUI.ruleView._boundCopyRule();
let menu = contentDoc.querySelector("#rule-view-context-menu");
ok(menu, "we have the context menu");
menu.hidePopup();
}
function checkCopyRuleWithEditorSelected()
{
let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
let contentDoc = ruleView.contentDocument;
let rows = contentDoc.querySelectorAll(".rule-view-row");
let propNodes = contentDoc.querySelectorAll(".ruleview-property");
let propNode = propNodes[2];
let propNameNode = propNode.querySelector(".ruleview-propertyname");
ok(propNameNode, "we have the property name node");
info("Checking that _boundCopyRule() returns the correct clipboard value");
let expectedPattern = "element {[\\r\\n]+" +
" margin: 10em;[\\r\\n]+" +
" font-size: 14pt;[\\r\\n]+" +
" font-family: helvetica,sans-serif;[\\r\\n]+" +
" color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
"}[\\r\\n]*";
let elementRuleEditor = rows[0]._ruleEditor;
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
ok(aEditor, "we have the editor");
waitForBlur.editor = aEditor;
// We need the context menu to open in the correct place in order for
// popupNode to be propertly set.
EventUtils.synthesizeMouse(aEditor.input, 1, 1,
{ type: "contextmenu", button: 2 }, ruleView.contentWindow);
SimpleTest.waitForClipboard(function IUI_boundCopyCheckWithSelection() {
let menu = contentDoc.querySelector("#rule-view-context-menu");
ok(menu, "we have the context menu");
menu.hidePopup();
return checkClipboardData(expectedPattern);
}, InspectorUI.ruleView._boundCopyRule, waitForBlur, function() {
failedClipboard(expectedPattern, checkCopyProperty);
});
});
EventUtils.synthesizeMouse(propNameNode, 1, 1, { }, ruleView.contentWindow);
}
function waitForBlur()
{
waitForEditorBlur(waitForBlur.editor, function() {
waitForBlur.editor = null;
checkCopyProperty();
});
waitForBlur.editor.input.blur();
}
function checkCopyProperty()
{
let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview");
let contentDoc = ruleView.contentDocument;
let props = contentDoc.querySelectorAll(".ruleview-property");
let prop = props[2];
info("Checking that _onCopyDeclaration() returns " +
"the correct clipboard value");
let expectedPattern = "font-family: helvetica,sans-serif;";
info("Expected pattern: " + expectedPattern);
// We need the context menu to open in the correct place in order for
// popupNode to be propertly set.
EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
ruleView.contentWindow);
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyDeclaration,
checkCopyPropertyName, checkCopyPropertyName);
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyDeclaration,
checkCopyPropertyName, function() {
failedClipboard(expectedPattern, checkCopyPropertyName);
});
}
function checkCopyPropertyName()
@ -128,13 +226,14 @@ function checkCopyPropertyName()
info("Checking that _onCopyProperty() returns " +
"the correct clipboard value");
let expectedPattern = "font-family";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyProperty,
checkCopyPropertyValue, checkCopyPropertyValue);
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyProperty,
checkCopyPropertyValue, function() {
failedClipboard(expectedPattern, checkCopyPropertyValue);
});
}
function checkCopyPropertyValue()
@ -142,13 +241,14 @@ function checkCopyPropertyValue()
info("Checking that _onCopyPropertyValue() " +
" returns the correct clipboard value");
let expectedPattern = "helvetica,sans-serif";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyPropertyValue,
checkCopySelection, checkCopySelection);
return checkClipboardData(expectedPattern);
},
InspectorUI.ruleView._boundCopyPropertyValue,
checkCopySelection, function() {
failedClipboard(expectedPattern, checkCopySelection);
});
}
function checkCopySelection()
@ -160,10 +260,12 @@ function checkCopySelection()
let range = document.createRange();
range.setStart(props[0], 0);
range.setEnd(props[4], 8);
ruleView.contentWindow.getSelection().addRange(range);
info("Checking that _onCopy() returns the correct" +
"clipboard value");
let selection = ruleView.contentWindow.getSelection();
selection.addRange(range);
info("Checking that _boundCopy() returns the correct" +
"clipboard value");
let expectedPattern = " margin: 10em;[\\r\\n]+" +
" font-size: 14pt;[\\r\\n]+" +
" font-family: helvetica,sans-serif;[\\r\\n]+" +
@ -171,11 +273,12 @@ function checkCopySelection()
"}[\\r\\n]+" +
"html {[\\r\\n]+" +
" color: rgb\\(0, 0, 0\\);[\\r\\n]*";
info("Expected pattern: " + expectedPattern);
SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
return checkClipboardData(expectedPattern);
},InspectorUI.ruleView._boundCopy, finishup, finishup);
return checkClipboardData(expectedPattern);
},InspectorUI.ruleView._boundCopy, finishup, function() {
failedClipboard(expectedPattern, finishup);
});
}
function checkClipboardData(aExpectedPattern)
@ -185,6 +288,28 @@ function checkClipboardData(aExpectedPattern)
return expectedRegExp.test(actual);
}
function failedClipboard(aExpectedPattern, aCallback)
{
// Format expected text for comparison
let terminator = osString == "WINNT" ? "\r\n" : "\n";
aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
let actual = SpecialPowers.getClipboardData("text/unicode");
// Trim the right hand side of our strings. This is because expectedPattern
// accounts for windows sometimes adding a newline to our copied data.
aExpectedPattern = aExpectedPattern.trimRight();
actual = actual.trimRight();
dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
"results (escaped for accurate comparison):\n");
info("Actual: " + escape(actual));
info("Expected: " + escape(aExpectedPattern));
aCallback();
}
function finishup()
{
InspectorUI.hideSidebar();