Revision 2d080f1a7dd986dcd3f33a7676d30f367217d988 authored by Eric Willigers on 03 October 2017, 02:21:05 UTC, committed by Chromium WPT Sync on 03 October 2017, 02:21:05 UTC
When offset-position is not 'auto', the path begins at offset-position.

When offset-position is not 'auto', but offset-path is 'none', we have a
simple translation to offset-position. In this case, offset-anchor uses
the same value as offset-position, but with percentages referring to
the element's width/height and not the containing block's width/height.

Spec:
https://drafts.fxtf.org/motion-1/#offset-position-property
https://drafts.fxtf.org/motion-1/#offset-anchor-property

Work remaining:
For this initial implementation, we use the parent layout object
instead of the true containing block.

(When an element is absolutely positioned and the parent element is the
containing block, these are the same.)

BUG=638055

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I53e5b576adebf65ede8ebd1dabe084713ebdf875
1 parent 85a1716
Raw File
attribute-changed-callback.html
<!DOCTYPE html>
<html>
<head>
<title>Custom Elements: attributeChangedCallback</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content="attributeChangedCallback must be enqueued whenever custom element's attribute is added, changed or removed">
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/custom-elements-helpers.js"></script>
</head>
<body>
<div id="log"></div>
<script>

var customElement = define_new_custom_element(['title', 'id', 'r']);

test(function () {
    const instance = document.createElement(customElement.name);
    assert_array_equals(customElement.takeLog().types(), ['constructed']);

    instance.setAttribute('title', 'foo');
    assert_equals(instance.getAttribute('title'), 'foo');
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'foo', namespace: null});

    instance.removeAttribute('title');
    assert_equals(instance.getAttribute('title'), null);
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'foo', newValue: null, namespace: null});
}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');

test(function () {
    var instance = document.createElement(customElement.name);
    assert_array_equals(customElement.takeLog().types(), ['constructed']);

    instance.setAttributeNS('http://www.w3.org/2000/svg', 'title', 'hello');
    assert_equals(instance.getAttribute('title'), 'hello');
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'hello', namespace: 'http://www.w3.org/2000/svg'});

    instance.removeAttributeNS('http://www.w3.org/2000/svg', 'title');
    assert_equals(instance.getAttribute('title'), null);
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: 'http://www.w3.org/2000/svg'});
}, 'setAttributeNS and removeAttributeNS must enqueue and invoke attributeChangedCallback');

test(function () {
    var instance = document.createElement(customElement.name);
    assert_array_equals(customElement.takeLog().types(), ['constructed']);

    var attr = document.createAttribute('id');
    attr.value = 'bar';
    instance.setAttributeNode(attr);

    assert_equals(instance.getAttribute('id'), 'bar');
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'bar', namespace: null});

    instance.removeAttributeNode(attr);
    assert_equals(instance.getAttribute('id'), null);
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: 'bar', newValue: null, namespace: null});
}, 'setAttributeNode and removeAttributeNode must enqueue and invoke attributeChangedCallback for an HTML attribute');

test(function () {
    const instance = document.createElement(customElement.name);
    assert_array_equals(customElement.takeLog().types(), ['constructed']);

    const attr = document.createAttributeNS('http://www.w3.org/2000/svg', 'r');
    attr.value = '100';
    instance.setAttributeNode(attr);

    assert_equals(instance.getAttribute('r'), '100');
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: null, newValue: '100', namespace: 'http://www.w3.org/2000/svg'});

    instance.removeAttributeNode(attr);
    assert_equals(instance.getAttribute('r'), null);
    var logEntries = customElement.takeLog();
    assert_array_equals(logEntries.types(), ['attributeChanged']);
    assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: '100', newValue: null, namespace: 'http://www.w3.org/2000/svg'});
}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback for an SVG attribute');

test(function () {
    const callsToOld = [];
    const callsToNew = [];
    class CustomElement extends HTMLElement { }
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        callsToOld.push(create_attribute_changed_callback_log(this, ...args));
    }
    CustomElement.observedAttributes = ['title'];
    customElements.define('element-with-mutated-attribute-changed-callback', CustomElement);
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        callsToNew.push(create_attribute_changed_callback_log(this, ...args));
    }

    const instance = document.createElement('element-with-mutated-attribute-changed-callback');
    instance.setAttribute('title', 'hi');
    assert_equals(instance.getAttribute('title'), 'hi');
    assert_array_equals(callsToNew, []);
    assert_equals(callsToOld.length, 1);
    assert_attribute_log_entry(callsToOld[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
}, 'Mutating attributeChangedCallback after calling customElements.define must not affect the callback being invoked');

test(function () {
    const calls = [];
    class CustomElement extends HTMLElement {
        attributeChangedCallback(...args) {
            calls.push(create_attribute_changed_callback_log(this, ...args));
        }
    }
    CustomElement.observedAttributes = ['title'];
    customElements.define('element-not-observing-id-attribute', CustomElement);

    const instance = document.createElement('element-not-observing-id-attribute');
    assert_equals(calls.length, 0);
    instance.setAttribute('title', 'hi');
    assert_equals(calls.length, 1);
    assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
    instance.setAttribute('id', 'some');
    assert_equals(calls.length, 1);
    assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
}, 'attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute');

test(function () {
    const calls = [];
    class CustomElement extends HTMLElement { }
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        calls.push(create_attribute_changed_callback_log(this, ...args));
    }
    CustomElement.observedAttributes = ['title', 'lang'];
    customElements.define('element-with-mutated-observed-attributes', CustomElement);
    CustomElement.observedAttributes = ['title', 'id'];

    const instance = document.createElement('element-with-mutated-observed-attributes');
    instance.setAttribute('title', 'hi');
    assert_equals(calls.length, 1);
    assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});

    instance.setAttribute('id', 'some');
    assert_equals(calls.length, 1);

    instance.setAttribute('lang', 'en');
    assert_equals(calls.length, 2);
    assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: null, newValue: 'en', namespace: null});
}, 'Mutating observedAttributes after calling customElements.define must not affect the set of attributes for which attributedChangedCallback is invoked');

test(function () {
    var calls = [];
    class CustomElement extends HTMLElement { }
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        calls.push(create_attribute_changed_callback_log(this, ...args));
    }
    CustomElement.observedAttributes = { [Symbol.iterator]: function *() { yield 'lang'; yield 'style'; } };
    customElements.define('element-with-generator-observed-attributes', CustomElement);

    var instance = document.createElement('element-with-generator-observed-attributes');
    instance.setAttribute('lang', 'en');
    assert_equals(calls.length, 1);
    assert_attribute_log_entry(calls[0], {name: 'lang', oldValue: null, newValue: 'en', namespace: null});

    instance.setAttribute('lang', 'ja');
    assert_equals(calls.length, 2);
    assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: 'en', newValue: 'ja', namespace: null});

    instance.setAttribute('title', 'hello');
    assert_equals(calls.length, 2);

    instance.setAttribute('style', 'font-size: 2rem');
    assert_equals(calls.length, 3);
    assert_attribute_log_entry(calls[2], {name: 'style', oldValue: null, newValue: 'font-size: 2rem', namespace: null});
}, 'attributedChangedCallback must be enqueued for attributes specified in a non-Array iterable observedAttributes');

test(function () {
    var calls = [];
    class CustomElement extends HTMLElement { }
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        calls.push(create_attribute_changed_callback_log(this, ...args));
    }
    CustomElement.observedAttributes = ['style'];
    customElements.define('element-with-style-attribute-observation', CustomElement);

    var instance = document.createElement('element-with-style-attribute-observation');
    assert_equals(calls.length, 0);

    instance.style.fontSize = '10px';
    assert_equals(calls.length, 1);
    assert_attribute_log_entry(calls[0], {name: 'style', oldValue: null, newValue: 'font-size: 10px;', namespace: null});

    instance.style.fontSize = '20px';
    assert_equals(calls.length, 2);
    assert_attribute_log_entry(calls[1], {name: 'style', oldValue: 'font-size: 10px;', newValue: 'font-size: 20px;', namespace: null});

}, 'attributedChangedCallback must be enqueued for style attribute change by mutating inline style declaration');

test(function () {
    var calls = [];
    class CustomElement extends HTMLElement { }
    CustomElement.prototype.attributeChangedCallback = function (...args) {
        calls.push(create_attribute_changed_callback_log(this, ...args));
    }
    CustomElement.observedAttributes = ['title'];
    customElements.define('element-with-no-style-attribute-observation', CustomElement);

    var instance = document.createElement('element-with-no-style-attribute-observation');
    assert_equals(calls.length, 0);
    instance.style.fontSize = '10px';
    assert_equals(calls.length, 0);
    instance.title = 'hello';
    assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hello', namespace: null});
}, 'attributedChangedCallback must not be enqueued when mutating inline style declaration if the style attribute is not observed');

</script>
</body>
</html>
back to top