mirror of
https://github.com/roytam1/UXP.git
synced 2026-06-10 18:38:34 +00:00
841 lines
29 KiB
JavaScript
841 lines
29 KiB
JavaScript
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
|
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
|
/* import-globals-from ../../test/head.js */
|
|
"use strict";
|
|
|
|
// Import the inspector's head.js first (which itself imports shared-head.js).
|
|
Services.scriptloader.loadSubScript(
|
|
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
|
|
this);
|
|
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref("devtools.defaultColorUnit");
|
|
});
|
|
|
|
var {getInplaceEditorForSpan: inplaceEditor} =
|
|
require("devtools/client/shared/inplace-editor");
|
|
|
|
const ROOT_TEST_DIR = getRootDirectory(gTestPath);
|
|
const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
|
|
|
|
const STYLE_INSPECTOR_L10N
|
|
= new LocalizationHelper("devtools/shared/locales/styleinspector.properties");
|
|
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref("devtools.defaultColorUnit");
|
|
});
|
|
|
|
/**
|
|
* The rule-view tests rely on a frame-script to be injected in the content test
|
|
* page. So override the shared-head's addTab to load the frame script after the
|
|
* tab was added.
|
|
* FIXME: Refactor the rule-view tests to use the testActor instead of a frame
|
|
* script, so they can run on remote targets too.
|
|
*/
|
|
var _addTab = addTab;
|
|
addTab = function (url) {
|
|
return _addTab(url).then(tab => {
|
|
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
|
|
let browser = tab.linkedBrowser;
|
|
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
|
|
return tab;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Wait for a content -> chrome message on the message manager (the window
|
|
* messagemanager is used).
|
|
*
|
|
* @param {String} name
|
|
* The message name
|
|
* @return {Promise} A promise that resolves to the response data when the
|
|
* message has been received
|
|
*/
|
|
function waitForContentMessage(name) {
|
|
info("Expecting message " + name + " from content");
|
|
|
|
let mm = gBrowser.selectedBrowser.messageManager;
|
|
|
|
let def = defer();
|
|
mm.addMessageListener(name, function onMessage(msg) {
|
|
mm.removeMessageListener(name, onMessage);
|
|
def.resolve(msg.data);
|
|
});
|
|
return def.promise;
|
|
}
|
|
|
|
/**
|
|
* Send an async message to the frame script (chrome -> content) and wait for a
|
|
* response message with the same name (content -> chrome).
|
|
*
|
|
* @param {String} name
|
|
* The message name. Should be one of the messages defined
|
|
* in doc_frame_script.js
|
|
* @param {Object} data
|
|
* Optional data to send along
|
|
* @param {Object} objects
|
|
* Optional CPOW objects to send along
|
|
* @param {Boolean} expectResponse
|
|
* If set to false, don't wait for a response with the same name
|
|
* from the content script. Defaults to true.
|
|
* @return {Promise} Resolves to the response data if a response is expected,
|
|
* immediately resolves otherwise
|
|
*/
|
|
function executeInContent(name, data = {}, objects = {},
|
|
expectResponse = true) {
|
|
info("Sending message " + name + " to content");
|
|
let mm = gBrowser.selectedBrowser.messageManager;
|
|
|
|
mm.sendAsyncMessage(name, data, objects);
|
|
if (expectResponse) {
|
|
return waitForContentMessage(name);
|
|
}
|
|
|
|
return promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* Send an async message to the frame script and get back the requested
|
|
* computed style property.
|
|
*
|
|
* @param {String} selector
|
|
* The selector used to obtain the element.
|
|
* @param {String} pseudo
|
|
* pseudo id to query, or null.
|
|
* @param {String} name
|
|
* name of the property.
|
|
*/
|
|
function* getComputedStyleProperty(selector, pseudo, propName) {
|
|
return yield executeInContent("Test:GetComputedStylePropertyValue",
|
|
{selector,
|
|
pseudo,
|
|
name: propName});
|
|
}
|
|
|
|
/**
|
|
* Get an element's inline style property value.
|
|
* @param {TestActor} testActor
|
|
* @param {String} selector
|
|
* The selector used to obtain the element.
|
|
* @param {String} name
|
|
* name of the property.
|
|
*/
|
|
function getStyle(testActor, selector, propName) {
|
|
return testActor.eval(`
|
|
content.document.querySelector("${selector}")
|
|
.style.getPropertyValue("${propName}");
|
|
`);
|
|
}
|
|
|
|
/**
|
|
* Send an async message to the frame script and wait until the requested
|
|
* computed style property has the expected value.
|
|
*
|
|
* @param {String} selector
|
|
* The selector used to obtain the element.
|
|
* @param {String} pseudo
|
|
* pseudo id to query, or null.
|
|
* @param {String} prop
|
|
* name of the property.
|
|
* @param {String} expected
|
|
* expected value of property
|
|
* @param {String} name
|
|
* the name used in test message
|
|
*/
|
|
function* waitForComputedStyleProperty(selector, pseudo, name, expected) {
|
|
return yield executeInContent("Test:WaitForComputedStylePropertyValue",
|
|
{selector,
|
|
pseudo,
|
|
expected,
|
|
name});
|
|
}
|
|
|
|
/**
|
|
* Given an inplace editable element, click to switch it to edit mode, wait for
|
|
* focus
|
|
*
|
|
* @return a promise that resolves to the inplace-editor element when ready
|
|
*/
|
|
var focusEditableField = Task.async(function* (ruleView, editable, xOffset = 1,
|
|
yOffset = 1, options = {}) {
|
|
let onFocus = once(editable.parentNode, "focus", true);
|
|
info("Clicking on editable field to turn to edit mode");
|
|
EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
|
|
editable.ownerDocument.defaultView);
|
|
yield onFocus;
|
|
|
|
info("Editable field gained focus, returning the input field now");
|
|
let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
|
|
|
|
return onEdit;
|
|
});
|
|
|
|
/**
|
|
* When a tooltip is closed, this ends up "commiting" the value changed within
|
|
* the tooltip (e.g. the color in case of a colorpicker) which, in turn, ends up
|
|
* setting the value of the corresponding css property in the rule-view.
|
|
* Use this function to close the tooltip and make sure the test waits for the
|
|
* ruleview-changed event.
|
|
* @param {SwatchBasedEditorTooltip} editorTooltip
|
|
* @param {CSSRuleView} view
|
|
*/
|
|
function* hideTooltipAndWaitForRuleViewChanged(editorTooltip, view) {
|
|
let onModified = view.once("ruleview-changed");
|
|
let onHidden = editorTooltip.tooltip.once("hidden");
|
|
editorTooltip.hide();
|
|
yield onModified;
|
|
yield onHidden;
|
|
}
|
|
|
|
/**
|
|
* Polls a given generator function waiting for it to return true.
|
|
*
|
|
* @param {Function} validatorFn
|
|
* A validator generator function that returns a boolean.
|
|
* This is called every few milliseconds to check if the result is true.
|
|
* When it is true, the promise resolves.
|
|
* @param {String} name
|
|
* Optional name of the test. This is used to generate
|
|
* the success and failure messages.
|
|
* @return a promise that resolves when the function returned true or rejects
|
|
* if the timeout is reached
|
|
*/
|
|
var waitForSuccess = Task.async(function* (validatorFn, desc = "untitled") {
|
|
let i = 0;
|
|
while (true) {
|
|
info("Checking: " + desc);
|
|
if (yield validatorFn()) {
|
|
ok(true, "Success: " + desc);
|
|
break;
|
|
}
|
|
i++;
|
|
if (i > 10) {
|
|
ok(false, "Failure: " + desc);
|
|
break;
|
|
}
|
|
yield new Promise(r => setTimeout(r, 200));
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get the DOMNode for a css rule in the rule-view that corresponds to the given
|
|
* selector
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} selectorText
|
|
* The selector in the rule-view for which the rule
|
|
* object is wanted
|
|
* @return {DOMNode}
|
|
*/
|
|
function getRuleViewRule(view, selectorText) {
|
|
let rule;
|
|
for (let r of view.styleDocument.querySelectorAll(".ruleview-rule")) {
|
|
let selector = r.querySelector(".ruleview-selectorcontainer, " +
|
|
".ruleview-selector-matched");
|
|
if (selector && selector.textContent === selectorText) {
|
|
rule = r;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rule;
|
|
}
|
|
|
|
/**
|
|
* Get references to the name and value span nodes corresponding to a given
|
|
* selector and property name in the rule-view
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} selectorText
|
|
* The selector in the rule-view to look for the property in
|
|
* @param {String} propertyName
|
|
* The name of the property
|
|
* @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
|
|
*/
|
|
function getRuleViewProperty(view, selectorText, propertyName) {
|
|
let prop;
|
|
|
|
let rule = getRuleViewRule(view, selectorText);
|
|
if (rule) {
|
|
// Look for the propertyName in that rule element
|
|
for (let p of rule.querySelectorAll(".ruleview-property")) {
|
|
let nameSpan = p.querySelector(".ruleview-propertyname");
|
|
let valueSpan = p.querySelector(".ruleview-propertyvalue");
|
|
|
|
if (nameSpan.textContent === propertyName) {
|
|
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return prop;
|
|
}
|
|
|
|
/**
|
|
* Get the text value of the property corresponding to a given selector and name
|
|
* in the rule-view
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} selectorText
|
|
* The selector in the rule-view to look for the property in
|
|
* @param {String} propertyName
|
|
* The name of the property
|
|
* @return {String} The property value
|
|
*/
|
|
function getRuleViewPropertyValue(view, selectorText, propertyName) {
|
|
return getRuleViewProperty(view, selectorText, propertyName)
|
|
.valueSpan.textContent;
|
|
}
|
|
|
|
/**
|
|
* Get a reference to the selector DOM element corresponding to a given selector
|
|
* in the rule-view
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} selectorText
|
|
* The selector in the rule-view to look for
|
|
* @return {DOMNode} The selector DOM element
|
|
*/
|
|
function getRuleViewSelector(view, selectorText) {
|
|
let rule = getRuleViewRule(view, selectorText);
|
|
return rule.querySelector(".ruleview-selector, .ruleview-selector-matched");
|
|
}
|
|
|
|
/**
|
|
* Get a reference to the selectorhighlighter icon DOM element corresponding to
|
|
* a given selector in the rule-view
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} selectorText
|
|
* The selector in the rule-view to look for
|
|
* @return {DOMNode} The selectorhighlighter icon DOM element
|
|
*/
|
|
function getRuleViewSelectorHighlighterIcon(view, selectorText) {
|
|
let rule = getRuleViewRule(view, selectorText);
|
|
return rule.querySelector(".ruleview-selectorhighlighter");
|
|
}
|
|
|
|
/**
|
|
* Simulate a color change in a given color picker tooltip, and optionally wait
|
|
* for a given element in the page to have its style changed as a result.
|
|
* Note that this function assumes that the colorpicker popup is already open
|
|
* and it won't close it after having selected the new color.
|
|
*
|
|
* @param {RuleView} ruleView
|
|
* The related rule view instance
|
|
* @param {SwatchColorPickerTooltip} colorPicker
|
|
* @param {Array} newRgba
|
|
* The new color to be set [r, g, b, a]
|
|
* @param {Object} expectedChange
|
|
* Optional object that needs the following props:
|
|
* - {String} selector The selector to the element in the page that
|
|
* will have its style changed.
|
|
* - {String} name The style name that will be changed
|
|
* - {String} value The expected style value
|
|
* The style will be checked like so: getComputedStyle(element)[name] === value
|
|
*/
|
|
var simulateColorPickerChange = Task.async(function* (ruleView, colorPicker,
|
|
newRgba, expectedChange) {
|
|
let onComputedStyleChanged;
|
|
if (expectedChange) {
|
|
let {selector, name, value} = expectedChange;
|
|
onComputedStyleChanged = waitForComputedStyleProperty(selector, null, name, value);
|
|
}
|
|
let onRuleViewChanged = ruleView.once("ruleview-changed");
|
|
info("Getting the spectrum colorpicker object");
|
|
let spectrum = colorPicker.spectrum;
|
|
info("Setting the new color");
|
|
spectrum.rgb = newRgba;
|
|
info("Applying the change");
|
|
spectrum.updateUI();
|
|
spectrum.onChange();
|
|
info("Waiting for rule-view to update");
|
|
yield onRuleViewChanged;
|
|
|
|
if (expectedChange) {
|
|
info("Waiting for the style to be applied on the page");
|
|
yield onComputedStyleChanged;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Open the color picker popup for a given property in a given rule and
|
|
* simulate a color change. Optionally wait for a given element in the page to
|
|
* have its style changed as a result.
|
|
*
|
|
* @param {RuleView} view
|
|
* The related rule view instance
|
|
* @param {Number} ruleIndex
|
|
* Which rule to target in the rule view
|
|
* @param {Number} propIndex
|
|
* Which property to target in the rule
|
|
* @param {Array} newRgba
|
|
* The new color to be set [r, g, b, a]
|
|
* @param {Object} expectedChange
|
|
* Optional object that needs the following props:
|
|
* - {String} selector The selector to the element in the page that
|
|
* will have its style changed.
|
|
* - {String} name The style name that will be changed
|
|
* - {String} value The expected style value
|
|
* The style will be checked like so: getComputedStyle(element)[name] === value
|
|
*/
|
|
var openColorPickerAndSelectColor = Task.async(function* (view, ruleIndex,
|
|
propIndex, newRgba, expectedChange) {
|
|
let ruleEditor = getRuleViewRuleEditor(view, ruleIndex);
|
|
let propEditor = ruleEditor.rule.textProps[propIndex].editor;
|
|
let swatch = propEditor.valueSpan.querySelector(".ruleview-colorswatch");
|
|
let cPicker = view.tooltips.colorPicker;
|
|
|
|
info("Opening the colorpicker by clicking the color swatch");
|
|
let onColorPickerReady = cPicker.once("ready");
|
|
swatch.click();
|
|
yield onColorPickerReady;
|
|
|
|
yield simulateColorPickerChange(view, cPicker, newRgba, expectedChange);
|
|
|
|
return {propEditor, swatch, cPicker};
|
|
});
|
|
|
|
/**
|
|
* Open the cubicbezier popup for a given property in a given rule and
|
|
* simulate a curve change. Optionally wait for a given element in the page to
|
|
* have its style changed as a result.
|
|
*
|
|
* @param {RuleView} view
|
|
* The related rule view instance
|
|
* @param {Number} ruleIndex
|
|
* Which rule to target in the rule view
|
|
* @param {Number} propIndex
|
|
* Which property to target in the rule
|
|
* @param {Array} coords
|
|
* The new coordinates to be used, e.g. [0.1, 2, 0.9, -1]
|
|
* @param {Object} expectedChange
|
|
* Optional object that needs the following props:
|
|
* - {String} selector The selector to the element in the page that
|
|
* will have its style changed.
|
|
* - {String} name The style name that will be changed
|
|
* - {String} value The expected style value
|
|
* The style will be checked like so: getComputedStyle(element)[name] === value
|
|
*/
|
|
var openCubicBezierAndChangeCoords = Task.async(function* (view, ruleIndex,
|
|
propIndex, coords, expectedChange) {
|
|
let ruleEditor = getRuleViewRuleEditor(view, ruleIndex);
|
|
let propEditor = ruleEditor.rule.textProps[propIndex].editor;
|
|
let swatch = propEditor.valueSpan.querySelector(".ruleview-bezierswatch");
|
|
let bezierTooltip = view.tooltips.cubicBezier;
|
|
|
|
info("Opening the cubicBezier by clicking the swatch");
|
|
let onBezierWidgetReady = bezierTooltip.once("ready");
|
|
swatch.click();
|
|
yield onBezierWidgetReady;
|
|
|
|
let widget = yield bezierTooltip.widget;
|
|
|
|
info("Simulating a change of curve in the widget");
|
|
let onRuleViewChanged = view.once("ruleview-changed");
|
|
widget.coordinates = coords;
|
|
yield onRuleViewChanged;
|
|
|
|
if (expectedChange) {
|
|
info("Waiting for the style to be applied on the page");
|
|
let {selector, name, value} = expectedChange;
|
|
yield waitForComputedStyleProperty(selector, null, name, value);
|
|
}
|
|
|
|
return {propEditor, swatch, bezierTooltip};
|
|
});
|
|
|
|
/**
|
|
* Get a rule-link from the rule-view given its index
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {Number} index
|
|
* The index of the link to get
|
|
* @return {DOMNode} The link if any at this index
|
|
*/
|
|
function getRuleViewLinkByIndex(view, index) {
|
|
let links = view.styleDocument.querySelectorAll(".ruleview-rule-source");
|
|
return links[index];
|
|
}
|
|
|
|
/**
|
|
* Get rule-link text from the rule-view given its index
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {Number} index
|
|
* The index of the link to get
|
|
* @return {String} The string at this index
|
|
*/
|
|
function getRuleViewLinkTextByIndex(view, index) {
|
|
let link = getRuleViewLinkByIndex(view, index);
|
|
return link.querySelector(".ruleview-rule-source-label").textContent;
|
|
}
|
|
|
|
/**
|
|
* Simulate adding a new property in an existing rule in the rule-view.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {Number} ruleIndex
|
|
* The index of the rule to use. Note that if ruleIndex is 0, you might
|
|
* want to also listen to markupmutation events in your test since
|
|
* that's going to change the style attribute of the selected node.
|
|
* @param {String} name
|
|
* The name for the new property
|
|
* @param {String} value
|
|
* The value for the new property
|
|
* @param {String} commitValueWith
|
|
* Which key should be used to commit the new value. VK_RETURN is used by
|
|
* default, but tests might want to use another key to test cancelling
|
|
* for exemple.
|
|
* @param {Boolean} blurNewProperty
|
|
* After the new value has been added, a new property would have been
|
|
* focused. This parameter is true by default, and that causes the new
|
|
* property to be blurred. Set to false if you don't want this.
|
|
* @return {TextProperty} The instance of the TextProperty that was added
|
|
*/
|
|
var addProperty = Task.async(function* (view, ruleIndex, name, value,
|
|
commitValueWith = "VK_RETURN",
|
|
blurNewProperty = true) {
|
|
info("Adding new property " + name + ":" + value + " to rule " + ruleIndex);
|
|
|
|
let ruleEditor = getRuleViewRuleEditor(view, ruleIndex);
|
|
let editor = yield focusNewRuleViewProperty(ruleEditor);
|
|
let numOfProps = ruleEditor.rule.textProps.length;
|
|
|
|
info("Adding name " + name);
|
|
editor.input.value = name;
|
|
let onNameAdded = view.once("ruleview-changed");
|
|
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
|
yield onNameAdded;
|
|
|
|
// Focus has moved to the value inplace-editor automatically.
|
|
editor = inplaceEditor(view.styleDocument.activeElement);
|
|
let textProps = ruleEditor.rule.textProps;
|
|
let textProp = textProps[textProps.length - 1];
|
|
|
|
is(ruleEditor.rule.textProps.length, numOfProps + 1,
|
|
"A new test property was added");
|
|
is(editor, inplaceEditor(textProp.editor.valueSpan),
|
|
"The inplace editor appeared for the value");
|
|
|
|
info("Adding value " + value);
|
|
// Setting the input value schedules a preview to be shown in 10ms which
|
|
// triggers a ruleview-changed event (see bug 1209295).
|
|
let onPreview = view.once("ruleview-changed");
|
|
editor.input.value = value;
|
|
view.throttle.flush();
|
|
yield onPreview;
|
|
|
|
let onValueAdded = view.once("ruleview-changed");
|
|
EventUtils.synthesizeKey(commitValueWith, {}, view.styleWindow);
|
|
yield onValueAdded;
|
|
|
|
if (blurNewProperty) {
|
|
view.styleDocument.activeElement.blur();
|
|
}
|
|
|
|
return textProp;
|
|
});
|
|
|
|
/**
|
|
* Simulate changing the value of a property in a rule in the rule-view.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {TextProperty} textProp
|
|
* The instance of the TextProperty to be changed
|
|
* @param {String} value
|
|
* The new value to be used. If null is passed, then the value will be
|
|
* deleted
|
|
* @param {Boolean} blurNewProperty
|
|
* After the value has been changed, a new property would have been
|
|
* focused. This parameter is true by default, and that causes the new
|
|
* property to be blurred. Set to false if you don't want this.
|
|
*/
|
|
var setProperty = Task.async(function* (view, textProp, value,
|
|
blurNewProperty = true) {
|
|
yield focusEditableField(view, textProp.editor.valueSpan);
|
|
|
|
let onPreview = view.once("ruleview-changed");
|
|
if (value === null) {
|
|
EventUtils.synthesizeKey("VK_DELETE", {}, view.styleWindow);
|
|
} else {
|
|
EventUtils.sendString(value, view.styleWindow);
|
|
}
|
|
view.throttle.flush();
|
|
yield onPreview;
|
|
|
|
let onValueDone = view.once("ruleview-changed");
|
|
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
|
yield onValueDone;
|
|
|
|
if (blurNewProperty) {
|
|
view.styleDocument.activeElement.blur();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Simulate removing a property from an existing rule in the rule-view.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {TextProperty} textProp
|
|
* The instance of the TextProperty to be removed
|
|
* @param {Boolean} blurNewProperty
|
|
* After the property has been removed, a new property would have been
|
|
* focused. This parameter is true by default, and that causes the new
|
|
* property to be blurred. Set to false if you don't want this.
|
|
*/
|
|
var removeProperty = Task.async(function* (view, textProp,
|
|
blurNewProperty = true) {
|
|
yield focusEditableField(view, textProp.editor.nameSpan);
|
|
|
|
let onModifications = view.once("ruleview-changed");
|
|
info("Deleting the property name now");
|
|
EventUtils.synthesizeKey("VK_DELETE", {}, view.styleWindow);
|
|
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
|
|
yield onModifications;
|
|
|
|
if (blurNewProperty) {
|
|
view.styleDocument.activeElement.blur();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Simulate clicking the enable/disable checkbox next to a property in a rule.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {TextProperty} textProp
|
|
* The instance of the TextProperty to be enabled/disabled
|
|
*/
|
|
var togglePropStatus = Task.async(function* (view, textProp) {
|
|
let onRuleViewRefreshed = view.once("ruleview-changed");
|
|
textProp.editor.enable.click();
|
|
yield onRuleViewRefreshed;
|
|
});
|
|
|
|
/**
|
|
* Click on a rule-view's close brace to focus a new property name editor
|
|
*
|
|
* @param {RuleEditor} ruleEditor
|
|
* An instance of RuleEditor that will receive the new property
|
|
* @return a promise that resolves to the newly created editor when ready and
|
|
* focused
|
|
*/
|
|
var focusNewRuleViewProperty = Task.async(function* (ruleEditor) {
|
|
info("Clicking on a close ruleEditor brace to start editing a new property");
|
|
|
|
// Use bottom alignment to avoid scrolling out of the parent element area.
|
|
ruleEditor.closeBrace.scrollIntoView(false);
|
|
let editor = yield focusEditableField(ruleEditor.ruleView,
|
|
ruleEditor.closeBrace);
|
|
|
|
is(inplaceEditor(ruleEditor.newPropSpan), editor,
|
|
"Focused editor is the new property editor.");
|
|
|
|
return editor;
|
|
});
|
|
|
|
/**
|
|
* Create a new property name in the rule-view, focusing a new property editor
|
|
* by clicking on the close brace, and then entering the given text.
|
|
* Keep in mind that the rule-view knows how to handle strings with multiple
|
|
* properties, so the input text may be like: "p1:v1;p2:v2;p3:v3".
|
|
*
|
|
* @param {RuleEditor} ruleEditor
|
|
* The instance of RuleEditor that will receive the new property(ies)
|
|
* @param {String} inputValue
|
|
* The text to be entered in the new property name field
|
|
* @return a promise that resolves when the new property name has been entered
|
|
* and once the value field is focused
|
|
*/
|
|
var createNewRuleViewProperty = Task.async(function* (ruleEditor, inputValue) {
|
|
info("Creating a new property editor");
|
|
let editor = yield focusNewRuleViewProperty(ruleEditor);
|
|
|
|
info("Entering the value " + inputValue);
|
|
editor.input.value = inputValue;
|
|
|
|
info("Submitting the new value and waiting for value field focus");
|
|
let onFocus = once(ruleEditor.element, "focus", true);
|
|
EventUtils.synthesizeKey("VK_RETURN", {},
|
|
ruleEditor.element.ownerDocument.defaultView);
|
|
yield onFocus;
|
|
});
|
|
|
|
/**
|
|
* Set the search value for the rule-view filter styles search box.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} searchValue
|
|
* The filter search value
|
|
* @return a promise that resolves when the rule-view is filtered for the
|
|
* search term
|
|
*/
|
|
var setSearchFilter = Task.async(function* (view, searchValue) {
|
|
info("Setting filter text to \"" + searchValue + "\"");
|
|
let win = view.styleWindow;
|
|
let searchField = view.searchField;
|
|
searchField.focus();
|
|
synthesizeKeys(searchValue, win);
|
|
yield view.inspector.once("ruleview-filtered");
|
|
});
|
|
|
|
/**
|
|
* Reload the current page and wait for the inspector to be initialized after
|
|
* the navigation
|
|
*
|
|
* @param {InspectorPanel} inspector
|
|
* The instance of InspectorPanel currently loaded in the toolbox
|
|
* @param {TestActor} testActor
|
|
* The current instance of the TestActor
|
|
*/
|
|
function* reloadPage(inspector, testActor) {
|
|
let onNewRoot = inspector.once("new-root");
|
|
yield testActor.reload();
|
|
yield onNewRoot;
|
|
yield inspector.markup._waitForChildren();
|
|
}
|
|
|
|
/**
|
|
* Create a new rule by clicking on the "add rule" button.
|
|
* This will leave the selector inplace-editor active.
|
|
*
|
|
* @param {InspectorPanel} inspector
|
|
* The instance of InspectorPanel currently loaded in the toolbox
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @return a promise that resolves after the rule has been added
|
|
*/
|
|
function* addNewRule(inspector, view) {
|
|
info("Adding the new rule using the button");
|
|
view.addRuleButton.click();
|
|
|
|
info("Waiting for rule view to change");
|
|
yield view.once("ruleview-changed");
|
|
}
|
|
|
|
/**
|
|
* Create a new rule by clicking on the "add rule" button, dismiss the editor field and
|
|
* verify that the selector is correct.
|
|
*
|
|
* @param {InspectorPanel} inspector
|
|
* The instance of InspectorPanel currently loaded in the toolbox
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {String} expectedSelector
|
|
* The value we expect the selector to have
|
|
* @param {Number} expectedIndex
|
|
* The index we expect the rule to have in the rule-view
|
|
* @return a promise that resolves after the rule has been added
|
|
*/
|
|
function* addNewRuleAndDismissEditor(inspector, view, expectedSelector, expectedIndex) {
|
|
yield addNewRule(inspector, view);
|
|
|
|
info("Getting the new rule at index " + expectedIndex);
|
|
let ruleEditor = getRuleViewRuleEditor(view, expectedIndex);
|
|
let editor = ruleEditor.selectorText.ownerDocument.activeElement;
|
|
is(editor.value, expectedSelector,
|
|
"The editor for the new selector has the correct value: " + expectedSelector);
|
|
|
|
info("Pressing escape to leave the editor");
|
|
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
|
|
|
is(ruleEditor.selectorText.textContent, expectedSelector,
|
|
"The new selector has the correct text: " + expectedSelector);
|
|
}
|
|
|
|
/**
|
|
* Simulate a sequence of non-character keys (return, escape, tab) and wait for
|
|
* a given element to receive the focus.
|
|
*
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @param {DOMNode} element
|
|
* The element that should be focused
|
|
* @param {Array} keys
|
|
* Array of non-character keys, the part that comes after "DOM_VK_" eg.
|
|
* "RETURN", "ESCAPE"
|
|
* @return a promise that resolves after the element received the focus
|
|
*/
|
|
function* sendKeysAndWaitForFocus(view, element, keys) {
|
|
let onFocus = once(element, "focus", true);
|
|
for (let key of keys) {
|
|
EventUtils.sendKey(key, view.styleWindow);
|
|
}
|
|
yield onFocus;
|
|
}
|
|
|
|
/**
|
|
* Open the style editor context menu and return all of it's items in a flat array
|
|
* @param {CssRuleView} view
|
|
* The instance of the rule-view panel
|
|
* @return An array of MenuItems
|
|
*/
|
|
function openStyleContextMenuAndGetAllItems(view, target) {
|
|
let menu = view._contextmenu._openMenu({target: target});
|
|
|
|
// Flatten all menu items into a single array to make searching through it easier
|
|
let allItems = [].concat.apply([], menu.items.map(function addItem(item) {
|
|
if (item.submenu) {
|
|
return addItem(item.submenu.items);
|
|
}
|
|
return item;
|
|
}));
|
|
|
|
return allItems;
|
|
}
|
|
|
|
/**
|
|
* Wait for a markupmutation event on the inspector that is for a style modification.
|
|
* @param {InspectorPanel} inspector
|
|
* @return {Promise}
|
|
*/
|
|
function waitForStyleModification(inspector) {
|
|
return new Promise(function (resolve) {
|
|
function checkForStyleModification(name, mutations) {
|
|
for (let mutation of mutations) {
|
|
if (mutation.type === "attributes" && mutation.attributeName === "style") {
|
|
inspector.off("markupmutation", checkForStyleModification);
|
|
resolve();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
inspector.on("markupmutation", checkForStyleModification);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Click on the selector icon
|
|
* @param {DOMNode} icon
|
|
* @param {CSSRuleView} view
|
|
*/
|
|
function* clickSelectorIcon(icon, view) {
|
|
let onToggled = view.once("ruleview-selectorhighlighter-toggled");
|
|
EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
|
|
yield onToggled;
|
|
}
|
|
|
|
/**
|
|
* Make sure window is properly focused before sending a key event.
|
|
* @param {Window} win
|
|
* @param {Event} key
|
|
*/
|
|
function focusAndSendKey(win, key) {
|
|
win.document.documentElement.focus();
|
|
EventUtils.sendKey(key, win);
|
|
}
|