Bug 886038 - Port the rule view to the styles actor. r=mratcliffe

This commit is contained in:
Dave Camp 2013-08-08 08:44:57 -07:00
Родитель 083c360416
Коммит 7b3a1264e1
22 изменённых файлов: 830 добавлений и 640 удалений

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

@ -68,14 +68,12 @@ function performTests()
// inspector has been told of the pseudoclass lock change.
inspector.selection.once("pseudoclass", () => {
// Give the rule view time to update.
executeSoon(() => {
inspector.once("rule-view-refreshed", () => {
testAdded();
// toggle the lock off and wait for the pseudoclass event again.
// Change the pseudo class and give the rule view time to update.
inspector.togglePseudoClass(pseudo);
inspector.selection.once("pseudoclass", () => {
// Give the rule view time to update.
executeSoon(() => {
inspector.once("rule-view-refreshed", () => {
testRemoved();
testRemovedFromUI();

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

@ -48,26 +48,20 @@ function test() {
instance.setSize(500, 500);
openInspector(onInspectorUIOpen);
openRuleView(onInspectorUIOpen);
}
function onInspectorUIOpen(aInspector) {
function onInspectorUIOpen(aInspector, aRuleView) {
inspector = aInspector;
ruleView = aRuleView;
ok(inspector, "Got inspector instance");
inspector.sidebar.select("ruleview");
let div = content.document.getElementsByTagName("div")[0];
inspector.sidebar.once("ruleview-ready", function() {
Services.obs.addObserver(testShrink, "StyleInspector-populated", false);
inspector.selection.setNode(div);
});
inspector.selection.setNode(div);
inspector.once("inspector-updated", testShrink);
}
function testShrink() {
Services.obs.removeObserver(testShrink, "StyleInspector-populated");
ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
is(numberOfRules(), 2, "Should have two rules initially.");

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

@ -18,3 +18,13 @@ function openInspector(callback)
});
}
function openRuleView(callback)
{
openInspector(inspector => {
inspector.sidebar.once("ruleview-ready", () => {
inspector.sidebar.select("ruleview");
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
callback(inspector, ruleView);
})
});
}

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

@ -7,9 +7,11 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
const promise = require("sdk/core/promise");
let {CssLogic} = require("devtools/styleinspector/css-logic");
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -34,6 +36,46 @@ const CSS_RESOURCE_RE = /url\([\'\"]?(.*?)[\'\"]?\)/;
const IOService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
function promiseWarn(err) {
console.error(err);
return promise.reject(err);
}
/**
* To figure out how shorthand properties are interpreted by the
* engine, we will set properties on a dummy element and observe
* how their .style attribute reflects them as computed values.
* This function creates the document in which those dummy elements
* will be created.
*/
var gDummyPromise;
function createDummyDocument() {
if (gDummyPromise) {
return gDummyPromise;
}
const { getDocShell, create: makeFrame } = require("sdk/frame/utils");
let frame = makeFrame(Services.appShell.hiddenDOMWindow.document, {
nodeName: "iframe",
namespaceURI: "http://www.w3.org/1999/xhtml",
allowJavascript: false,
allowPlugins: false,
allowAuth: false
});
let docShell = getDocShell(frame);
let eventTarget = docShell.chromeEventHandler;
docShell.createAboutBlankContentViewer(Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal));
let window = docShell.contentViewer.DOMDocument.defaultView;
window.location = "data:text/html,<html></html>";
let deferred = promise.defer()
eventTarget.addEventListener("DOMContentLoaded", function handler(event) {
eventTarget.removeEventListener("DOMContentLoaded", handler, false);
deferred.resolve(window.document);
}, false);
gDummyPromise = deferred.promise;
return gDummyPromise;
}
/**
* Our model looks like this:
*
@ -62,13 +104,17 @@ const IOService = Cc["@mozilla.org/network/io-service;1"]
* The ElementStyle can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
* @param {PageStyleFront} aPageStyle
* Front for the page style actor that will be providing
* the style information.
*
* @constructor
*/
function ElementStyle(aElement, aStore)
function ElementStyle(aElement, aStore, aPageStyle)
{
this.element = aElement;
this.store = aStore || {};
this.pageStyle = aPageStyle;
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
@ -85,9 +131,12 @@ function ElementStyle(aElement, aStore)
// To figure out how shorthand properties are interpreted by the
// engine, we will set properties on a dummy element and observe
// how their .style attribute reflects them as computed values.
this.dummyElement = doc.createElementNS(this.element.namespaceURI,
this.element.tagName);
this.populate();
this.dummyElementPromise = createDummyDocument().then(document => {
this.dummyElement = document.createElementNS(this.element.namespaceURI,
this.element.tagName);
document.documentElement.appendChild(this.dummyElement);
return this.dummyElement;
}).then(null, promiseWarn);
}
// We're exporting _ElementStyle for unit tests.
exports._ElementStyle = ElementStyle;
@ -101,6 +150,17 @@ ElementStyle.prototype = {
// to figure out how shorthand properties will be parsed.
dummyElement: null,
destroy: function()
{
this.dummyElement = null;
this.dummyElementPromise.then(dummyElement => {
if (dummyElement.parentNode) {
dummyElement.parentNode.removeChild(dummyElement);
}
this.dummyElementPromise = null;
});
},
/**
* Called by the Rule object when it has been changed through the
* setProperty* methods.
@ -115,62 +175,44 @@ ElementStyle.prototype = {
/**
* Refresh the list of rules to be displayed for the active element.
* Upon completion, this.rules[] will hold a list of Rule objects.
*
* Returns a promise that will be resolved when the elementStyle is
* ready.
*/
populate: function ElementStyle_populate()
{
// Store the current list of rules (if any) during the population
// process. They will be reused if possible.
this._refreshRules = this.rules;
let populated = this.pageStyle.getApplied(this.element, {
inherited: true,
matchedSelectors: true
}).then(entries => {
// Make sure the dummy element has been created before continuing...
return this.dummyElementPromise.then(() => {
if (this.populated != populated) {
// Don't care anymore.
return promise.reject("unused");
}
this.rules = [];
// Store the current list of rules (if any) during the population
// process. They will be reused if possible.
this._refreshRules = this.rules;
let element = this.element;
do {
this._addElementRules(element);
} while ((element = element.parentNode) &&
element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
this.rules = [];
// Mark overridden computed styles.
this.markOverridden();
for (let entry of entries) {
this._maybeAddRule(entry);
}
// We're done with the previous list of rules.
delete this._refreshRules;
},
// Mark overridden computed styles.
this.markOverridden();
_addElementRules: function ElementStyle_addElementRules(aElement)
{
let inherited = aElement !== this.element ? aElement : null;
// We're done with the previous list of rules.
delete this._refreshRules;
// Include the element's style first.
this._maybeAddRule({
style: aElement.style,
selectorText: CssLogic.l10n("rule.sourceElement"),
inherited: inherited
});
// Get the styles that apply to the element.
var domRules = domUtils.getCSSStyleRules(aElement);
// getCSStyleRules returns ordered from least-specific to
// most-specific.
for (let i = domRules.Count() - 1; i >= 0; i--) {
let domRule = domRules.GetElementAt(i);
// XXX: Optionally provide access to system sheets.
let contentSheet = CssLogic.isContentStylesheet(domRule.parentStyleSheet);
if (!contentSheet) {
continue;
}
if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) {
continue;
}
this._maybeAddRule({
domRule: domRule,
inherited: inherited
return null;
});
}
}).then(null, promiseWarn);
this.populated = populated;
return this.populated;
},
/**
@ -186,8 +228,12 @@ ElementStyle.prototype = {
{
// If we've already included this domRule (for example, when a
// common selector is inherited), ignore it.
if (aOptions.domRule &&
this.rules.some(function(rule) rule.domRule === aOptions.domRule)) {
if (aOptions.rule &&
this.rules.some(function(rule) rule.domRule === aOptions.rule)) {
return false;
}
if (aOptions.system) {
return false;
}
@ -195,11 +241,13 @@ ElementStyle.prototype = {
// If we're refreshing and the rule previously existed, reuse the
// Rule object.
for (let r of (this._refreshRules || [])) {
if (r.matches(aOptions)) {
rule = r;
rule.refresh();
break;
if (this._refreshRules) {
for (let r of this._refreshRules) {
if (r.matches(aOptions)) {
rule = r;
rule.refresh(aOptions);
break;
}
}
}
@ -214,6 +262,7 @@ ElementStyle.prototype = {
}
this.rules.push(rule);
return true;
},
/**
@ -324,11 +373,7 @@ ElementStyle.prototype = {
* The ElementStyle to which this rule belongs.
* @param {object} aOptions
* The information used to construct this rule. Properties include:
* domRule: the nsIDOMCSSStyleRule to view, if any.
* style: the nsIDOMCSSStyleDeclaration to view. If omitted,
* the domRule's style will be used.
* selectorText: selector text to display. If omitted, the domRule's
* selectorText will be used.
* rule: A StyleRuleActor
* inherited: An element this rule was inherited from. If omitted,
* the rule applies directly to the current element.
* @constructor
@ -336,15 +381,17 @@ ElementStyle.prototype = {
function Rule(aElementStyle, aOptions)
{
this.elementStyle = aElementStyle;
this.domRule = aOptions.domRule || null;
this.style = aOptions.style || this.domRule.style;
this.selectorText = aOptions.selectorText || this.domRule.selectorText;
this.domRule = aOptions.rule || null;
this.style = aOptions.rule;
this.matchedSelectors = aOptions.matchedSelectors || [];
this.inherited = aOptions.inherited || null;
this._modificationDepth = 0;
if (this.domRule) {
let parentRule = this.domRule.parentRule;
if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
this.mediaText = parentRule.media.mediaText;
this.mediaText = parentRule.mediaText;
}
}
@ -363,11 +410,12 @@ Rule.prototype = {
return this._title;
}
this._title = CssLogic.shortSource(this.sheet);
if (this.domRule) {
if (this.domRule.type !== ELEMENT_STYLE) {
this._title += ":" + this.ruleLine;
}
return this._title + (this.mediaText ? " @media " + this.mediaText : "");
this._title = this._title + (this.mediaText ? " @media " + this.mediaText : "");
return this._title;
},
get inheritedSource()
@ -387,6 +435,11 @@ Rule.prototype = {
return this._inheritedSource;
},
get selectorText()
{
return this.domRule.selectors ? this.domRule.selectors.join(", ") : CssLogic.l10n("rule.sourceElement");
},
/**
* The rule's stylesheet.
*/
@ -400,11 +453,7 @@ Rule.prototype = {
*/
get ruleLine()
{
if (!this.sheet) {
// No stylesheet, no ruleLine
return null;
}
return domUtils.getRuleLine(this.domRule);
return this.domRule ? this.domRule.line : null;
},
/**
@ -416,7 +465,7 @@ Rule.prototype = {
*/
matches: function Rule_matches(aOptions)
{
return (this.style === (aOptions.style || aOptions.domRule.style));
return this.style === aOptions.rule;
},
/**
@ -447,12 +496,15 @@ Rule.prototype = {
* when calling from setPropertyValue & setPropertyName to signify
* that the property should be saved in store.userProperties.
*/
applyProperties: function Rule_applyProperties(aName)
applyProperties: function Rule_applyProperties(aModifications, aName)
{
if (!aModifications) {
aModifications = this.style.startModifyingProperties();
}
let disabledProps = [];
let store = this.elementStyle.store;
for each (let prop in this.textProps) {
for (let prop of this.textProps) {
if (!prop.enabled) {
disabledProps.push({
name: prop.name,
@ -462,21 +514,11 @@ Rule.prototype = {
continue;
}
this.style.setProperty(prop.name, prop.value, prop.priority);
aModifications.setProperty(prop.name, prop.value, prop.priority);
if (aName && prop.name == aName) {
store.userProperties.setProperty(
this.style, prop.name,
this.style.getPropertyValue(prop.name),
prop.value);
}
// Refresh the property's priority from the style, to reflect
// any changes made during parsing.
prop.priority = this.style.getPropertyPriority(prop.name);
prop.updateComputed();
}
this.elementStyle._changed();
// Store disabled properties in the disabled store.
let disabled = this.elementStyle.store.disabled;
@ -486,7 +528,46 @@ Rule.prototype = {
disabled.delete(this.style);
}
this.elementStyle.markOverridden();
let promise = aModifications.apply().then(() => {
let cssProps = {};
for (let cssProp of this._parseCSSText(this.style.cssText)) {
cssProps[cssProp.name] = cssProp;
}
for (let textProp of this.textProps) {
if (!textProp.enabled) {
continue;
}
let cssProp = cssProps[textProp.name];
if (!cssProp) {
cssProp = {
name: textProp.name,
value: "",
priority: ""
}
}
if (aName && textProp.name == aName) {
store.userProperties.setProperty(
this.style, textProp.name,
null,
cssProp.value,
textProp.value);
}
textProp.priority = cssProp.priority;
}
this.elementStyle.markOverridden();
if (promise === this._applyingModifications) {
this._applyingModifications = null;
}
this.elementStyle._changed();
}).then(null, promiseWarn);
this._applyingModifications = promise;
return promise;
},
/**
@ -502,9 +583,10 @@ Rule.prototype = {
if (aName === aProperty.name) {
return;
}
this.style.removeProperty(aProperty.name);
let modifications = this.style.startModifyingProperties();
modifications.removeProperty(aProperty.name);
aProperty.name = aName;
this.applyProperties(aName);
this.applyProperties(modifications, aName);
},
/**
@ -524,7 +606,7 @@ Rule.prototype = {
}
aProperty.value = aValue;
aProperty.priority = aPriority;
this.applyProperties(aProperty.name);
this.applyProperties(null, aProperty.name);
},
/**
@ -533,10 +615,11 @@ Rule.prototype = {
setPropertyEnabled: function Rule_enableProperty(aProperty, aValue)
{
aProperty.enabled = !!aValue;
let modifications = this.style.startModifyingProperties();
if (!aProperty.enabled) {
this.style.removeProperty(aProperty.name);
modifications.removeProperty(aProperty.name);
}
this.applyProperties();
this.applyProperties(modifications);
},
/**
@ -546,10 +629,32 @@ Rule.prototype = {
removeProperty: function Rule_removeProperty(aProperty)
{
this.textProps = this.textProps.filter(function(prop) prop != aProperty);
this.style.removeProperty(aProperty);
let modifications = this.style.startModifyingProperties();
modifications.removeProperty(aProperty.name);
// Need to re-apply properties in case removing this TextProperty
// exposes another one.
this.applyProperties();
this.applyProperties(modifications);
},
_parseCSSText: function Rule_parseProperties(aCssText)
{
let lines = aCssText.match(CSS_LINE_RE);
let props = [];
for (let line of lines) {
dump("line: " + line + "\n");
let [, name, value, priority] = CSS_PROP_RE.exec(line) || []
if (!name || !value) {
continue;
}
props.push({
name: name,
value: value,
priority: priority || ""
});
}
return props;
},
/**
@ -560,19 +665,15 @@ Rule.prototype = {
{
let textProps = [];
let store = this.elementStyle.store;
let lines = this.style.cssText.match(CSS_LINE_RE);
for each (let line in lines) {
let matches = CSS_PROP_RE.exec(line);
if (!matches || !matches[2])
continue;
let name = matches[1];
let props = this._parseCSSText(this.style.cssText);
for (let prop of props) {
let name = prop.name;
if (this.inherited && !domUtils.isInheritedProperty(name)) {
continue;
}
let value = store.userProperties.getProperty(this.style, name, matches[2]);
let prop = new TextProperty(this, name, value, matches[3] || "");
textProps.push(prop);
let value = store.userProperties.getProperty(this.style, name, prop.value);
let textProp = new TextProperty(this, name, value, prop.priority);
textProps.push(textProp);
}
return textProps;
@ -607,8 +708,9 @@ Rule.prototype = {
* Reread the current state of the rules and rebuild text
* properties as needed.
*/
refresh: function Rule_refresh()
refresh: function Rule_refresh(aOptions)
{
this.matchedSelectors = aOptions.matchedSelectors || [];
let newTextProps = this._getTextProperties();
// Update current properties for each property present on the style.
@ -861,14 +963,15 @@ TextProperty.prototype = {
* The CSS rule view can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
* @param {<iframe>} aOuterIFrame
* The iframe containing the ruleview.
* @param {PageStyleFront} aPageStyle
* The PageStyleFront for communicating with the remote server.
* @constructor
*/
function CssRuleView(aDoc, aStore)
function CssRuleView(aDoc, aStore, aPageStyle)
{
this.doc = aDoc;
this.store = aStore;
this.pageStyle = aPageStyle;
this.element = this.doc.createElementNS(HTML_NS, "div");
this.element.className = "ruleview devtools-monospace";
this.element.flex = 1;
@ -892,6 +995,10 @@ CssRuleView.prototype = {
// The element that we're inspecting.
_viewedElement: null,
setPageStyle: function(aPageStyle) {
this.pageStyle = aPageStyle;
},
/**
* Return {bool} true if the rule view currently has an input editor visible.
*/
@ -910,6 +1017,8 @@ CssRuleView.prototype = {
this.element.parentNode.removeChild(this.element);
}
this.elementStyle.destroy();
this.popup.destroy();
},
@ -922,7 +1031,7 @@ CssRuleView.prototype = {
highlight: function CssRuleView_highlight(aElement)
{
if (this._viewedElement === aElement) {
return;
return promise.resolve(undefined);
}
this.clear();
@ -934,15 +1043,15 @@ CssRuleView.prototype = {
this._viewedElement = aElement;
if (!this._viewedElement) {
this._showEmpty();
return;
return promise.resolve(undefined);
}
this._elementStyle = new ElementStyle(aElement, this.store);
this._elementStyle.onChanged = function() {
this._changed();
}.bind(this);
this._createEditors();
this._elementStyle = new ElementStyle(aElement, this.store, this.pageStyle);
return this._populate().then(() => {
this._elementStyle.onChanged = () => {
this._changed();
};
}).then(null, console.error);
},
/**
@ -952,21 +1061,29 @@ CssRuleView.prototype = {
{
// Ignore refreshes during editing or when no element is selected.
if (this.isEditing || !this._elementStyle) {
return;
return promise.resolve(null);
}
this._clearRules();
// Repopulate the element style.
this._elementStyle.populate();
return this._populate();
},
// Refresh the rule editors.
this._createEditors();
_populate: function() {
let elementStyle = this._elementStyle;
return this._elementStyle.populate().then(() => {
if (this._elementStyle != elementStyle) {
return promise.reject("element changed");
}
this._createEditors();
// Notify anyone that cares that we refreshed.
var evt = this.doc.createEvent("Events");
evt.initEvent("CssRuleViewRefreshed", true, false);
this.element.dispatchEvent(evt);
// Notify anyone that cares that we refreshed.
var evt = this.doc.createEvent("Events");
evt.initEvent("CssRuleViewRefreshed", true, false);
this.element.dispatchEvent(evt);
return undefined;
}).then(null, promiseWarn);
},
/**
@ -1023,7 +1140,10 @@ CssRuleView.prototype = {
// Run through the current list of rules, attaching
// their editors in order. Create editors if needed.
let lastInheritedSource = "";
for each (let rule in this._elementStyle.rules) {
for (let rule of this._elementStyle.rules) {
if (rule.domRule.system) {
continue;
}
let inheritedSource = rule.inheritedSource;
if (inheritedSource != lastInheritedSource) {
@ -1117,7 +1237,7 @@ RuleEditor.prototype = {
class: "ruleview-rule-source theme-link"
});
source.addEventListener("click", function() {
let rule = this.rule;
let rule = this.rule.domRule;
let evt = this.doc.createEvent("CustomEvent");
evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
rule: rule,
@ -1198,11 +1318,10 @@ RuleEditor.prototype = {
// If selector text comes from a css rule, highlight selectors that
// actually match. For custom selector text (such as for the 'element'
// style, just show the text directly.
if (this.rule.domRule && this.rule.domRule.selectorText) {
let selectors = CssLogic.getSelectors(this.rule.domRule);
let element = this.rule.inherited || this.ruleView._viewedElement;
for (let i = 0; i < selectors.length; i++) {
let selector = selectors[i];
if (this.rule.domRule.type === ELEMENT_STYLE) {
this.selectorText.textContent = this.rule.selectorText;
} else {
this.rule.domRule.selectors.forEach((selector, i) => {
if (i != 0) {
createChild(this.selectorText, "span", {
class: "ruleview-selector-separator",
@ -1210,7 +1329,7 @@ RuleEditor.prototype = {
});
}
let cls;
if (domUtils.selectorMatchesElement(element, this.rule.domRule, i)) {
if (this.rule.matchedSelectors.indexOf(selector) > -1) {
cls = "ruleview-selector-matched";
} else {
cls = "ruleview-selector-unmatched";
@ -1219,9 +1338,7 @@ RuleEditor.prototype = {
class: cls,
textContent: selector
});
}
} else {
this.selectorText.textContent = this.rule.selectorText;
});
}
for (let prop of this.rule.textProps) {
@ -1339,7 +1456,7 @@ function TextPropertyEditor(aRuleEditor, aProperty)
this.browserWindow = this.doc.defaultView.top;
let sheet = this.prop.rule.sheet;
let href = sheet ? CssLogic.href(sheet) : null;
let href = sheet ? (sheet.href || sheet.nodeHref) : null;
if (href) {
this.sheetURI = IOService.newURI(href, null, null);
}

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

@ -29,19 +29,21 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
this.view = new RuleView.CssRuleView(this.doc, null);
this.doc.documentElement.appendChild(this.view.element);
this._changeHandler = function() {
this._changeHandler = () => {
this.inspector.markDirty();
}.bind(this);
};
this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler)
this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler);
this._cssLinkHandler = function(aEvent) {
let contentDoc = this.inspector.selection.document;
this._refreshHandler = () => {
this.inspector.emit("rule-view-refreshed");
};
this.view.element.addEventListener("CssRuleViewRefreshed", this._refreshHandler);
this._cssLinkHandler = (aEvent) => {
let rule = aEvent.detail.rule;
let line = rule.ruleLine || 0;
let styleSheet = rule.sheet;
let styleSheets = contentDoc.styleSheets;
let contentSheet = false;
let line = rule.line || 0;
// The style editor can only display stylesheets coming from content because
// chrome stylesheets are not listed in the editor's stylesheet selector.
@ -49,42 +51,34 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
// If the stylesheet is a content stylesheet we send it to the style
// editor else we display it in the view source window.
//
// Array.prototype.indexOf always returns -1 here so we loop through
// the styleSheets object instead.
for each (let sheet in styleSheets) {
if (sheet == styleSheet) {
contentSheet = true;
break;
}
}
if (contentSheet) {
let href = rule.href;
let sheet = rule.parentStyleSheet;
if (sheet && href && !sheet.isSystem) {
let target = this.inspector.target;
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
toolbox.getCurrentPanel().selectStyleSheet(styleSheet.href, line);
toolbox.getCurrentPanel().selectStyleSheet(href, line);
});
}
} else {
let href = styleSheet ? styleSheet.href : "";
if (rule.elementStyle.element) {
href = rule.elementStyle.element.ownerDocument.location.href;
}
let viewSourceUtils = this.inspector.viewSourceUtils;
viewSourceUtils.viewSource(href, null, contentDoc, line);
return;
}
}.bind(this);
let contentDoc = this.inspector.selection.document;
let viewSourceUtils = this.inspector.viewSourceUtils;
viewSourceUtils.viewSource(href, null, contentDoc, line);
}
this.view.element.addEventListener("CssRuleViewCSSLinkClicked",
this._cssLinkHandler);
this._onSelect = this.onSelect.bind(this);
this.inspector.selection.on("detached", this._onSelect);
this.inspector.selection.on("new-node", this._onSelect);
this.inspector.selection.on("new-node-front", this._onSelect);
this.refresh = this.refresh.bind(this);
this.inspector.on("layout-change", this.refresh);
this.inspector.sidebar.on("ruleview-selected", this.refresh);
this.panelSelected = this.panelSelected.bind(this);
this.inspector.sidebar.on("ruleview-selected", this.panelSelected);
this.inspector.selection.on("pseudoclass", this.refresh);
if (this.inspector.highlighter) {
this.inspector.highlighter.on("locked", this._onSelect);
@ -97,22 +91,30 @@ exports.RuleViewTool = RuleViewTool;
RuleViewTool.prototype = {
onSelect: function RVT_onSelect(aEvent) {
if (!this.isActive()) {
// We'll update when the panel is selected.
return;
}
this.view.setPageStyle(this.inspector.pageStyle);
if (!this.inspector.selection.isConnected() ||
!this.inspector.selection.isElementNode()) {
this.view.highlight(null);
return;
}
if (!aEvent || aEvent == "new-node") {
if (!aEvent || aEvent == "new-node-front") {
if (this.inspector.selection.reason == "highlighter") {
this.view.highlight(null);
} else {
this.view.highlight(this.inspector.selection.node);
let done = this.inspector.updating("rule-view");
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
}
}
if (aEvent == "locked") {
this.view.highlight(this.inspector.selection.node);
let done = this.inspector.updating("rule-view");
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
}
},
@ -126,11 +128,19 @@ RuleViewTool.prototype = {
}
},
panelSelected: function() {
if (this.inspector.selection.nodeFront === this.view.viewedElement) {
this.view.nodeChanged();
} else {
this.onSelect();
}
},
destroy: function RVT_destroy() {
this.inspector.off("layout-change", this.refresh);
this.inspector.sidebar.off("ruleview-selected", this.refresh);
this.inspector.selection.off("pseudoclass", this.refresh);
this.inspector.selection.off("new-node", this._onSelect);
this.inspector.selection.off("new-node-front", this._onSelect);
if (this.inspector.highlighter) {
this.inspector.highlighter.off("locked", this._onSelect);
}
@ -141,6 +151,9 @@ RuleViewTool.prototype = {
this.view.element.removeEventListener("CssRuleViewChanged",
this._changeHandler);
this.view.element.removeEventListener("CssRuleViewRefreshed",
this._refreshHandler);
this.doc.documentElement.removeChild(this.view.element);
this.view.destroy();

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

@ -6,6 +6,7 @@
// rule view.
let doc;
let inspector = null;
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
"test/browser_bug722196_identify_media_queries.html";
@ -21,7 +22,15 @@ function docLoaded()
{
browser.removeEventListener("load", docLoaded, true);
doc = content.document;
checkSheets();
openRuleView(selectNode);
}
function selectNode(aInspector, aRuleView)
{
inspector = aInspector;
inspector.selection.setNode(doc.querySelector("div"));
inspector.once("inspector-updated", checkSheets);
}
function checkSheets()
@ -29,14 +38,14 @@ function checkSheets()
var div = doc.querySelector("div");
ok(div, "captain, we have the div");
let elementStyle = new _ElementStyle(div);
is(elementStyle.rules.length, 3, "Should have 3 rules.");
let elementStyle = ruleView()._elementStyle;
let _strings = Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties");
let inline = _strings.GetStringFromName("rule.sourceInline");
is(elementStyle.rules.length, 3, "Should have 3 rules.");
is(elementStyle.rules[0].title, inline, "check rule 0 title");
is(elementStyle.rules[1].title, inline +
":15 @media screen and (min-width: 1px)", "check rule 1 title");

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

@ -6,8 +6,8 @@
// arrow keys works correctly.
let doc;
let ruleDialog;
let ruleView;
let view;
let inspector;
function setUpTests()
{
@ -15,25 +15,22 @@ function setUpTests()
'margin-top:0px;' +
'padding-top: 0px;' +
'color:#000000;' +
'background-color: #000000; >"'+
'background-color: #000000;" >'+
'</div>';
let testElement = doc.getElementById("test");
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
"cssruleviewtest",
"width=350,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad, true);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.highlight(testElement);
waitForFocus(runTests, ruleDialog);
}, true);
openRuleView((aInspector, aRuleView) => {
inspector = aInspector;
view = aRuleView;
inspector.selection.setNode(doc.getElementById("test"));
inspector.once("inspector-updated", () => {
runTests();
})
});
}
function runTests()
{
let idRuleEditor = ruleView.element.children[0]._ruleEditor;
let idRuleEditor = view.element.children[0]._ruleEditor;
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
@ -52,7 +49,7 @@ function runTests()
8: { pageDown: true, shift: true, start: "0px", end: "-100px", selectAll: true,
nextTest: test2 }
});
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
})();
function test2() {
@ -69,7 +66,7 @@ function runTests()
9: { start: "0ex", end: "1ex", selectAll: true,
nextTest: test3 }
});
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
};
function test3() {
@ -83,7 +80,7 @@ function runTests()
6: { down: true, shift: true, start: "#000000", end: "#000000", selectAll: true,
nextTest: test4 }
});
EventUtils.synthesizeMouse(hexColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(hexColorPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
};
function test4() {
@ -97,7 +94,7 @@ function runTests()
6: { down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6,7],
nextTest: test5 }
});
EventUtils.synthesizeMouse(rgbColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(rgbColorPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
};
function test5() {
@ -111,7 +108,7 @@ function runTests()
6: { down: true, shift: true, start: "0px 0px 0px 0px", end: "-10px 0px 0px 0px", selectAll: true,
nextTest: test6 }
});
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
};
function test6() {
@ -133,7 +130,7 @@ function runTests()
14: { alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10,14],
endTest: true }
});
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
};
}
@ -172,14 +169,11 @@ function testIncrement( aEditor, aOptions )
key = ( aOptions.pageDown ) ? "VK_PAGE_DOWN" : ( aOptions.pageUp ) ? "VK_PAGE_UP" : key;
EventUtils.synthesizeKey(key,
{altKey: aOptions.alt, shiftKey: aOptions.shift},
ruleDialog);
view.doc.defaultView);
}
function finishTest()
{
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();

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

@ -62,7 +62,7 @@ function openRuleView()
let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.setNode(node);
inspector.sidebar.once("ruleview-ready", testCompletion);
inspector.once("inspector-updated", testCompletion);
});
}

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

@ -49,7 +49,7 @@ function openRuleView() {
let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.setNode(node);
inspector.sidebar.once("ruleview-ready", testCompletion);
inspector.once("inspector-updated", testCompletion);
});
}

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

@ -47,7 +47,7 @@ function openRuleView()
let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.setNode(node);
inspector.sidebar.once("ruleview-ready", testCompletion);
inspector.once("inspector-updated", testCompletion);
});
}

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

@ -48,7 +48,7 @@ function openRuleView() {
let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.setNode(node);
inspector.sidebar.once("ruleview-ready", testCompletion);
inspector.once("inspector-updated", testCompletion);
});
}

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

@ -62,42 +62,38 @@ function highlightNode()
// Highlight a node.
let div = content.document.getElementsByTagName("div")[0];
inspector.selection.once("new-node", function() {
inspector.selection.setNode(div);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, div, "selection matches the div element");
testInlineStyle();
});
executeSoon(function() {
inspector.selection.setNode(div);
});
}).then(null, console.error);
}
function testInlineStyle()
{
executeSoon(function() {
info("clicking an inline style");
info("clicking an inline style");
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
if (aTopic != "domwindowopened") {
return;
}
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
if (aTopic != "domwindowopened") {
return;
}
win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function windowLoad() {
win.removeEventListener("load", windowLoad);
let windowType = win.document.documentElement.getAttribute("windowtype");
is(windowType, "navigator:view-source", "view source window is open");
win.close();
Services.ww.unregisterNotification(onWindow);
executeSoon(() => {
testInlineStyleSheet();
});
win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function windowLoad() {
win.removeEventListener("load", windowLoad);
let windowType = win.document.documentElement.getAttribute("windowtype");
is(windowType, "navigator:view-source", "view source window is open");
win.close();
Services.ww.unregisterNotification(onWindow);
executeSoon(() => {
testInlineStyleSheet();
});
});
let link = getLinkByIndex(0);
link.scrollIntoView();
link.click();
});
let link = getLinkByIndex(0);
link.scrollIntoView();
link.click();
}
function testInlineStyleSheet()

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

@ -45,13 +45,12 @@ function highlightNode()
// Highlight a node.
let div = content.document.getElementsByTagName("div")[0];
inspector.selection.once("new-node", function() {
inspector.once("inspector-updated", function() {
is(inspector.selection.node, div, "selection matches the div element");
executeSoon(checkCopySelection);
});
executeSoon(function() {
inspector.selection.setNode(div);
});
inspector.selection.setNode(div);
}
function checkCopySelection()

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

@ -3,20 +3,9 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
let doc;
let ruleDialog;
let ruleWindow;
let ruleView;
var gRuleViewChanged = false;
function ruleViewChanged()
{
gRuleViewChanged = true;
}
function expectChange()
{
ok(gRuleViewChanged, "Rule view should have fired a change event.");
gRuleViewChanged = false;
}
let inspector;
function startTest()
{
@ -32,18 +21,13 @@ function startTest()
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let testElement = doc.getElementById("testid");
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
"cssruleviewtest",
"width=200,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad, true);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.highlight(testElement);
waitForFocus(testCancelNew, ruleDialog);
}, true);
openRuleView((aInspector, aRuleView) => {
inspector = aInspector;
ruleView = aRuleView;
ruleWindow = aRuleView.doc.defaultView;
inspector.selection.setNode(testElement);
inspector.once("inspector-updated", testCancelNew);
});
}
function testCancelNew()
@ -55,7 +39,7 @@ function testCancelNew()
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
waitForEditorBlur(aEditor, function () {
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
ok(!elementRuleEditor.rule._applyingModifications, "Shouldn't have an outstanding request after a cancel.");
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
testCreateNew();
@ -64,7 +48,7 @@ function testCancelNew()
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
ruleWindow);
}
function testCreateNew()
@ -77,26 +61,28 @@ function testCreateNew()
input.value = "background-color";
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
expectChange();
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
aEditor.input.value = "#XYZ";
waitForEditorBlur(aEditor, function() {
expectChange();
is(textProp.value, "#XYZ", "Text prop should have been changed.");
is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
testEditProperty();
});
aEditor.input.blur();
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
aEditor.input.value = "#XYZ";
waitForEditorBlur(aEditor, function() {
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
is(textProp.value, "#XYZ", "Text prop should have been changed.");
is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
testEditProperty();
}));
});
aEditor.input.blur();
}));
});
EventUtils.synthesizeKey("VK_RETURN", {}, ruleDialog);
EventUtils.synthesizeKey("VK_RETURN", {}, ruleWindow);
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
ruleWindow);
}
function testEditProperty()
@ -107,38 +93,37 @@ function testEditProperty()
is(inplaceEditor(propEditor.nameSpan), aEditor, "Next focused editor should be the name editor.");
let input = aEditor.input;
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
expectChange();
input = aEditor.input;
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
input = aEditor.input;
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
waitForEditorBlur(aEditor, function() {
expectChange();
let value = idRuleEditor.rule.style.getPropertyValue("border-color");
is(value, "red", "border-color should have been set.");
is(propEditor._validate(), true, "red should be a valid entry");
finishTest();
});
waitForEditorBlur(aEditor, function() {
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
let value = idRuleEditor.rule.domRule._rawStyle().getPropertyValue("border-color");
is(value, "red", "border-color should have been set.");
is(propEditor._validate(), true, "red should be a valid entry");
finishTest();
}));
});
for each (let ch in "red;") {
EventUtils.sendChar(ch, ruleDialog);
}
for (let ch of "red;") {
EventUtils.sendChar(ch, ruleWindow);
}
}));
});
for each (let ch in "border-color:") {
EventUtils.sendChar(ch, ruleDialog);
for (let ch of "border-color:") {
EventUtils.sendChar(ch, ruleWindow);
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
{ },
ruleDialog);
ruleWindow);
}
function finishTest()
{
ruleView.element.removeEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
inspector = ruleWindow = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();

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

@ -9,18 +9,12 @@ let doc;
let inspector;
let stylePanel;
function openRuleView()
function selectNode(aInspector, aRuleView)
{
var target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
inspector = toolbox.getCurrentPanel();
inspector.sidebar.select("ruleview");
// Highlight a node.
let node = content.document.getElementsByTagName("h1")[0];
inspector.sidebar.once("ruleview-ready", testFocus);
});
inspector = aInspector;
let node = content.document.getElementsByTagName("h1")[0];
inspector.selection.setNode(node);
inspector.once("inspector-updated", testFocus);
}
function testFocus()
@ -64,7 +58,7 @@ function test()
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
doc.title = "Rule View Test";
waitForFocus(openRuleView, content);
waitForFocus(() => openRuleView(selectNode), content);
}, true);
content.location = "data:text/html,<h1>Some header text</h1>";

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

@ -4,8 +4,16 @@
let doc;
function simpleInherit()
let inspector;
let view;
let {ELEMENT_STYLE} = devtools.require("devtools/server/actors/styles");
function simpleInherit(aInspector, aRuleView)
{
inspector = aInspector;
view = aRuleView;
let style = '' +
'#test2 {' +
' background-color: green;' +
@ -15,23 +23,26 @@ function simpleInherit()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
inspector.selection.setNode(doc.getElementById("test1"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
is(elementStyle.rules.length, 2, "Should have 2 rules.");
is(elementStyle.rules.length, 2, "Should have 2 rules.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
let inheritRule = elementStyle.rules[1];
is(inheritRule.selectorText, "#test2", "Inherited rule should be the one that includes inheritable properties.");
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
let inheritProp = inheritRule.textProps[0];
is(inheritProp.name, "color", "color should have been inherited.");
let inheritRule = elementStyle.rules[1];
is(inheritRule.selectorText, "#test2", "Inherited rule should be the one that includes inheritable properties.");
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
let inheritProp = inheritRule.textProps[0];
is(inheritProp.name, "color", "color should have been inherited.");
styleNode.parentNode.removeChild(styleNode);
styleNode.parentNode.removeChild(styleNode);
emptyInherit();
emptyInherit();
}).then(null, console.error);
}
function emptyInherit()
@ -45,37 +56,43 @@ function emptyInherit()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
inspector.selection.setNode(doc.getElementById("test1"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
is(elementStyle.rules.length, 1, "Should have 1 rule.");
is(elementStyle.rules.length, 1, "Should have 1 rule.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
styleNode.parentNode.removeChild(styleNode);
styleNode.parentNode.removeChild(styleNode);
elementStyleInherit();
elementStyleInherit();
}).then(null, console.error);
}
function elementStyleInherit()
{
doc.body.innerHTML = '<div id="test2" style="color: red"><div id="test1">Styled Node</div></div>';
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
inspector.selection.setNode(doc.getElementById("test1"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
is(elementStyle.rules.length, 2, "Should have 2 rules.");
is(elementStyle.rules.length, 2, "Should have 2 rules.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
let elementRule = elementStyle.rules[0];
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
let inheritRule = elementStyle.rules[1];
ok(!inheritRule.domRule, "Inherited rule should be an element style, not a rule.");
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
let inheritProp = inheritRule.textProps[0];
is(inheritProp.name, "color", "color should have been inherited.");
let inheritRule = elementStyle.rules[1];
is(inheritRule.domRule.type, ELEMENT_STYLE, "Inherited rule should be an element style, not a rule.");
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
let inheritProp = inheritRule.textProps[0];
is(inheritProp.name, "color", "color should have been inherited.");
finishTest();
finishTest();
}).then(null, console.error);
}
function finishTest()
@ -92,7 +109,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
waitForFocus(simpleInherit, content);
waitForFocus(() => openRuleView(simpleInherit), content);
}, true);
content.location = "data:text/html,basic style inspector tests";

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

@ -4,47 +4,68 @@
let doc;
function simpleOverride()
function simpleOverride(aInspector, aRuleView)
{
doc.body.innerHTML = '<div id="testid">Styled Node</div>';
let element = doc.getElementById("testid");
let elementStyle = new _ElementStyle(element);
let elementRule = elementStyle.rules[0];
let firstProp = elementRule.createProperty("background-color", "green", "");
let secondProp = elementRule.createProperty("background-color", "blue", "");
is(elementRule.textProps[0], firstProp, "Rules should be in addition order.");
is(elementRule.textProps[1], secondProp, "Rules should be in addition order.");
aInspector.selection.setNode(element);
aInspector.once("inspector-updated", () => {
let elementStyle = aRuleView._elementStyle;
is(element.style.getPropertyValue("background-color"), "blue", "Second property should have been used.");
let elementRule = elementStyle.rules[0];
let firstProp = elementRule.createProperty("background-color", "green", "");
let secondProp = elementRule.createProperty("background-color", "blue", "");
is(elementRule.textProps[0], firstProp, "Rules should be in addition order.");
is(elementRule.textProps[1], secondProp, "Rules should be in addition order.");
secondProp.remove();
is(element.style.getPropertyValue("background-color"), "green", "After deleting second property, first should be used.");
promiseDone(elementRule._applyingModifications.then(() => {
is(element.style.getPropertyValue("background-color"), "blue", "Second property should have been used.");
secondProp = elementRule.createProperty("background-color", "blue", "");
is(element.style.getPropertyValue("background-color"), "blue", "New property should be used.");
secondProp.remove();
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "green", "After deleting second property, first should be used.");
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
secondProp = elementRule.createProperty("background-color", "blue", "");
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "blue", "New property should be used.");
secondProp.setEnabled(false);
is(element.style.getPropertyValue("background-color"), "green", "After disabling second property, first value should be used");
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
firstProp.setEnabled(false);
is(element.style.getPropertyValue("background-color"), "", "After disabling both properties, value should be empty.");
secondProp.setEnabled(false);
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "green", "After disabling second property, first value should be used");
secondProp.setEnabled(true);
is(element.style.getPropertyValue("background-color"), "blue", "Value should be set correctly after re-enabling");
firstProp.setEnabled(false);
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "", "After disabling both properties, value should be empty.");
firstProp.setEnabled(true);
is(element.style.getPropertyValue("background-color"), "blue", "Re-enabling an earlier property shouldn't make it override a later property.");
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
secondProp.setEnabled(true);
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "blue", "Value should be set correctly after re-enabling");
firstProp.setValue("purple", "");
is(element.style.getPropertyValue("background-color"), "blue", "Modifying an earlier property shouldn't override a later property.");
firstProp.setEnabled(true);
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "blue", "Re-enabling an earlier property shouldn't make it override a later property.");
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
finishTest();
firstProp.setValue("purple", "");
return elementRule._applyingModifications;
}).then(() => {
is(element.style.getPropertyValue("background-color"), "blue", "Modifying an earlier property shouldn't override a later property.");
finishTest();
}));
});
}
function finishTest()
@ -61,7 +82,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
waitForFocus(simpleOverride, content);
waitForFocus(() => openRuleView(simpleOverride), content);
}, true);
content.location = "data:text/html,basic style inspector tests";

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

@ -3,9 +3,13 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
let doc;
let inspector;
let view;
function simpleOverride()
function simpleOverride(aInspector, aRuleView)
{
inspector = aInspector;
view = aRuleView;
let style = '' +
'#testid {' +
' background-color: blue;' +
@ -17,31 +21,34 @@ function simpleOverride()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
inspector.selection.setNode(doc.getElementById("testid"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
is(idProp.name, "background-color", "First ID prop should be background-color");
ok(!idProp.overridden, "ID prop should not be overridden.");
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
is(idProp.name, "background-color", "First ID prop should be background-color");
ok(!idProp.overridden, "ID prop should not be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
is(classProp.name, "background-color", "First class prop should be background-color");
ok(classProp.overridden, "Class property should be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
is(classProp.name, "background-color", "First class prop should be background-color");
ok(classProp.overridden, "Class property should be overridden.");
// Override background-color by changing the element style.
let elementRule = elementStyle.rules[0];
elementRule.createProperty("background-color", "purple", "");
let elementProp = elementRule.textProps[0];
is(classProp.name, "background-color", "First element prop should now be background-color");
// Override background-color by changing the element style.
let elementRule = elementStyle.rules[0];
elementRule.createProperty("background-color", "purple", "");
promiseDone(elementRule._applyingModifications.then(() => {
let elementProp = elementRule.textProps[0];
is(classProp.name, "background-color", "First element prop should now be background-color");
ok(!elementProp.overridden, "Element style property should not be overridden");
ok(idProp.overridden, "ID property should be overridden");
ok(classProp.overridden, "Class property should be overridden");
ok(!elementProp.overridden, "Element style property should not be overridden");
ok(idProp.overridden, "ID property should be overridden");
ok(classProp.overridden, "Class property should be overridden");
styleNode.parentNode.removeChild(styleNode);
partialOverride();
styleNode.parentNode.removeChild(styleNode);
partialOverride();
}));
});
}
function partialOverride()
@ -59,22 +66,25 @@ function partialOverride()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
inspector.selection.setNode(doc.getElementById("testid"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
for each (let computed in classProp.computed) {
if (computed.name.indexOf("margin-left") == 0) {
ok(computed.overridden, "margin-left props should be overridden.");
} else {
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
for (let computed of classProp.computed) {
if (computed.name.indexOf("margin-left") == 0) {
ok(computed.overridden, "margin-left props should be overridden.");
} else {
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
}
}
}
styleNode.parentNode.removeChild(styleNode);
styleNode.parentNode.removeChild(styleNode);
importantOverride();
importantOverride();
});
}
function importantOverride()
@ -91,24 +101,29 @@ function importantOverride()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
inspector.selection.setNode(doc.getElementById("testid"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
ok(idProp.overridden, "Not-important rule should be overridden.");
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
ok(idProp.overridden, "Not-important rule should be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Important rule should not be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Important rule should not be overridden.");
styleNode.parentNode.removeChild(styleNode);
styleNode.parentNode.removeChild(styleNode);
let elementRule = elementStyle.rules[0];
let elementProp = elementRule.createProperty("background-color", "purple", "important");
ok(classProp.overridden, "New important prop should override class property.");
ok(!elementProp.overridden, "New important prop should not be overriden.");
let elementRule = elementStyle.rules[0];
let elementProp = elementRule.createProperty("background-color", "purple", "important");
promiseDone(elementRule._applyingModifications.then(() => {
ok(classProp.overridden, "New important prop should override class property.");
ok(!elementProp.overridden, "New important prop should not be overriden.");
disableOverride();
disableOverride();
}));
});
}
function disableOverride()
@ -123,19 +138,23 @@ function disableOverride()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
inspector.selection.setNode(doc.getElementById("testid"));
inspector.once("inspector-updated", () => {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
idProp.setEnabled(false);
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
idProp.setEnabled(false);
promiseDone(idRule._applyingModifications.then(() => {
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
styleNode.parentNode.removeChild(styleNode);
styleNode.parentNode.removeChild(styleNode);
finishTest();
finishTest();
}));
});
}
function finishTest()
@ -152,7 +171,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
waitForFocus(simpleOverride, content);
waitForFocus(() => openRuleView(simpleOverride), content);
}, true);
content.location = "data:text/html,basic style inspector tests";

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

@ -3,23 +3,15 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
let doc;
let ruleDialog;
let inspector;
let ruleWindow;
let ruleView;
var gRuleViewChanged = false;
function ruleViewChanged()
{
gRuleViewChanged = true;
}
function expectChange()
{
ok(gRuleViewChanged, "Rule view should have fired a change event.");
gRuleViewChanged = false;
}
function startTest()
function startTest(aInspector, aRuleView)
{
inspector = aInspector;
ruleWindow = aRuleView.doc.defaultView;
ruleView = aRuleView;
let style = '' +
'#testid {' +
' background-color: blue;' +
@ -30,30 +22,24 @@ function startTest()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let testElement = doc.getElementById("testid");
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
"cssruleviewtest",
"width=200,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
is(ruleView.element.querySelectorAll("#noResults").length, 1, "Has a no-results element.");
ruleView.highlight(testElement);
inspector.selection.setNode(testElement);
inspector.once("inspector-updated", () => {
is(ruleView.element.querySelectorAll("#noResults").length, 0, "After a highlight, no longer has a no-results element.");
ruleView.highlight(null);
is(ruleView.element.querySelectorAll("#noResults").length, 1, "After highlighting null, has a no-results element again.");
ruleView.highlight(testElement);
inspector.selection.setNode(null);
inspector.once("inspector-updated", () => {
is(ruleView.element.querySelectorAll("#noResults").length, 1, "After highlighting null, has a no-results element again.");
inspector.selection.setNode(testElement)
inspector.once("inspector-updated", () => {
let classEditor = ruleView.element.children[2]._ruleEditor;
is(classEditor.selectorText.querySelector(".ruleview-selector-matched").textContent, ".testclass", ".textclass should be matched.");
is(classEditor.selectorText.querySelector(".ruleview-selector-unmatched").textContent, ".unmatched", ".unmatched should not be matched.");
let classEditor = ruleView.element.children[2]._ruleEditor;
is(classEditor.selectorText.querySelector(".ruleview-selector-matched").textContent, ".testclass", ".textclass should be matched.");
is(classEditor.selectorText.querySelector(".ruleview-selector-unmatched").textContent, ".unmatched", ".unmatched should not be matched.");
waitForFocus(testCancelNew, ruleDialog);
}, true);
testCancelNew();
});
});
});
}
function testCancelNew()
@ -66,7 +52,7 @@ function testCancelNew()
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
waitForEditorBlur(aEditor, function () {
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
ok(!elementRuleEditor.rule._applyingModifications, "Shouldn't have an outstanding modification request after a cancel.");
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
testCreateNew();
@ -76,7 +62,7 @@ function testCancelNew()
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
ruleWindow);
}
function testCreateNew()
@ -91,33 +77,35 @@ function testCreateNew()
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
input.select();
input.value = "background-color";
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
expectChange();
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
aEditor.input.value = "purple";
waitForEditorBlur(aEditor, function() {
expectChange();
is(textProp.value, "purple", "Text prop should have been changed.");
testEditProperty();
});
aEditor.input.value = "purple";
waitForEditorBlur(aEditor, function() {
expectRuleChange(elementRuleEditor.rule).then(() => {
is(textProp.value, "purple", "Text prop should have been changed.");
testEditProperty();
});
});
aEditor.input.blur();
aEditor.input.blur();
}));
});
EventUtils.sendKey("return", ruleDialog);
EventUtils.sendKey("return", ruleWindow);
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
ruleWindow);
}
function testEditProperty()
@ -133,48 +121,50 @@ function testEditProperty()
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
input.select();
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
expectChange();
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
input = aEditor.input;
input = aEditor.input;
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
input.select();
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
input.select();
waitForEditorBlur(aEditor, function() {
expectChange();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
"border-color should have been set.");
waitForEditorBlur(aEditor, function() {
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {;
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "red",
"border-color should have been set.");
let props = ruleView.element.querySelectorAll(".ruleview-property");
for (let i = 0; i < props.length; i++) {
is(props[i].hasAttribute("dirty"), i <= 1,
"props[" + i + "] marked dirty as appropriate");
let props = ruleView.element.querySelectorAll(".ruleview-property");
for (let i = 0; i < props.length; i++) {
is(props[i].hasAttribute("dirty"), i <= 1,
"props[" + i + "] marked dirty as appropriate");
}
testDisableProperty();
}));
});
for (let ch of "red;") {
EventUtils.sendChar(ch, ruleWindow);
is(propEditor.warning.hidden, ch == "d" || ch == ";",
"warning triangle is hidden or shown as appropriate");
}
testDisableProperty();
});
for each (let ch in "red;") {
EventUtils.sendChar(ch, ruleDialog);
is(propEditor.warning.hidden, ch == "d" || ch == ";",
"warning triangle is hidden or shown as appropriate");
}
}));
});
for each (let ch in "border-color:") {
EventUtils.sendChar(ch, ruleDialog);
for (let ch of "border-color:") {
EventUtils.sendChar(ch, ruleWindow);
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
{ },
ruleDialog);
ruleWindow);
}
function testDisableProperty()
@ -183,22 +173,22 @@ function testDisableProperty()
let propEditor = idRuleEditor.rule.textProps[0].editor;
propEditor.enable.click();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "", "Border-color should have been unset.");
expectChange();
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "", "Border-color should have been unset.");
propEditor.enable.click();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
"Border-color should have been reset.");
expectChange();
propEditor.enable.click();
return expectRuleChange(idRuleEditor.rule);
}).then(() => {
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "red",
"Border-color should have been reset.");
finishTest();
finishTest();
}));
}
function finishTest()
{
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
inspector = ruleWindow = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();
@ -211,7 +201,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
waitForFocus(startTest, content);
waitForFocus(() => openRuleView(startTest), content);
}, true);
content.location = "data:text/html,basic style inspector tests";

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

@ -3,12 +3,15 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
let doc;
let ruleDialog;
let inspector;
let ruleView;
let testElement;
function startTest()
function startTest(aInspector, aRuleView)
{
inspector = aInspector;
ruleView = aRuleView;
let style = '' +
'#testid {' +
' background-color: blue;' +
@ -19,21 +22,15 @@ function startTest()
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
testElement = doc.getElementById("testid");
let elementStyle = 'margin-top: 1px; padding-top: 5px;'
testElement.setAttribute("style", elementStyle);
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
"cssruleviewtest",
"width=200,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.highlight(testElement);
waitForFocus(testRuleChanges, ruleDialog);
inspector.selection.setNode(testElement);
inspector.once("inspector-updated", () => {
testRuleChanges();
}, true);
}
@ -47,24 +44,24 @@ function testRuleChanges()
// Change the id and refresh.
testElement.setAttribute("id", "differentid");
ruleView.nodeChanged();
promiseDone(ruleView.nodeChanged().then(() => {
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 2, "Two rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 2, "Three rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
testElement.setAttribute("id", "testid");
return ruleView.nodeChanged();
}).then(() => {
// Put the id back.
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 3, "Three rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
testElement.setAttribute("id", "testid");
ruleView.nodeChanged();
// Put the id back.
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 3, "Three rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
testPropertyChanges();
testPropertyChanges();
}));
}
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
@ -81,59 +78,64 @@ function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
function testPropertyChanges()
{
// Add a second margin-top value, just to make things interesting.
let rule = ruleView._elementStyle.rules[0];
let ruleEditor = ruleView._elementStyle.rules[0].editor;
ruleEditor.addProperty("margin-top", "5px", "");
promiseDone(expectRuleChange(rule).then(() => {
// Set the element style back to a 1px margin-top.
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
let rule = ruleView._elementStyle.rules[0];
// Now set it back to 5px, the 5px value should be re-enabled.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
return ruleView.nodeChanged();
// Set the element style back to a 1px margin-top.
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
// Now set it back to 5px, the 5px value should be re-enabled.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
// Set the margin property to a value that doesn't exist in the editor.
// Should reuse the currently-enabled element (the second one.)
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
// Set the margin property to a value that doesn't exist in the editor.
// Should reuse the currently-enabled element (the second one.)
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
// Remove the padding-top attribute. Should disable the padding property but not remove it.
testElement.setAttribute("style", "margin-top: 5px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
// Remove the padding-top attribute. Should disable the padding property but not remove it.
testElement.setAttribute("style", "margin-top: 5px;");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
// Put the padding-top attribute back in, should re-enable the padding property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
// Put the padding-top attribute back in, should re-enable the padding property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
// Add an entirely new property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
// Add an entirely new property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
ruleView.nodeChanged();
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
finishTest();
finishTest();
}));
}
function finishTest()
{
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
inspector = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();
@ -146,7 +148,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
doc = content.document;
waitForFocus(startTest, content);
waitForFocus(() => openRuleView(startTest), content);
}, true);
content.location = "data:text/html,basic style inspector tests";

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

@ -15,22 +15,11 @@ const TEST_URI = BASE_URL +
const TEST_IMAGE = BASE_URL + "test-image.png";
const BASE_64_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
function createDocument()
{
doc.title = "Style Inspector URL Clickable test";
openInspector(function(aInspector) {
inspector = aInspector;
executeSoon(selectNode);
});
}
function selectNode(aInspector)
function selectNode(aInspector, aRuleView)
{
inspector = aInspector;
let sidebar = inspector.sidebar;
let iframe = sidebar._tabbox.querySelector(".iframe-ruleview");
let contentDoc = iframe.contentWindow.document;
let contentDoc = aRuleView.doc;
let relative = doc.querySelector(".relative");
let absolute = doc.querySelector(".absolute");
@ -45,35 +34,44 @@ function selectNode(aInspector)
ok(noimage, "captain, we have the noimage div");
inspector.selection.setNode(relative);
is(inspector.selection.node, relative, "selection matches the relative element");
let relativeLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (relativeLink, "Link exists for relative node");
ok (relativeLink.getAttribute("href"), TEST_IMAGE);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, relative, "selection matches the relative element");
let relativeLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (relativeLink, "Link exists for relative node");
ok (relativeLink.getAttribute("href"), TEST_IMAGE);
inspector.selection.setNode(absolute);
is(inspector.selection.node, absolute, "selection matches the absolute element");
let absoluteLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (absoluteLink, "Link exists for absolute node");
ok (absoluteLink.getAttribute("href"), TEST_IMAGE);
inspector.selection.setNode(absolute);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, absolute, "selection matches the absolute element");
let absoluteLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (absoluteLink, "Link exists for absolute node");
ok (absoluteLink.getAttribute("href"), TEST_IMAGE);
inspector.selection.setNode(inline);
is(inspector.selection.node, inline, "selection matches the inline element");
let inlineLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (inlineLink, "Link exists for inline node");
ok (inlineLink.getAttribute("href"), TEST_IMAGE);
inspector.selection.setNode(inline);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, inline, "selection matches the inline element");
let inlineLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (inlineLink, "Link exists for inline node");
ok (inlineLink.getAttribute("href"), TEST_IMAGE);
inspector.selection.setNode(base64);
is(inspector.selection.node, base64, "selection matches the base64 element");
let base64Link = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (base64Link, "Link exists for base64 node");
ok (base64Link.getAttribute("href"), BASE_64_URL);
inspector.selection.setNode(base64);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, base64, "selection matches the base64 element");
let base64Link = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (base64Link, "Link exists for base64 node");
ok (base64Link.getAttribute("href"), BASE_64_URL);
inspector.selection.setNode(noimage);
is(inspector.selection.node, noimage, "selection matches the inline element");
let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (!noimageLink, "There is no link for the node with no background image");
finishUp();
inspector.selection.setNode(noimage);
inspector.once("inspector-updated", () => {
is(inspector.selection.node, noimage, "selection matches the inline element");
let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
ok (!noimageLink, "There is no link for the node with no background image");
finishUp();
});
});
});
});
});
}
function finishUp()
@ -90,7 +88,7 @@ function test()
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
doc = content.document;
waitForFocus(createDocument, content);
waitForFocus(() => openRuleView(selectNode), content);
}, true);
content.location = TEST_URI;

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

@ -3,7 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.debugger.log");
});
let tempScope = {};
Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
let ConsoleUtils = tempScope.ConsoleUtils;
let gDevTools = tempScope.gDevTools;
@ -16,6 +22,8 @@ let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
let promise = devtools.require("sdk/core/promise");
let {
editableField,
getInplaceEditorForSpan: inplaceEditor
@ -40,6 +48,17 @@ function openInspector(callback)
});
}
function openRuleView(callback)
{
openInspector(inspector => {
inspector.sidebar.once("ruleview-ready", () => {
inspector.sidebar.select("ruleview");
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
callback(inspector, ruleView);
})
});
}
function addStyle(aDocument, aString)
{
let node = aDocument.createElement('style');
@ -120,6 +139,21 @@ function contextMenuClick(element) {
element.dispatchEvent(evt);
}
function expectRuleChange(rule) {
return rule._applyingModifications;
}
function promiseDone(promise) {
promise.then(null, err => {
ok(false, "Promise failed: " + err);
if (err.stack) {
dump(err.stack);
}
SimpleTest.finish();
});
}
registerCleanupFunction(tearDown);
waitForExplicitFinish();