diff options
Diffstat (limited to 'devtools/client/inspector/rules/test/head.js')
-rw-r--r-- | devtools/client/inspector/rules/test/head.js | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/devtools/client/inspector/rules/test/head.js b/devtools/client/inspector/rules/test/head.js new file mode 100644 index 000000000..5e5ede09b --- /dev/null +++ b/devtools/client/inspector/rules/test/head.js @@ -0,0 +1,840 @@ +/* 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); +} |