1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is MozMill Test code.
16 * The Initial Developer of the Original Code is the Mozilla Foundation.
17 * Portions created by the Initial Developer are Copyright (C) 2010
18 * the Initial Developer. All Rights Reserved.
21 * Adrian Kalla <akalla@aviary.pl>
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 // Include required modules
38 var domUtils = require("dom-utils");
39 var screenshot = require("screenshot");
40 var utils = require("utils");
43 Components.utils.import("resource://mozmill/modules/jum.js", jumlib);
46 * Callback function for parsing the results of testing for duplicated
49 * This function processes the access keys found in one access keys scope
50 * looking for access keys that are listed more than one time.
51 * At the end, it calls the screenshot.create to create a screenshot with the
52 * elements containing the broken access keys highlighted.
54 * @param {array of array of object} accessKeysSet
55 * @param {MozmillController} controller
57 function checkAccessKeysResults(controller, accessKeysSet) {
58 // Sort the access keys to have them in a A->Z order
59 var accessKeysList = accessKeysSet.sort();
61 // List of access keys
64 // List of values to identify the access keys
67 // List of rectangles of nodes containing access keys
70 // List of rectangles of nodes with broken access keys
73 // Makes lists of all access keys and the values the access keys are in
74 for (var i = 0; i < accessKeysList.length; i++) {
75 var accessKey = accessKeysList[i][0];
76 var node = accessKeysList[i][1];
78 // Set the id and label to be shown in the console
79 var id = node.id || "(id is undefined)";
80 var label = node.label || "(label is undefined)";
82 var box = node.boxObject;
87 // if the access key is already in our list, take it out to replace it
89 if (accessKey == aKeysList[aKeysList.length-1]) {
90 innerIds = valueList.pop();
91 innerRects = rects.pop();
93 aKeysList.push([accessKey]);
95 innerIds.push("[id: " + id + ", label: " + label + "]");
96 valueList.push(innerIds);
97 innerRects.push([box.x, box.y, box.width, box.height]);
98 rects.push(innerRects);
101 // Go through all access keys and find the duplicated ones
102 for (var i = 0; i < valueList.length; i++) {
103 // Only access keys contained in more than one node are the ones we are
105 if (valueList[i].length > 1) {
106 for (var j = 0; j < rects[i].length; j++) {
107 badRects.push(rects[i][j]);
109 jumlib.assert(false, 'accessKey: ' + aKeysList[i] +
110 ' found in string\'s: ' + valueList[i].join(", "));
114 // If we have found broken access keys, make a screenshot
115 if (badRects.length > 0) {
116 screenshot.create(controller, badRects);
121 * Callback function for testing for cropped elements.
123 * Checks if the XUL boxObject has screen coordinates outside of
124 * the screen coordinates of its parent. If there's no parent, return.
126 * @param {node} child
127 * @returns List of boxes that can be highlighted on a screenshot
128 * @type {array of array of int}
130 function checkDimensions(child) {
131 if (!child.boxObject)
133 var childBox = child.boxObject;
134 var parent = childBox.parentBox;
136 // toplevel element or hidden elements, like script tags
137 if (!parent || parent == child.element || !parent.boxObject) {
140 var parentBox = parent.boxObject;
145 if (childBox.height && childBox.screenX < parentBox.screenX) {
146 badRects.push([childBox.x, childBox.y, parentBox.x - childBox.x,
148 jumlib.assert(false, 'Node is cut off at the left: ' +
149 _reportNode(child) + '. Parent node: ' + _reportNode(parent));
151 if (childBox.height && childBox.screenX + childBox.width >
152 parentBox.screenX + parentBox.width) {
153 badRects.push([parentBox.x + parentBox.width, childBox.y,
154 childBox.x + childBox.width - parentBox.x - parentBox.width,
156 jumlib.assert(false, 'Node is cut off at the right: ' +
157 _reportNode(child) + '. Parent node: ' + _reportNode(parent));
161 // We don't want to test menupopup's, as they always report the full height
162 // of all items in the popup
163 if (child.nodeName != 'menupopup' && parent.nodeName != 'menupopup') {
164 if (childBox.width && childBox.screenY < parentBox.screenY) {
165 badRects.push([childBox.x, childBox.y, parentBox.y - childBox.y,
167 jumlib.assert(false, 'Node is cut off at the top: ' +
168 _reportNode(child) + '. Parent node: ' + _reportNode(parent));
170 if (childBox.width && childBox.screenY + childBox.height >
171 parentBox.screenY + parentBox.height) {
172 badRects.push([childBox.x, parentBox.y + parentBox.height,
174 childBox.y + childBox.height - parentBox.y - parentBox.height]);
175 jumlib.assert(false, 'Node is cut off at the bottom: ' +
176 _reportNode(child) + '. Parent node: ' + _reportNode(parent));
184 * Filters out nodes which should not be tested because they are not in the
185 * current access key scope.
188 * @returns Filter status of the given node
189 * @type {array of array of int}
191 function filterAccessKeys(node) {
192 // Menus will need a separate filter set
193 var notAllowedLocalNames = ["menu", "menubar", "menupopup", "popupset"];
195 if (!node.disabled && !node.collapsed && !node.hidden &&
196 notAllowedLocalNames.indexOf(node.localName) == -1) {
197 // Code specific to the preferences panes to reject out not visible nodes
199 if (node.parentNode && (node.parentNode.localName == "prefwindow" &&
200 node.parentNode.currentPane.id != node.id) ||
201 ((node.parentNode.localName == "tabpanels" ||
202 node.parentNode.localName == "deck") &&
203 node.parentNode.selectedPanel.id != node.id)) {
204 return domUtils.DOMWalker.FILTER_REJECT;
205 // end of the specific code
206 } else if (node.accessKey) {
207 return domUtils.DOMWalker.FILTER_ACCEPT;
209 return domUtils.DOMWalker.FILTER_SKIP;
212 // we don't want to test not visible elements
213 return domUtils.DOMWalker.FILTER_REJECT;
218 * Filters out nodes which should not be tested because they are not visible
221 * @returns Filter status of the given node
222 * @type {array of array of int}
224 function filterCroppedNodes(node) {
225 if (!node.boxObject) {
226 return domUtils.DOMWalker.FILTER_SKIP;
228 if (!node.disabled && !node.collapsed && !node.hidden) {
229 // Code specific to the preferences panes to reject out not visible nodes
231 if (node.parentNode && (node.parentNode.localName == "prefwindow" &&
232 node.parentNode.currentPane.id != node.id) ||
233 ((node.parentNode.localName == "tabpanels" ||
234 node.parentNode.localName == "deck") &&
235 node.parentNode.selectedPanel.id != node.id)) {
236 return domUtils.DOMWalker.FILTER_REJECT;
237 // end of the specific code
239 return domUtils.DOMWalker.FILTER_ACCEPT;
242 // we don't want to test not visible elements
243 return domUtils.DOMWalker.FILTER_REJECT;
249 * Callback function for testing access keys. To be used with the DOMWalker.
251 * It packs a submitted node and its access key into a double array
253 * @param {node} node Node containing the access key
254 * @returns lower-cased access key and its node in a nested array
255 * @type {array of array}
257 function prepareAccessKey(node) {
258 return [[node.accessKey.toLowerCase(), node]];
262 * Callback function for parsing the results of testing for cropped elements.
264 * This function calls the screenshot.create method if there is at least one
267 * @param {array of array of int} boxes
268 * @param {MozmillController} controller
270 function processDimensionsResults(controller, boxes) {
271 if (boxes && boxes.length > 0) {
272 screenshot.create(controller, boxes);
277 * Tries to return a useful string identificator of the given node
280 * @returns Identificator of the node
283 function _reportNode(node) {
285 return "id: " + node.id;
286 } else if (node.label) {
287 return "label: " + node.label;
288 } else if (node.value) {
289 return "value: " + node.value;
290 } else if (node.hasAttributes()) {
291 var attrs = "node attributes: ";
292 for (var i = node.attributes.length - 1; i >= 0; i--) {
293 attrs += node.attributes[i].name + "->" + node.attributes[i].value + ";";
297 return "anonymous node";
301 // Export of functions
302 exports.checkAccessKeysResults = checkAccessKeysResults;
303 exports.checkDimensions = checkDimensions;
304 exports.filterAccessKeys = filterAccessKeys;
305 exports.filterCroppedNodes = filterCroppedNodes;
306 exports.prepareAccessKey = prepareAccessKey;
307 exports.processDimensionsResults = processDimensionsResults;