1 SimpleTest.waitForExplicitFinish();
3 var pointer_events_values = [
16 var paint_values = ["blue", "transparent", "none"];
18 var opacity_values = ["1", "0.5", "0"];
20 var visibility_values = ["visible", "hidden", "collapse"];
23 * List of attributes and various values for which we want to test permutations
24 * when hit testing a pointer event that is over an element's fill area,
25 * stroke area, or both (where they overlap).
27 * We're using an array of objects so that we have control over the order in
28 * which permutations are tested.
30 * TODO: test the effect of clipping, masking, filters, markers, etc.
32 var hit_test_inputs = {
34 { name: "pointer-events", values: pointer_events_values },
35 { name: "fill", values: paint_values },
36 { name: "fill-opacity", values: opacity_values },
37 { name: "opacity", values: opacity_values },
38 { name: "visibility", values: visibility_values },
41 { name: "pointer-events", values: pointer_events_values },
42 { name: "stroke", values: paint_values },
43 { name: "stroke-opacity", values: opacity_values },
44 { name: "opacity", values: opacity_values },
45 { name: "visibility", values: visibility_values },
48 { name: "pointer-events", values: pointer_events_values },
49 { name: "fill", values: paint_values },
50 { name: "fill-opacity", values: opacity_values },
51 { name: "stroke", values: paint_values },
52 { name: "stroke-opacity", values: opacity_values },
53 { name: "opacity", values: opacity_values },
54 { name: "visibility", values: visibility_values },
59 * The following object contains a list of 'pointer-events' property values,
60 * each with an object detailing the conditions under which the fill and stroke
61 * of a graphical object will intercept pointer events for the given value. If
62 * the object contains a 'fill-intercepts-iff' property then the fill is
63 * expected to intercept pointer events for that value of 'pointer-events' if
64 * and only if the conditions listed in the 'fill-intercepts-iff' object are
65 * met. If there are no conditions in the 'fill-intercepts-iff' object then the
66 * fill should always intercept pointer events. However, if the
67 * 'fill-intercepts-iff' property is not set at all then it indicates that the
68 * fill should never intercept pointer events. The same rules apply for
69 * 'stroke-intercepts-iff'.
71 * If an attribute name in the conditions list is followed by the "!"
72 * character then the requirement for a hit is that its value is NOT any
73 * of the values listed in the given array.
75 var hit_conditions = {
77 "fill-intercepts-iff": {
78 visibility: ["visible"],
81 "stroke-intercepts-iff": {
82 visibility: ["visible"],
87 "fill-intercepts-iff": {
88 visibility: ["visible"],
91 "stroke-intercepts-iff": {
92 visibility: ["visible"],
97 "fill-intercepts-iff": {
98 visibility: ["visible"],
100 // stroke never intercepts pointer events
103 // fill never intercepts pointer events
104 "stroke-intercepts-iff": {
105 visibility: ["visible"],
109 "fill-intercepts-iff": {
110 visibility: ["visible"],
112 "stroke-intercepts-iff": {
113 visibility: ["visible"],
117 "fill-intercepts-iff": {
120 "stroke-intercepts-iff": {
125 "fill-intercepts-iff": {
126 // fill always intercepts pointer events
128 // stroke never intercepts pointer events
131 // fill never intercepts pointer events
132 "stroke-intercepts-iff": {
133 // stroke always intercepts pointer events
137 "fill-intercepts-iff": {
138 // fill always intercepts pointer events
140 "stroke-intercepts-iff": {
141 // stroke always intercepts pointer events
145 // neither fill nor stroke intercept pointer events
150 var POINT_OVER_FILL = 0x1;
151 var POINT_OVER_STROKE = 0x2;
154 * Examine the element's attribute values and, based on the area(s) of the
155 * element that the pointer event is over (fill and/or stroke areas), return
156 * true if the element is expected to intercept the event, otherwise false.
158 function hit_expected(
160 over /* bit flags indicating which area(s) of the element the pointer is over */
162 function expect_hit(target) {
164 hit_conditions[element.getAttribute("pointer-events")][
165 target + "-intercepts-iff"
168 if (!intercepts_iff) {
169 return false; // never intercepts events
172 for (var attr in intercepts_iff) {
173 var vals = intercepts_iff[attr]; // must get this before we adjust 'attr'
175 if (attr.substr(-1) == "!") {
177 attr = attr.substr(0, attr.length - 1);
179 var match = vals.indexOf(element.getAttribute(attr)) > -1;
192 ((over & POINT_OVER_FILL) != 0 && expect_hit("fill")) ||
193 ((over & POINT_OVER_STROKE) != 0 && expect_hit("stroke"))
197 function for_all_permutations(inputs, callback) {
198 var current_permutation = arguments[2] || {};
199 var index = arguments[3] || 0;
201 if (index < inputs.length) {
202 var name = inputs[index].name;
203 var values = inputs[index].values;
204 for (var i = 0; i < values.length; ++i) {
205 current_permutation[name] = values[i];
206 for_all_permutations(inputs, callback, current_permutation, index + 1);
211 callback(current_permutation);
214 function make_log_msg(over, tag, attributes) {
216 if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) {
217 target = "fill and stroke";
218 } else if (over == POINT_OVER_FILL) {
220 } else if (over == POINT_OVER_STROKE) {
223 throw new Error("unexpected bit combination in 'over'");
226 "Check if events are intercepted at a point over the " +
231 for (var attr in attributes) {
232 msg += " " + attr + "=" + attributes[attr];
237 var dx, dy; // offset of <svg> element from pointer coordinates origin
239 function test_element(
243 over /* bit flags indicating which area(s) of the element the pointer is over */
245 var element = document.getElementById(id);
246 var tag = element.tagName;
248 function test_permutation(attributes) {
249 for (var attr in attributes) {
250 element.setAttribute(attr, attributes[attr]);
252 var hits = document.elementFromPoint(dx + x, dy + y) == element;
253 var msg = make_log_msg(over, tag, attributes);
255 is(hits, hit_expected(element, over), msg);
259 if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) {
260 inputs = hit_test_inputs.both;
261 } else if (over == POINT_OVER_FILL) {
262 inputs = hit_test_inputs.fill;
263 } else if (over == POINT_OVER_STROKE) {
264 inputs = hit_test_inputs.stroke;
266 throw new Error("unexpected bit combination in 'over'");
269 for_all_permutations(inputs, test_permutation);
271 // To reduce the chance of bogus results in subsequent tests:
272 element.setAttribute("fill", "none");
273 element.setAttribute("stroke", "none");
276 function run_tests(subtest) {
277 var div = document.getElementById("div");
281 // Run the test with only a subset of pointer-events values, to avoid
282 // running over the mochitest time limit. The subtest argument indicates
283 // whether to use the first half of the pointer-events values (0)
284 // or the second half (1).
285 var partition = Math.floor(pointer_events_values.length / 2);
288 pointer_events_values.splice(partition);
291 pointer_events_values.splice(0, partition);
294 throw new Error("unexpected subtest number");
297 test_element("rect", 30, 30, POINT_OVER_FILL);
298 test_element("rect", 5, 5, POINT_OVER_STROKE);
300 // The SVG 1.1 spec essentially says that, for text, hit testing is done
301 // against the character cells of the text, and not the fill and stroke as
302 // you might expect for a normal graphics element like <path>. See the
303 // paragraph starting "For text elements..." in this section:
305 // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
307 // This requirement essentially means that for the purposes of hit testing
308 // the fill and stroke areas are the same area - the character cell. (At
309 // least until we support having any fill or stroke that lies outside the
310 // character cells intercept events like Opera does - see below.) Thus, for
311 // text, when a pointer event is over a character cell it is essentially over
312 // both the fill and stroke at the same time. That's the reason we pass both
313 // the POINT_OVER_FILL and POINT_OVER_STROKE bits in test_element's 'over'
314 // argument below. It's also the reason why we only test one point in the
315 // text rather than having separate tests for fill and stroke.
317 // For hit testing of text, Opera essentially treats fill and stroke like it
318 // would on any normal element, but it adds the character cells of glyhs to
319 // both the glyphs' fill AND stroke. I think this is what we should do too.
320 // It's compatible with the letter of the SVG 1.1 rules, and it allows any
321 // parts of a glyph that are outside the glyph's character cells to also
322 // intercept events in the normal way. When we make that change we'll be able
323 // to add separate fill and stroke tests for text below.
325 test_element("text", 210, 30, POINT_OVER_FILL | POINT_OVER_STROKE);