browser/devtools/styleinspector/test/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     2 /* Any copyright is dedicated to the Public Domain.
     3  http://creativecommons.org/publicdomain/zero/1.0/ */
     5 "use strict";
     7 const Cu = Components.utils;
     8 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
     9 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
    10 let TargetFactory = devtools.TargetFactory;
    11 let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
    12 let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
    13 let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
    14 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
    15 let {editableField, getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
    16 let {console} = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
    18 // All test are asynchronous
    19 waitForExplicitFinish();
    21 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/styleinspector/test/";
    22 const TEST_URL_ROOT_SSL = "https://example.com/browser/browser/devtools/styleinspector/test/";
    24 // Auto clean-up when a test ends
    25 registerCleanupFunction(() => {
    26   try {
    27     let target = TargetFactory.forTab(gBrowser.selectedTab);
    28     gDevTools.closeToolbox(target);
    29   } catch (ex) {
    30     dump(ex);
    31   }
    32   while (gBrowser.tabs.length > 1) {
    33     gBrowser.removeCurrentTab();
    34   }
    35 });
    37 // Uncomment to log events
    38 // Services.prefs.setBoolPref("devtools.dump.emit", true);
    40 // Clean-up all prefs that might have been changed during a test run
    41 // (safer here because if the test fails, then the pref is never reverted)
    42 registerCleanupFunction(() => {
    43   Services.prefs.clearUserPref("devtools.dump.emit");
    44   Services.prefs.clearUserPref("devtools.defaultColorUnit");
    45 });
    47 /**
    48  * The functions found below are here to ease test development and maintenance.
    49  * Most of these functions are stateless and will require some form of context
    50  * (the instance of the current toolbox, or inspector panel for instance).
    51  *
    52  * Most of these functions are async too and return promises.
    53  *
    54  * All tests should follow the following pattern:
    55  *
    56  * let test = asyncTest(function*() {
    57  *   yield addTab(TEST_URI);
    58  *   let {toolbox, inspector, view} = yield openComputedView();
    59  *
    60  *   yield selectNode("#test", inspector);
    61  *   yield someAsyncTestFunction(view);
    62  * });
    63  *
    64  * asyncTest is the way to define the testcase in the test file. It accepts
    65  * a single generator-function argument.
    66  * The generator function should yield any async call.
    67  *
    68  * There is no need to clean tabs up at the end of a test as this is done
    69  * automatically.
    70  *
    71  * It is advised not to store any references on the global scope. There shouldn't
    72  * be a need to anyway. Thanks to asyncTest, test steps, even though asynchronous,
    73  * can be described in a nice flat way, and if/for/while/... control flow can be
    74  * used as in sync code, making it possible to write the outline of the test case
    75  * all in asyncTest, and delegate actual processing and assertions to other
    76  * functions.
    77  */
    79 /* *********************************************
    80  * UTILS
    81  * *********************************************
    82  * General test utilities.
    83  * Define the test case, add new tabs, open the toolbox and switch to the
    84  * various panels, select nodes, get node references, ...
    85  */
    87 /**
    88  * Define an async test based on a generator function
    89  */
    90 function asyncTest(generator) {
    91   return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
    92 }
    94 /**
    95  * Add a new test tab in the browser and load the given url.
    96  * @param {String} url The url to be loaded in the new tab
    97  * @return a promise that resolves to the tab object when the url is loaded
    98  */
    99 function addTab(url) {
   100   let def = promise.defer();
   102   let tab = gBrowser.selectedTab = gBrowser.addTab();
   103   gBrowser.selectedBrowser.addEventListener("load", function onload() {
   104     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
   105     info("URL " + url + " loading complete into new test tab");
   106     waitForFocus(() => {
   107       def.resolve(tab);
   108     }, content);
   109   }, true);
   110   content.location = url;
   112   return def.promise;
   113 }
   115 /**
   116  * Simple DOM node accesor function that takes either a node or a string css
   117  * selector as argument and returns the corresponding node
   118  * @param {String|DOMNode} nodeOrSelector
   119  * @return {DOMNode}
   120  */
   121 function getNode(nodeOrSelector) {
   122   return typeof nodeOrSelector === "string" ?
   123     content.document.querySelector(nodeOrSelector) :
   124     nodeOrSelector;
   125 }
   127 /**
   128  * Set the inspector's current selection to a node or to the first match of the
   129  * given css selector
   130  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   131  * loaded in the toolbox
   132  * @param {String} reason Defaults to "test" which instructs the inspector not
   133  * to highlight the node upon selection
   134  * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
   135  * @return a promise that resolves when the inspector is updated with the new
   136  * node
   137  */
   138 function selectNode(nodeOrSelector, inspector, reason="test") {
   139   info("Selecting the node " + nodeOrSelector);
   140   let node = getNode(nodeOrSelector);
   141   let updated = inspector.once("inspector-updated");
   142   inspector.selection.setNode(node, reason);
   143   return updated;
   144 }
   146 /**
   147  * Set the inspector's current selection to null so that no node is selected
   148  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   149  * loaded in the toolbox
   150  * @return a promise that resolves when the inspector is updated
   151  */
   152 function clearCurrentNodeSelection(inspector) {
   153   info("Clearing the current selection");
   154   let updated = inspector.once("inspector-updated");
   155   inspector.selection.setNode(null);
   156   return updated;
   157 }
   159 /**
   160  * Open the toolbox, with the inspector tool visible.
   161  * @return a promise that resolves when the inspector is ready
   162  */
   163 let openInspector = Task.async(function*() {
   164   info("Opening the inspector");
   165   let target = TargetFactory.forTab(gBrowser.selectedTab);
   167   let inspector, toolbox;
   169   // Checking if the toolbox and the inspector are already loaded
   170   // The inspector-updated event should only be waited for if the inspector
   171   // isn't loaded yet
   172   toolbox = gDevTools.getToolbox(target);
   173   if (toolbox) {
   174     inspector = toolbox.getPanel("inspector");
   175     if (inspector) {
   176       info("Toolbox and inspector already open");
   177       return {
   178         toolbox: toolbox,
   179         inspector: inspector
   180       };
   181     }
   182   }
   184   info("Opening the toolbox");
   185   toolbox = yield gDevTools.showToolbox(target, "inspector");
   186   yield waitForToolboxFrameFocus(toolbox);
   187   inspector = toolbox.getPanel("inspector");
   189   info("Waiting for the inspector to update");
   190   yield inspector.once("inspector-updated");
   192   return {
   193     toolbox: toolbox,
   194     inspector: inspector
   195   };
   196 });
   198 /**
   199  * Wait for the toolbox frame to receive focus after it loads
   200  * @param {Toolbox} toolbox
   201  * @return a promise that resolves when focus has been received
   202  */
   203 function waitForToolboxFrameFocus(toolbox) {
   204   info("Making sure that the toolbox's frame is focused");
   205   let def = promise.defer();
   206   let win = toolbox.frame.contentWindow;
   207   waitForFocus(def.resolve, win);
   208   return def.promise;
   209 }
   211 /**
   212  * Open the toolbox, with the inspector tool visible, and the sidebar that
   213  * corresponds to the given id selected
   214  * @return a promise that resolves when the inspector is ready and the sidebar
   215  * view is visible and ready
   216  */
   217 let openInspectorSideBar = Task.async(function*(id) {
   218   let {toolbox, inspector} = yield openInspector();
   220   if (!hasSideBarTab(inspector, id)) {
   221     info("Waiting for the " + id + " sidebar to be ready");
   222     yield inspector.sidebar.once(id + "-ready");
   223   }
   225   info("Selecting the " + id + " sidebar");
   226   inspector.sidebar.select(id);
   228   return {
   229     toolbox: toolbox,
   230     inspector: inspector,
   231     view: inspector.sidebar.getWindowForTab(id)[id].view
   232   };
   233 });
   235 /**
   236  * Open the toolbox, with the inspector tool visible, and the computed-view
   237  * sidebar tab selected.
   238  * @return a promise that resolves when the inspector is ready and the computed
   239  * view is visible and ready
   240  */
   241 function openComputedView() {
   242   return openInspectorSideBar("computedview");
   243 }
   245 /**
   246  * Open the toolbox, with the inspector tool visible, and the rule-view
   247  * sidebar tab selected.
   248  * @return a promise that resolves when the inspector is ready and the rule
   249  * view is visible and ready
   250  */
   251 function openRuleView() {
   252   return openInspectorSideBar("ruleview");
   253 }
   255 /**
   256  * Wait for eventName on target.
   257  * @param {Object} target An observable object that either supports on/off or
   258  * addEventListener/removeEventListener
   259  * @param {String} eventName
   260  * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
   261  * @return A promise that resolves when the event has been handled
   262  */
   263 function once(target, eventName, useCapture=false) {
   264   info("Waiting for event: '" + eventName + "' on " + target + ".");
   266   let deferred = promise.defer();
   268   for (let [add, remove] of [
   269     ["addEventListener", "removeEventListener"],
   270     ["addListener", "removeListener"],
   271     ["on", "off"]
   272   ]) {
   273     if ((add in target) && (remove in target)) {
   274       target[add](eventName, function onEvent(...aArgs) {
   275         target[remove](eventName, onEvent, useCapture);
   276         deferred.resolve.apply(deferred, aArgs);
   277       }, useCapture);
   278       break;
   279     }
   280   }
   282   return deferred.promise;
   283 }
   285 /**
   286  * This shouldn't be used in the tests, but is useful when writing new tests or
   287  * debugging existing tests in order to introduce delays in the test steps
   288  * @param {Number} ms The time to wait
   289  * @return A promise that resolves when the time is passed
   290  */
   291 function wait(ms) {
   292   let def = promise.defer();
   293   content.setTimeout(def.resolve, ms);
   294   return def.promise;
   295 }
   297 /**
   298  * Given an inplace editable element, click to switch it to edit mode, wait for
   299  * focus
   300  * @return a promise that resolves to the inplace-editor element when ready
   301  */
   302 let focusEditableField = Task.async(function*(editable, xOffset=1, yOffset=1, options={}) {
   303   let onFocus = once(editable.parentNode, "focus", true);
   305   info("Clicking on editable field to turn to edit mode");
   306   EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
   307     editable.ownerDocument.defaultView);
   308   let event = yield onFocus;
   310   info("Editable field gained focus, returning the input field now");
   311   return inplaceEditor(editable.ownerDocument.activeElement);
   312 });
   314 /**
   315  * Given a tooltip object instance (see Tooltip.js), checks if it is set to
   316  * toggle and hover and if so, checks if the given target is a valid hover target.
   317  * This won't actually show the tooltip (the less we interact with XUL panels
   318  * during test runs, the better).
   319  * @return a promise that resolves when the answer is known
   320  */
   321 function isHoverTooltipTarget(tooltip, target) {
   322   if (!tooltip._basedNode || !tooltip.panel) {
   323     return promise.reject(new Error(
   324       "The tooltip passed isn't set to toggle on hover or is not a tooltip"));
   325   }
   326   return tooltip.isValidHoverTarget(target);
   327 }
   329 /**
   330  * Same as isHoverTooltipTarget except that it will fail the test if there is no
   331  * tooltip defined on hover of the given element
   332  * @return a promise
   333  */
   334 function assertHoverTooltipOn(tooltip, element) {
   335   return isHoverTooltipTarget(tooltip, element).then(() => {
   336     ok(true, "A tooltip is defined on hover of the given element");
   337   }, () => {
   338     ok(false, "No tooltip is defined on hover of the given element");
   339   });
   340 }
   342 /**
   343  * Same as assertHoverTooltipOn but fails the test if there is a tooltip defined
   344  * on hover of the given element
   345  * @return a promise
   346  */
   347 function assertNoHoverTooltipOn(tooltip, element) {
   348   return isHoverTooltipTarget(tooltip, element).then(() => {
   349     ok(false, "A tooltip is defined on hover of the given element");
   350   }, () => {
   351     ok(true, "No tooltip is defined on hover of the given element");
   352   });
   353 }
   355 /**
   356  * Listen for a new window to open and return a promise that resolves when one
   357  * does and completes its load.
   358  * Only resolves when the new window topic isn't domwindowopened.
   359  * @return a promise that resolves to the window object
   360  */
   361 function waitForWindow() {
   362   let def = promise.defer();
   364   info("Waiting for a window to open");
   365   Services.ww.registerNotification(function onWindow(subject, topic) {
   366     if (topic != "domwindowopened") {
   367       return;
   368     }
   369     info("A window has been opened");
   370     let win = subject.QueryInterface(Ci.nsIDOMWindow);
   371     once(win, "load").then(() => {
   372       info("The window load completed");
   373       Services.ww.unregisterNotification(onWindow);
   374       def.resolve(win);
   375     });
   376   });
   378   return def.promise;
   379 }
   381 /**
   382  * @see SimpleTest.waitForClipboard
   383  * @param {Function} setup Function to execute before checking for the
   384  * clipboard content
   385  * @param {String|Boolean} expected An expected string or validator function
   386  * @return a promise that resolves when the expected string has been found or
   387  * the validator function has returned true, rejects otherwise.
   388  */
   389 function waitForClipboard(setup, expected) {
   390   let def = promise.defer();
   391   SimpleTest.waitForClipboard(expected, setup, def.resolve, def.reject);
   392   return def.promise;
   393 }
   395 /**
   396  * Dispatch the copy event on the given element
   397  */
   398 function fireCopyEvent(element) {
   399   let evt = element.ownerDocument.createEvent("Event");
   400   evt.initEvent("copy", true, true);
   401   element.dispatchEvent(evt);
   402 }
   404 /**
   405  * Polls a given function waiting for it to return true.
   406  *
   407  * @param {Function} validatorFn A validator function that returns a boolean.
   408  * This is called every few milliseconds to check if the result is true. When
   409  * it is true, the promise resolves. If validatorFn never returns true, then
   410  * polling timeouts after several tries and the promise rejects.
   411  * @param {String} name Optional name of the test. This is used to generate
   412  * the success and failure messages.
   413  * @param {Number} timeout Optional timeout for the validator function, in
   414  * milliseconds. Default is 5000.
   415  * @return a promise that resolves when the function returned true or rejects
   416  * if the timeout is reached
   417  */
   418 function waitForSuccess(validatorFn, name="untitled", timeout=5000) {
   419   let def = promise.defer();
   420   let start = Date.now();
   422   function wait(validatorFn) {
   423     if ((Date.now() - start) > timeout) {
   424       ok(false, "Validator function " + name + " timed out");
   425       return def.reject();
   426     }
   427     if (validatorFn()) {
   428       ok(true, "Validator function " + name + " returned true");
   429       def.resolve();
   430     } else {
   431       setTimeout(() => wait(validatorFn), 100);
   432     }
   433   }
   434   wait(validatorFn);
   436   return def.promise;
   437 }
   439 /**
   440  * Create a new style tag containing the given style text and append it to the
   441  * document's head node
   442  * @param {Document} doc
   443  * @param {String} style
   444  * @return {DOMNode} The newly created style node
   445  */
   446 function addStyle(doc, style) {
   447   info("Adding a new style tag to the document with style content: " +
   448     style.substring(0, 50));
   449   let node = doc.createElement('style');
   450   node.setAttribute("type", "text/css");
   451   node.textContent = style;
   452   doc.getElementsByTagName("head")[0].appendChild(node);
   453   return node;
   454 }
   456 /**
   457  * Checks whether the inspector's sidebar corresponding to the given id already
   458  * exists
   459  * @param {InspectorPanel}
   460  * @param {String}
   461  * @return {Boolean}
   462  */
   463 function hasSideBarTab(inspector, id) {
   464   return !!inspector.sidebar.getWindowForTab(id);
   465 }
   467 /* *********************************************
   468  * RULE-VIEW
   469  * *********************************************
   470  * Rule-view related test utility functions
   471  * This object contains functions to get rules, get properties, ...
   472  */
   474 /**
   475  * Get the DOMNode for a css rule in the rule-view that corresponds to the given
   476  * selector
   477  * @param {CssRuleView} view The instance of the rule-view panel
   478  * @param {String} selectorText The selector in the rule-view for which the rule
   479  * object is wanted
   480  * @return {DOMNode}
   481  */
   482 function getRuleViewRule(view, selectorText) {
   483   let rule;
   484   for (let r of view.doc.querySelectorAll(".ruleview-rule")) {
   485     let selector = r.querySelector(".ruleview-selector, .ruleview-selector-matched");
   486     if (selector && selector.textContent === selectorText) {
   487       rule = r;
   488       break;
   489     }
   490   }
   492   return rule;
   493 }
   495 /**
   496  * Get references to the name and value span nodes corresponding to a given
   497  * selector and property name in the rule-view
   498  * @param {CssRuleView} view The instance of the rule-view panel
   499  * @param {String} selectorText The selector in the rule-view to look for the
   500  * property in
   501  * @param {String} propertyName The name of the property
   502  * @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
   503  */
   504 function getRuleViewProperty(view, selectorText, propertyName) {
   505   let prop;
   507   let rule = getRuleViewRule(view, selectorText);
   508   if (rule) {
   509     // Look for the propertyName in that rule element
   510     for (let p of rule.querySelectorAll(".ruleview-property")) {
   511       let nameSpan = p.querySelector(".ruleview-propertyname");
   512       let valueSpan = p.querySelector(".ruleview-propertyvalue");
   514       if (nameSpan.textContent === propertyName) {
   515         prop = {nameSpan: nameSpan, valueSpan: valueSpan};
   516         break;
   517       }
   518     }
   519   }
   520   return prop;
   521 }
   523 /**
   524  * Get the text value of the property corresponding to a given selector and name
   525  * in the rule-view
   526  * @param {CssRuleView} view The instance of the rule-view panel
   527  * @param {String} selectorText The selector in the rule-view to look for the
   528  * property in
   529  * @param {String} propertyName The name of the property
   530  * @return {String} The property value
   531  */
   532 function getRuleViewPropertyValue(view, selectorText, propertyName) {
   533   return getRuleViewProperty(view, selectorText, propertyName)
   534     .valueSpan.textContent;
   535 }
   537 /**
   538  * Simulate a color change in a given color picker tooltip, and optionally wait
   539  * for a given element in the page to have its style changed as a result
   540  * @param {SwatchColorPickerTooltip} colorPicker
   541  * @param {Array} newRgba The new color to be set [r, g, b, a]
   542  * @param {Object} expectedChange Optional object that needs the following props:
   543  *                 - {DOMNode} element The element in the page that will have its
   544  *                   style changed.
   545  *                 - {String} name The style name that will be changed
   546  *                 - {String} value The expected style value
   547  * The style will be checked like so: getComputedStyle(element)[name] === value
   548  */
   549 let simulateColorPickerChange = Task.async(function*(colorPicker, newRgba, expectedChange) {
   550   info("Getting the spectrum colorpicker object");
   551   let spectrum = yield colorPicker.spectrum;
   552   info("Setting the new color");
   553   spectrum.rgb = newRgba;
   554   info("Applying the change");
   555   spectrum.updateUI();
   556   spectrum.onChange();
   558   if (expectedChange) {
   559     info("Waiting for the style to be applied on the page");
   560     yield waitForSuccess(() => {
   561       let {element, name, value} = expectedChange;
   562       return content.getComputedStyle(element)[name] === value;
   563     }, "Color picker change applied on the page");
   564   }
   565 });
   567 /**
   568  * Get a rule-link from the rule-view given its index
   569  * @param {CssRuleView} view The instance of the rule-view panel
   570  * @param {Number} index The index of the link to get
   571  * @return {DOMNode} The link if any at this index
   572  */
   573 function getRuleViewLinkByIndex(view, index) {
   574   let links = view.doc.querySelectorAll(".ruleview-rule-source");
   575   return links[index];
   576 }
   578 /**
   579  * Click on a rule-view's close brace to focus a new property name editor
   580  * @param {RuleEditor} ruleEditor An instance of RuleEditor that will receive
   581  * the new property
   582  * @return a promise that resolves to the newly created editor when ready and
   583  * focused
   584  */
   585 let focusNewRuleViewProperty = Task.async(function*(ruleEditor) {
   586   info("Clicking on a close ruleEditor brace to start editing a new property");
   587   ruleEditor.closeBrace.scrollIntoView();
   588   let editor = yield focusEditableField(ruleEditor.closeBrace);
   590   is(inplaceEditor(ruleEditor.newPropSpan), editor, "Focused editor is the new property editor.");
   591   is(ruleEditor.rule.textProps.length,  0, "Starting with one new text property.");
   592   is(ruleEditor.propertyList.children.length, 1, "Starting with two property editors.");
   594   return editor;
   595 });
   597 /**
   598  * Create a new property name in the rule-view, focusing a new property editor
   599  * by clicking on the close brace, and then entering the given text.
   600  * Keep in mind that the rule-view knows how to handle strings with multiple
   601  * properties, so the input text may be like: "p1:v1;p2:v2;p3:v3".
   602  * @param {RuleEditor} ruleEditor The instance of RuleEditor that will receive
   603  * the new property(ies)
   604  * @param {String} inputValue The text to be entered in the new property name
   605  * field
   606  * @return a promise that resolves when the new property name has been entered
   607  * and once the value field is focused
   608  */
   609 let createNewRuleViewProperty = Task.async(function*(ruleEditor, inputValue) {
   610   info("Creating a new property editor");
   611   let editor = yield focusNewRuleViewProperty(ruleEditor);
   613   info("Entering the value " + inputValue);
   614   editor.input.value = inputValue;
   616   info("Submitting the new value and waiting for value field focus");
   617   let onFocus = once(ruleEditor.element, "focus", true);
   618   EventUtils.synthesizeKey("VK_RETURN", {},
   619     ruleEditor.element.ownerDocument.defaultView);
   620   yield onFocus;
   621 });
   623 // TO BE UNCOMMENTED WHEN THE EYEDROPPER FINALLY LANDS
   624 // /**
   625 //  * Given a color swatch in the ruleview, click on it to open the color picker
   626 //  * and then click on the eyedropper button to start the eyedropper tool
   627 //  * @param {CssRuleView} view The instance of the rule-view panel
   628 //  * @param {DOMNode} swatch The color swatch to be clicked on
   629 //  * @return A promise that resolves when the dropper is opened
   630 //  */
   631 // let openRuleViewEyeDropper = Task.async(function*(view, swatch) {
   632 //   info("Opening the colorpicker tooltip on a colorswatch");
   633 //   let tooltip = view.colorPicker.tooltip;
   634 //   let onTooltipShown = tooltip.once("shown");
   635 //   swatch.click();
   636 //   yield onTooltipShown;
   638 //   info("Finding the eyedropper icon in the colorpicker document");
   639 //   let tooltipDoc = tooltip.content.contentDocument;
   640 //   let dropperButton = tooltipDoc.querySelector("#eyedropper-button");
   641 //   ok(dropperButton, "Found the eyedropper icon");
   643 //   info("Opening the eyedropper");
   644 //   let onOpen = tooltip.once("eyedropper-opened");
   645 //   dropperButton.click();
   646 //   return yield onOpen;
   647 // });
   649 /* *********************************************
   650  * COMPUTED-VIEW
   651  * *********************************************
   652  * Computed-view related utility functions.
   653  * Allows to get properties, links, expand properties, ...
   654  */
   656 /**
   657  * Get references to the name and value span nodes corresponding to a given
   658  * property name in the computed-view
   659  * @param {CssHtmlTree} view The instance of the computed view panel
   660  * @param {String} name The name of the property to retrieve
   661  * @return an object {nameSpan, valueSpan}
   662  */
   663 function getComputedViewProperty(view, name) {
   664   let prop;
   665   for (let property of view.styleDocument.querySelectorAll(".property-view")) {
   666     let nameSpan = property.querySelector(".property-name");
   667     let valueSpan = property.querySelector(".property-value");
   669     if (nameSpan.textContent === name) {
   670       prop = {nameSpan: nameSpan, valueSpan: valueSpan};
   671       break;
   672     }
   673   }
   674   return prop;
   675 }
   677 /**
   678  * Get the text value of the property corresponding to a given name in the
   679  * computed-view
   680  * @param {CssHtmlTree} view The instance of the computed view panel
   681  * @param {String} name The name of the property to retrieve
   682  * @return {String} The property value
   683  */
   684 function getComputedViewPropertyValue(view, selectorText, propertyName) {
   685   return getComputedViewProperty(view, selectorText, propertyName)
   686     .valueSpan.textContent;
   687 }
   689 /**
   690  * Expand a given property, given its index in the current property list of
   691  * the computed view
   692  * @param {CssHtmlTree} view The instance of the computed view panel
   693  * @param {InspectorPanel} inspector The instance of the inspector panel
   694  * @param {Number} index The index of the property to be expanded
   695  * @return a promise that resolves when the property has been expanded, or
   696  * rejects if the property was not found
   697  */
   698 function expandComputedViewPropertyByIndex(view, inspector, index) {
   699   info("Expanding property " + index + " in the computed view");
   700   let expandos = view.styleDocument.querySelectorAll(".expandable");
   701   if (!expandos.length || !expandos[index]) {
   702     return promise.reject();
   703   }
   705   let onExpand = inspector.once("computed-view-property-expanded");
   706   expandos[index].click();
   707   return onExpand;
   708 }
   710 /**
   711  * Get a rule-link from the computed-view given its index
   712  * @param {CssHtmlTree} view The instance of the computed view panel
   713  * @param {Number} index The index of the link to be retrieved
   714  * @return {DOMNode} The link at the given index, if one exists, null otherwise
   715  */
   716 function getComputedViewLinkByIndex(view, index) {
   717   let links = view.styleDocument.querySelectorAll(".rule-link .link");
   718   return links[index];
   719 }
   721 /* *********************************************
   722  * STYLE-EDITOR
   723  * *********************************************
   724  * Style-editor related utility functions.
   725  */
   727 /**
   728  * Wait for the toolbox to emit the styleeditor-selected event and when done
   729  * wait for the stylesheet identified by href to be loaded in the stylesheet
   730  * editor
   731  * @param {Toolbox} toolbox
   732  * @param {String} href Optional, if not provided, wait for the first editor
   733  * to be ready
   734  * @return a promise that resolves to the editor when the stylesheet editor is
   735  * ready
   736  */
   737 function waitForStyleEditor(toolbox, href) {
   738   let def = promise.defer();
   740   info("Waiting for the toolbox to switch to the styleeditor");
   741   toolbox.once("styleeditor-ready").then(() => {
   742     let panel = toolbox.getCurrentPanel();
   743     ok(panel && panel.UI, "Styleeditor panel switched to front");
   745     panel.UI.on("editor-selected", function onEditorSelected(event, editor) {
   746       let currentHref = editor.styleSheet.href;
   747       if (!href || (href && currentHref.endsWith(href))) {
   748         info("Stylesheet editor selected");
   749         panel.UI.off("editor-selected", onEditorSelected);
   750         editor.getSourceEditor().then(editor => {
   751           info("Stylesheet editor fully loaded");
   752           def.resolve(editor);
   753         });
   754       }
   755     });
   756   });
   758   return def.promise;
   759 }

mercurial