/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ window.onload = function () { checkDefaultValues() printTest( new RustEmitter(), document.body.children[0], document.body.children[1], document.body.children[2]) } function assert (condition, message) { if (!condition) { throw new Error(message) } } function printTest (e, LTRContainer, RTLContainer, genericContainer) { e.push([ '/**', ' * Copyright (c) 2014-present, Facebook, Inc.', ' *', ' * This source code is licensed under the MIT license found in the', ' * LICENSE file in the root directory of this source tree.', ' */', '', '// @Generated by gentest/gentest.rb from gentest/fixtures/' + document.title + '.html', '' ]) e.emitPrologue() var LTRLayoutTree = calculateTree(LTRContainer) var RTLLayoutTree = calculateTree(RTLContainer) var genericLayoutTree = calculateTree(genericContainer) for (var i = 0; i < genericLayoutTree.length; i++) { e.emitTestPrologue(genericLayoutTree[i].name, genericLayoutTree[i].experiments) if (genericLayoutTree[i].name == 'wrap_column') { // Modify width and left values due to both safari and chrome not abiding by the // specification. The undefined dimension of a parent should be defined by the total size // of their children in that dimension. // See diagram under flex-wrap header https://www.w3.org/TR/css-flexbox-1/ assert(LTRLayoutTree[0].width == 30, 'wrap_column LTR root.width should be 30') LTRLayoutTree[0].width = 60 assert(RTLLayoutTree[0].width == 30, 'wrap_column RTL root.width should be 30') RTLLayoutTree[0].width = 60 var children = RTLLayoutTree[0].children assert(children[0].left == 0, 'wrap_column RTL root_child0.left should be 0') children[0].left = 30 assert(children[1].left == 0, 'wrap_column RTL root_child0.left should be 0') children[1].left = 30 assert(children[2].left == 0, 'wrap_column RTL root_child2.left should be 0') children[2].left = 30 assert(children[3].left == -30, 'wrap_column RTL root_child3.left should be -30') children[3].left = 0 } setupTestTree( e, undefined, LTRLayoutTree[i], genericLayoutTree[i], 'root', null) e.YGNodeCalculateLayout('root', e.YGDirectionLTR, genericLayoutTree[i].experiments) e.push('') assertTestTree(e, LTRLayoutTree[i], 'root', null) e.push('') e.YGNodeCalculateLayout('root', e.YGDirectionRTL, genericLayoutTree[i].experiments) e.push('') assertTestTree(e, RTLLayoutTree[i], 'root', null) e.emitTestEpilogue(genericLayoutTree[i].experiments) } e.emitEpilogue() e.print() } function assertTestTree (e, node, nodeName, parentName) { e.AssertEQ(node.left, e.YGNodeLayoutGetLeft(nodeName)) e.AssertEQ(node.top, e.YGNodeLayoutGetTop(nodeName)) e.AssertEQ(node.width, e.YGNodeLayoutGetWidth(nodeName)) e.AssertEQ(node.height, e.YGNodeLayoutGetHeight(nodeName)) for (var i = 0; i < node.children.length; i++) { e.push('') var childName = nodeName + '_child' + i assertTestTree(e, node.children[i], childName, nodeName) } } function checkDefaultValues () { // Sanity check of the Yoga default values by test-template.html [ { style: 'flex-direction', value: 'column' }, { style: 'justify-content', value: 'flex-start' }, { style: 'align-content', value: 'flex-start' }, { style: 'align-items', value: 'stretch' }, { style: 'position', value: 'relative' }, { style: 'flex-wrap', value: 'nowrap' }, { style: 'overflow', value: 'visible' }, { style: 'flex-grow', value: '0' }, { style: 'flex-shrink', value: '0' }, { style: 'left', value: 'undefined' }, { style: 'top', value: 'undefined' }, { style: 'right', value: 'undefined' }, { style: 'bottom', value: 'undefined' }, { style: 'display', value: 'flex' } ].forEach(function (item) { assert(item.value === getDefaultStyleValue(item.style), item.style + ' should be ' + item.value) }) } function setupTestTree (e, parent, node, genericNode, nodeName, parentName, index) { e.emitTestTreePrologue(nodeName) for (var style in node.style) { // Skip position info for root as it messes up tests if (node.declaredStyle[style] === '' && (style == 'position' || style == 'left' || style == 'top' || style == 'right' || style == 'bottom' || style == 'width' || style == 'height')) { continue } if (node.style[style] !== getDefaultStyleValue(style)) { switch (style) { case 'direction': e.YGNodeStyleSetDirection(nodeName, directionValue(e, node.style[style])) break case 'flex-direction': e.YGNodeStyleSetFlexDirection(nodeName, flexDirectionValue(e, node.style[style])) break case 'justify-content': e.YGNodeStyleSetJustifyContent(nodeName, justifyValue(e, node.style[style])) break case 'align-content': e.YGNodeStyleSetAlignContent(nodeName, alignValue(e, node.style[style])) break case 'align-items': e.YGNodeStyleSetAlignItems(nodeName, alignValue(e, node.style[style])) break case 'align-self': if (!parent || node.style[style] !== parent.style['align-items']) { e.YGNodeStyleSetAlignSelf(nodeName, alignValue(e, node.style[style])) } break case 'position': e.YGNodeStyleSetPositionType(nodeName, positionValue(e, node.style[style])) break case 'flex-wrap': e.YGNodeStyleSetFlexWrap(nodeName, wrapValue(e, node.style[style])) break case 'overflow': e.YGNodeStyleSetOverflow(nodeName, overflowValue(e, node.style[style])) break case 'flex-grow': e.YGNodeStyleSetFlexGrow(nodeName, node.style[style]) break case 'flex-shrink': e.YGNodeStyleSetFlexShrink(nodeName, node.style[style]) break case 'flex-basis': e.YGNodeStyleSetFlexBasis(nodeName, pointValue(e, node.style[style])) break case 'left': if (genericNode.rawStyle.indexOf('start:') >= 0) { e.YGNodeStyleSetPosition(nodeName, e.YGEdgeStart, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetPosition(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style])) } break case 'top': e.YGNodeStyleSetPosition(nodeName, e.YGEdgeTop, pointValue(e, node.style[style])) break case 'right': if (genericNode.rawStyle.indexOf('end:') >= 0) { e.YGNodeStyleSetPosition(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetPosition(nodeName, e.YGEdgeRight, pointValue(e, node.style[style])) } break case 'bottom': e.YGNodeStyleSetPosition(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style])) break case 'margin-left': if (genericNode.rawStyle.indexOf('margin-start:') >= 0) { e.YGNodeStyleSetMargin(nodeName, e.YGEdgeStart, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetMargin(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style])) } break case 'margin-top': e.YGNodeStyleSetMargin(nodeName, e.YGEdgeTop, pointValue(e, node.style[style])) break case 'margin-right': if (genericNode.rawStyle.indexOf('margin-end:') >= 0) { e.YGNodeStyleSetMargin(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetMargin(nodeName, e.YGEdgeRight, pointValue(e, node.style[style])) } break case 'margin-bottom': e.YGNodeStyleSetMargin(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style])) break case 'padding-left': if (genericNode.rawStyle.indexOf('padding-start:') >= 0) { e.YGNodeStyleSetPadding(nodeName, e.YGEdgeStart, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetPadding(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style])) } break case 'padding-top': e.YGNodeStyleSetPadding(nodeName, e.YGEdgeTop, pointValue(e, node.style[style])) break case 'padding-right': if (genericNode.rawStyle.indexOf('padding-end:') >= 0) { e.YGNodeStyleSetPadding(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetPadding(nodeName, e.YGEdgeRight, pointValue(e, node.style[style])) } break case 'padding-bottom': e.YGNodeStyleSetPadding(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style])) break case 'border-left-width': if (genericNode.rawStyle.indexOf('border-start-width:') >= 0) { e.YGNodeStyleSetBorder(nodeName, e.YGEdgeStart, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetBorder(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style])) } break case 'border-top-width': e.YGNodeStyleSetBorder(nodeName, e.YGEdgeTop, pointValue(e, node.style[style])) break case 'border-right-width': if (genericNode.rawStyle.indexOf('border-end-width:') >= 0) { e.YGNodeStyleSetBorder(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style])) } else { e.YGNodeStyleSetBorder(nodeName, e.YGEdgeRight, pointValue(e, node.style[style])) } break case 'border-bottom-width': e.YGNodeStyleSetBorder(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style])) break case 'width': e.YGNodeStyleSetWidth(nodeName, pointValue(e, node.style[style])) break case 'min-width': e.YGNodeStyleSetMinWidth(nodeName, pointValue(e, node.style[style])) break case 'max-width': e.YGNodeStyleSetMaxWidth(nodeName, pointValue(e, node.style[style])) break case 'height': e.YGNodeStyleSetHeight(nodeName, pointValue(e, node.style[style])) break case 'min-height': e.YGNodeStyleSetMinHeight(nodeName, pointValue(e, node.style[style])) break case 'max-height': e.YGNodeStyleSetMaxHeight(nodeName, pointValue(e, node.style[style])) break case 'display': e.YGNodeStyleSetDisplay(nodeName, displayValue(e, node.style[style])) break } } } if (parentName) { e.YGNodeInsertChild(parentName, nodeName, index) } for (var i = 0; i < node.children.length; i++) { e.push('') var childName = nodeName + '_child' + i setupTestTree( e, node, node.children[i], genericNode.children[i], childName, nodeName, i) } } function overflowValue (e, value) { switch (value) { case 'visible': return e.YGOverflowVisible case 'hidden': return e.YGOverflowHidden } } function wrapValue (e, value) { switch (value) { case 'wrap': return e.YGWrapWrap case 'wrap-reverse': return e.YGWrapWrapReverse case 'nowrap': return e.YGWrapNoWrap } } function flexDirectionValue (e, value) { switch (value) { case 'row': return e.YGFlexDirectionRow case 'row-reverse': return e.YGFlexDirectionRowReverse case 'column': return e.YGFlexDirectionColumn case 'column-reverse': return e.YGFlexDirectionColumnReverse } } function justifyValue (e, value) { switch (value) { case 'center': return e.YGJustifyCenter case 'space-around': return e.YGJustifySpaceAround case 'space-between': return e.YGJustifySpaceBetween case 'space-evenly': return e.YGJustifySpaceEvenly case 'flex-start': return e.YGJustifyFlexStart case 'flex-end': return e.YGJustifyFlexEnd } } function positionValue (e, value) { switch (value) { case 'absolute': return e.YGPositionTypeAbsolute default: return e.YGPositionTypeRelative } } function directionValue (e, value) { switch (value) { case 'ltr': return e.YGDirectionLTR case 'rtl': return e.YGDirectionRTL case 'inherit': return e.YGDirectionInherit } } function alignValue (e, value) { switch (value) { case 'auto': return e.YGAlignAuto case 'center': return e.YGAlignCenter case 'stretch': return e.YGAlignStretch case 'flex-start': return e.YGAlignFlexStart case 'flex-end': return e.YGAlignFlexEnd case 'space-between': return e.YGAlignSpaceBetween case 'space-around': return e.YGAlignSpaceAround case 'baseline': return e.YGAlignBaseline } } function pointValue (e, value) { switch (value) { case 'auto': return e.YGAuto case 'undefined': return e.YGUndefined default: return value } } function displayValue (e, value) { switch (value) { case 'flex': return e.YGDisplayFlex case 'none': return e.YGDisplayNone } } function getDefaultStyleValue (style) { if (style == 'position') { return 'relative' } switch (style) { case 'left': case 'top': case 'right': case 'bottom': case 'start': case 'end': return 'undefined' } var node = document.getElementById('default') return getComputedStyle(node, null).getPropertyValue(style) } function getRoundedSize (node) { var boundingRect = node.getBoundingClientRect() return { width: Math.round(boundingRect.right) - Math.round(boundingRect.left), height: Math.round(boundingRect.bottom) - Math.round(boundingRect.top) } } function calculateTree (root, roundToPixelGrid) { var rootLayout = [] var names = [] for (var i = 0; i < root.children.length; i++) { var child = root.children[i] var layout = { name: child.id !== '' ? child.id : 'INSERT_NAME_HERE', left: child.offsetLeft + child.parentNode.clientLeft, top: child.offsetTop + child.parentNode.clientTop, width: child.offsetWidth, height: child.offsetHeight, children: calculateTree(child, roundToPixelGrid), style: getYogaStyle(child), declaredStyle: child.style, rawStyle: child.getAttribute('style'), experiments: child.getAttribute('experiments') ? child.getAttribute('experiments').split(' ') : [] } var originName = layout.name var size = getRoundedSize(child) layout.width = size.width layout.height = size.height if (names.includes(originName)) { layout.name = originName + '_' + names.filter(function (n) { return n === originName }).length } rootLayout.push(layout) names.push(originName) } return rootLayout } function getYogaStyle (node) { return [ 'direction', 'flex-direction', 'justify-content', 'align-content', 'align-items', 'align-self', 'position', 'flex-wrap', 'overflow', 'flex-grow', 'flex-shrink', 'flex-basis', 'left', 'top', 'right', 'bottom', 'margin-left', 'margin-top', 'margin-right', 'margin-bottom', 'padding-left', 'padding-top', 'padding-right', 'padding-bottom', 'border-left-width', 'border-top-width', 'border-right-width', 'border-bottom-width', 'width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'display' ].reduce(function (map, key) { map[key] = node.style[key] || getComputedStyle(node, null).getPropertyValue(key) return map }, {}) } var Emitter = function (lang, indent) { this.lang = lang this.indent = indent this.indents = [] this.lines = [] } Emitter.prototype = Object.create(Object.prototype, { constructor: { value: Emitter }, pushIndent: { value: function () { this.indents.push(this.indent) } }, popIndent: { value: function () { this.indents.pop() } }, push: { value: function (line) { if (line instanceof Array) { line.forEach(function (element) { this.push(element) }, this) return } else if (line.length > 0) { line = this.indents.join('') + line } this.lines.push(line) } }, print: { value: function () { console.log(this.lines.join('\n')) } } })