From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/svg/test/pointer-events.js | 328 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 dom/svg/test/pointer-events.js (limited to 'dom/svg/test/pointer-events.js') diff --git a/dom/svg/test/pointer-events.js b/dom/svg/test/pointer-events.js new file mode 100644 index 000000000..cf5d6cb70 --- /dev/null +++ b/dom/svg/test/pointer-events.js @@ -0,0 +1,328 @@ +SimpleTest.waitForExplicitFinish(); + +var pointer_events_values = [ + 'auto', + 'visiblePainted', + 'visibleFill', + 'visibleStroke', + 'visible', + 'painted', + 'fill', + 'stroke', + 'all', + 'none' +]; + +var paint_values = [ + 'blue', + 'transparent', + 'none' +]; + +var opacity_values = [ + '1', + '0.5', + '0' +]; + +var visibility_values = [ + 'visible', + 'hidden', + 'collapse' +]; + +/** + * List of attributes and various values for which we want to test permutations + * when hit testing a pointer event that is over an element's fill area, + * stroke area, or both (where they overlap). + * + * We're using an array of objects so that we have control over the order in + * which permutations are tested. + * + * TODO: test the effect of clipping, masking, filters, markers, etc. + */ +var hit_test_inputs = { + fill: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'fill', values: paint_values }, + { name: 'fill-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ], + stroke: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'stroke', values: paint_values }, + { name: 'stroke-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ], + both: [ + { name: 'pointer-events', values: pointer_events_values }, + { name: 'fill', values: paint_values }, + { name: 'fill-opacity', values: opacity_values }, + { name: 'stroke', values: paint_values }, + { name: 'stroke-opacity', values: opacity_values }, + { name: 'opacity', values: opacity_values }, + { name: 'visibility', values: visibility_values } + ] +} + +/** + * The following object contains a list of 'pointer-events' property values, + * each with an object detailing the conditions under which the fill and stroke + * of a graphical object will intercept pointer events for the given value. If + * the object contains a 'fill-intercepts-iff' property then the fill is + * expected to intercept pointer events for that value of 'pointer-events' if + * and only if the conditions listed in the 'fill-intercepts-iff' object are + * met. If there are no conditions in the 'fill-intercepts-iff' object then the + * fill should always intercept pointer events. However, if the + * 'fill-intercepts-iff' property is not set at all then it indicates that the + * fill should never intercept pointer events. The same rules apply for + * 'stroke-intercepts-iff'. + * + * If an attribute name in the conditions list is followed by the "!" + * character then the requirement for a hit is that its value is NOT any + * of the values listed in the given array. + */ +var hit_conditions = { + auto: { + 'fill-intercepts-iff': { + 'visibility': ['visible'], + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'visibility': ['visible'], + 'stroke!': ['none'] + } + }, + visiblePainted: { + 'fill-intercepts-iff': { + 'visibility': ['visible'], + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'visibility': ['visible'], + 'stroke!': ['none'] + } + }, + visibleFill: { + 'fill-intercepts-iff': { + visibility: ['visible'] + } + // stroke never intercepts pointer events + }, + visibleStroke: { + // fill never intercepts pointer events + 'stroke-intercepts-iff': { + visibility: ['visible'] + } + }, + visible: { + 'fill-intercepts-iff': { + visibility: ['visible'] + }, + 'stroke-intercepts-iff': { + visibility: ['visible'] + } + }, + painted: { + 'fill-intercepts-iff': { + 'fill!': ['none'] + }, + 'stroke-intercepts-iff': { + 'stroke!': ['none'] + } + }, + fill: { + 'fill-intercepts-iff': { + // fill always intercepts pointer events + } + // stroke never intercepts pointer events + }, + stroke: { + // fill never intercepts pointer events + 'stroke-intercepts-iff': { + // stroke always intercepts pointer events + } + }, + all: { + 'fill-intercepts-iff': { + // fill always intercepts pointer events + }, + 'stroke-intercepts-iff': { + // stroke always intercepts pointer events + } + }, + none: { + // neither fill nor stroke intercept pointer events + } +} + +// bit flags +var POINT_OVER_FILL = 0x1; +var POINT_OVER_STROKE = 0x2; + +/** + * Examine the element's attribute values and, based on the area(s) of the + * element that the pointer event is over (fill and/or stroke areas), return + * true if the element is expected to intercept the event, otherwise false. + */ +function hit_expected(element, over /* bit flags indicating which area(s) of the element the pointer is over */) +{ + function expect_hit(target){ + var intercepts_iff = + hit_conditions[element.getAttribute('pointer-events')][target + '-intercepts-iff']; + + if (!intercepts_iff) { + return false; // never intercepts events + } + + for (var attr in intercepts_iff) { + var vals = intercepts_iff[attr]; // must get this before we adjust 'attr' + var invert = false; + if (attr.substr(-1) == '!') { + invert = true; + attr = attr.substr(0, attr.length-1); + } + var match = vals.indexOf(element.getAttribute(attr)) > -1; + if (invert) { + match = !match; + } + if (!match) { + return false; + } + } + + return true; + } + + return (over & POINT_OVER_FILL) != 0 && expect_hit('fill') || + (over & POINT_OVER_STROKE) != 0 && expect_hit('stroke'); +} + +function for_all_permutations(inputs, callback) +{ + var current_permutation = arguments[2] || {}; + var index = arguments[3] || 0; + + if (index < inputs.length) { + var name = inputs[index].name; + var values = inputs[index].values; + for (var i = 0; i < values.length; ++i) { + current_permutation[name] = values[i]; + for_all_permutations(inputs, callback, current_permutation, index+1); + } + return; + } + + callback(current_permutation); +} + +function make_log_msg(over, tag, attributes) +{ + var target; + if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) { + target = 'fill and stroke'; + } else if (over == POINT_OVER_FILL) { + target = 'fill'; + } else if (over == POINT_OVER_STROKE) { + target = 'stroke'; + } else { + throw "unexpected bit combination in 'over'"; + } + var msg = 'Check if events are intercepted at a point over the '+target+' on <'+tag+'> for'; + for (var attr in attributes) { + msg += ' '+attr+'='+attributes[attr]; + } + return msg; +} + +var dx, dy; // offset of element from pointer coordinates origin + +function test_element(id, x, y, over /* bit flags indicating which area(s) of the element the pointer is over */) +{ + var element = document.getElementById(id); + var tag = element.tagName; + + function test_permutation(attributes) { + for (var attr in attributes) { + element.setAttribute(attr, attributes[attr]); + } + var hits = document.elementFromPoint(dx + x, dy + y) == element; + var msg = make_log_msg(over, tag, attributes); + + is(hits, hit_expected(element, over), msg); + } + + var inputs; + if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) { + inputs = hit_test_inputs['both']; + } else if (over == POINT_OVER_FILL) { + inputs = hit_test_inputs['fill']; + } else if (over == POINT_OVER_STROKE) { + inputs = hit_test_inputs['stroke']; + } else { + throw "unexpected bit combination in 'over'"; + } + + for_all_permutations(inputs, test_permutation); + + // To reduce the chance of bogus results in subsequent tests: + element.setAttribute('fill', 'none'); + element.setAttribute('stroke', 'none'); +} + +function run_tests(subtest) +{ + var div = document.getElementById("div"); + dx = div.offsetLeft; + dy = div.offsetTop; + + // Run the test with only a subset of pointer-events values, to avoid + // running over the mochitest time limit. The subtest argument indicates + // whether to use the first half of the pointer-events values (0) + // or the second half (1). + var partition = Math.floor(pointer_events_values.length / 2); + switch (subtest) { + case 0: + pointer_events_values.splice(partition); + break; + case 1: + pointer_events_values.splice(0, partition); + break; + case 2: + throw "unexpected subtest number"; + } + + test_element('rect', 30, 30, POINT_OVER_FILL); + test_element('rect', 5, 5, POINT_OVER_STROKE); + + // The SVG 1.1 spec essentially says that, for text, hit testing is done + // against the character cells of the text, and not the fill and stroke as + // you might expect for a normal graphics element like . See the + // paragraph starting "For text elements..." in this section: + // + // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty + // + // This requirement essentially means that for the purposes of hit testing + // the fill and stroke areas are the same area - the character cell. (At + // least until we support having any fill or stroke that lies outside the + // character cells intercept events like Opera does - see below.) Thus, for + // text, when a pointer event is over a character cell it is essentially over + // both the fill and stroke at the same time. That's the reason we pass both + // the POINT_OVER_FILL and POINT_OVER_STROKE bits in test_element's 'over' + // argument below. It's also the reason why we only test one point in the + // text rather than having separate tests for fill and stroke. + // + // For hit testing of text, Opera essentially treats fill and stroke like it + // would on any normal element, but it adds the character cells of glyhs to + // both the glyphs' fill AND stroke. I think this is what we should do too. + // It's compatible with the letter of the SVG 1.1 rules, and it allows any + // parts of a glyph that are outside the glyph's character cells to also + // intercept events in the normal way. When we make that change we'll be able + // to add separate fill and stroke tests for text below. + + test_element('text', 210, 30, POINT_OVER_FILL | POINT_OVER_STROKE); + + SimpleTest.finish(); +} -- cgit v1.2.3