| Index: third_party/WebKit/ManualTests/compositor-worker/sticky/js/sticky.js
|
| diff --git a/third_party/WebKit/ManualTests/compositor-worker/sticky/js/sticky.js b/third_party/WebKit/ManualTests/compositor-worker/sticky/js/sticky.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..375df49399264292541160b809c96484c0bb3ab5
|
| --- /dev/null
|
| +++ b/third_party/WebKit/ManualTests/compositor-worker/sticky/js/sticky.js
|
| @@ -0,0 +1,167 @@
|
| +(function(scope) {
|
| + "use strict";
|
| + function getStickyElements(nodelist, node) {
|
| + nodelist = nodelist || [];
|
| + node = node || document.body;
|
| + if (node.classList.contains('sticky'))
|
| + nodelist.push(node);
|
| + // Q: How do we support this position property?
|
| + // if (getComputedStyle(node).position == 'sticky')
|
| + // nodelist.append(node);
|
| +
|
| + // Q: How do we just do work on sticky elements rather than entire DOM?
|
| + for (var i = 0; i < node.children.length; ++i) {
|
| + getStickyElements(nodelist, node.children[i]);
|
| + }
|
| + return nodelist;
|
| + }
|
| +
|
| + function installStickyHandler(scroller, element, sticky_info) {
|
| + var main_info = {
|
| + 'sticky': new WrapElement(element),
|
| + 'scroller': new WrapElement(scroller),
|
| + 'limits': sticky_info.limits,
|
| + };
|
| +
|
| + // Install a viewport observer to update position on the main thread.
|
| + var observer = new ViewportObserver(updateStickyPosition.bind(null, main_info));
|
| + observer.observe(scroller);
|
| + // TODO: Install compositor worker to update on compositor thread.
|
| + }
|
| +
|
| + function updateStickyPosition(info) {
|
| + // Find sticky position
|
| + // Note: top and left take priority over bottom and right as per spec:
|
| + var offsetX, offsetY;
|
| + if (info.limits.y.attachment == 'top')
|
| + offsetY = Math.min(Math.max(info.limits.y.min, info.scroller.scrollTop() - info.limits.y.top), info.limits.y.max);
|
| + if (info.limits.y.attachment == 'bottom')
|
| + offsetY = Math.max(Math.min(info.limits.y.max, info.scroller.scrollTop() + info.limits.y.bottom), info.limits.y.min);
|
| + if (info.limits.x.attachment == 'left')
|
| + offsetX = Math.min(Math.max(info.limits.x.min, info.scroller.scrollLeft() - info.limits.x.left), info.limits.x.max);
|
| + if (info.limits.x.attachment == 'right')
|
| + offsetX = Math.max(Math.min(info.limits.x.max, info.scroller.scrollLeft() + info.limits.x.right), info.limits.x.min);
|
| +
|
| + // Q: How should we really move the element around? This is overloading the top / left properties which has a specific meaning for sticky.
|
| + // A: Probably hidden position attributes (e.g. used left, used top), but how do we want to expose these?
|
| + info.sticky.setTransform(offsetX || 0, offsetY || 0, info.limits.id);
|
| + }
|
| +
|
| + function gatherStickyInfo() {
|
| + var nodes = getStickyElements();
|
| + scope.sticky_info = [];
|
| + for (var i = 0; i < nodes.length; i++) {
|
| + var node = nodes[i];
|
| + var cb = getContainingBlockElement(node);
|
| + var s = getContainingScrollingElement(node);
|
| +
|
| + // Q: Need to be notified when sticky node is removed / moved - Mutation Observer?
|
| + var info = {
|
| + sticky: scope.CompositorProxy ? new CompositorProxy(node, ['transform']) : undefined,
|
| + scroller: scope.CompositorProxy ? new CompositorProxy(s, ['scrollTop', 'scrollLeft']) : undefined,
|
| + limits: scope.getStickyOffsetLimits(node, cb, s, getStickyOffsets(node)),
|
| + }
|
| + scope.sticky_info.push(info);
|
| + installStickyHandler(s, node, info);
|
| + }
|
| + if (scope.CompositorWorker) {
|
| + scope.worker = new CompositorWorker('js/sticky.js');
|
| + scope.worker.postMessage(scope.sticky_info);
|
| + }
|
| + }
|
| +
|
| + scope.getStickyOffsetLimits = function(sticky, containing, scroller, clamp) {
|
| + var c = containing.getBoundingClientRect();
|
| + var s = sticky.getBoundingClientRect();
|
| + var v = getViewportRect(scroller);
|
| +
|
| + var limits = {
|
| + id: sticky.id,
|
| + x: {
|
| + left: s.left + scroller.scrollLeft - (clamp.left || 0),
|
| + right: v.right - s.right - scroller.scrollLeft - (clamp.right || 0),
|
| + min: c.left - s.left + (clamp.left || 0),
|
| + max: c.right - s.right - (clamp.right || 0),
|
| + },
|
| + y: {
|
| + top: s.top + scroller.scrollTop - (clamp.top || 0),
|
| + bottom: v.bottom - s.bottom - scroller.scrollTop - (clamp.bottom || 0),
|
| + min: c.top - s.top + (clamp.top || 0),
|
| + max: c.bottom - s.bottom - (clamp.bottom || 0),
|
| + }};
|
| + if (clamp.bottom)
|
| + limits.y.attachment = 'bottom';
|
| + if (clamp.top)
|
| + limits.y.attachment = 'top';
|
| + if (clamp.right)
|
| + limits.x.attachment = 'right';
|
| + if (clamp.left)
|
| + limits.x.attachment = 'left';
|
| + return limits;
|
| + }
|
| +
|
| + function getStickyOffsets(node) {
|
| + var style = getComputedStyle(node);
|
| + // Note: this assumes top/left/right/bottom will be set in 'px'.
|
| + // TODO: Support percentage?
|
| + var info = {
|
| + 'top': style.top == 'auto' ? undefined : parseFloat(style.top),
|
| + 'left': style.left == 'auto' ? undefined : parseFloat(style.left),
|
| + 'right': style.right == 'auto' ? undefined : parseFloat(style.right),
|
| + 'bottom': style.bottom == 'auto' ? undefined : parseFloat(style.bottom),
|
| + };
|
| + node.style.top = node.style.left = node.style.right = node.style.bottom = 'auto';
|
| + return info;
|
| + }
|
| +
|
| + scope.compositorUpdate = function() {
|
| + for (var i = 0; i < scope.sticky_info.length; i++) {
|
| + updateStickyPosition(scope.sticky_info[i]);
|
| + }
|
| + scope.requestAnimationFrame(scope.compositorUpdate);
|
| + }
|
| +
|
| + scope.WrapElement = function(element) {
|
| + this.element_ = element;
|
| + }
|
| + scope.WrapElement.prototype.scrollTop = function() { return this.element_.scrollTop; };
|
| + scope.WrapElement.prototype.scrollLeft = function() { return this.element_.scrollLeft; };
|
| + scope.WrapElement.prototype.setTransform = function(x, y, id, extra) {
|
| + this.element_.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
|
| + };
|
| +
|
| + scope.WrapCompositorProxy = function(proxy) {
|
| + this.proxy_ = proxy;
|
| + }
|
| + scope.WrapCompositorProxy.prototype.scrollTop = function() { return this.proxy_.scrollTop; };
|
| + scope.WrapCompositorProxy.prototype.scrollLeft = function() { return this.proxy_.scrollLeft; };
|
| + scope.WrapCompositorProxy.prototype.setTransform = function(x, y, id, extra) {
|
| + var t = this.proxy_.transform;
|
| + t.m41 = x;
|
| + t.m42 = y;
|
| + this.proxy_.transform = t;
|
| + };
|
| +
|
| +
|
| + function initCompositorWorker() {
|
| + console.log('Init on compoositor worker');
|
| + self.onmessage = function(e) {
|
| + scope.sticky_info = e.data;
|
| + // Wrap all of the compositor proxies to match expectations.
|
| + for (var i = 0; i < sticky_info.length; i++) {
|
| + sticky_info[i].sticky = new scope.WrapCompositorProxy(sticky_info[i].sticky);
|
| + sticky_info[i].scroller = new scope.WrapCompositorProxy(sticky_info[i].scroller);
|
| + }
|
| + scope.requestAnimationFrame(scope.compositorUpdate);
|
| + console.log('onmessage on compositor');
|
| + };
|
| + }
|
| +
|
| +
|
| + if (scope.window) {
|
| + scope.document.addEventListener('DOMContentLoaded', gatherStickyInfo);
|
| + } else {
|
| + scope.sticky_info = [];
|
| + initCompositorWorker();
|
| + }
|
| +})(self);
|
|
|