2 * Copyright (C) 2018 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 constructor(top, left) {
49 this.m_left += distance;
53 this.m_top += distance;
57 if (distance.top && distance.left) {
58 this.m_top += distance.top();
59 this.m_left += distance.left();
61 else if (distance.width && distance.height) {
62 this.m_top += distance.height();
63 this.m_left += distance.width();
68 return this.top() == other.top() && this.left() == other.left();
72 return new LayoutPoint(this.top(), this.left());
77 constructor(width, height) {
79 this.m_height = height;
87 this.m_height = height;
99 this.m_width += distance.width();
100 this.m_height += distance.height();
104 this.m_width -= distance.width();
105 this.m_height -= distance.height();
109 return this.m_width <= 0 || this.m_height <= 0;
113 return this.width() == other.width() && this.height() == other.height();
117 return new LayoutSize(this.width(), this.height());
122 constructor(topLeft, size) {
123 this.m_topLeft = topLeft.clone();
124 this.m_size = size.clone();
128 this.m_topLeft.setTop(top);
132 this.m_topLeft.setLeft(left);
136 this.m_size.setHeight(bottom - this.m_topLeft.top());
140 this.m_size.setWidth(right - this.m_topLeft.left());
144 return this.m_topLeft.left();
148 return this.m_topLeft.top();
152 return this.m_topLeft.top() + this.m_size.height();
156 return this.m_topLeft.left() + this.m_size.width();
159 setTopLeft(topLeft) {
160 this.m_topLeft = topLeft.clone();
164 return this.m_topLeft.clone();
168 return new LayoutPoint(this.top(), this.right());
172 return new LayoutPoint(this.bottom(), this.right());
176 this.m_size.setWidth(width);
180 this.m_size.setHeight(height);
184 this.m_size = newSize.clone();
188 return this.m_size.clone();
192 return this.m_size.width();
196 return this.m_size.height();
200 this.m_size.growBy(distance);
204 this.m_size.shrinkBy(distance);
208 this.m_topLeft.moveBy(distance);
212 return this.m_size.isEmpty();
216 return this.m_topLeft.equal(other.topLeft()) && this.m_size.equal(other.size());
220 return !this.isEmpty() && !other.isEmpty()
221 && this.left() < other.right() && other.left() < this.right()
222 && this.top() < other.bottom() && other.top() < this.bottom();
226 return this.left() <= other.left() && this.right() >= other.right()
227 && this.top() <= other.top() && this.bottom() >= other.bottom();
231 return new LayoutRect(this.topLeft().clone(), this.size().clone());
235 function ASSERT_NOT_REACHED() {
236 throw Error("Should not reach!");
239 function ASSERT(statement) {
242 throw Error("Assertion failure");
246 static computedValue(strValue, baseValue) {
247 if (strValue.indexOf("px") > -1)
248 return parseFloat(strValue);
249 if (strValue.indexOf("%") > -1)
250 return parseFloat(strValue) * baseValue / 100;
254 static propertyIsAuto(propertyName, box) {
255 if (box.isAnonymous())
257 return window.getComputedStyle(box.node()).isPropertyValueInitial(propertyName);
260 static isWidthAuto(box) {
261 return Utils.propertyIsAuto("width", box);
264 static isHeightAuto(box) {
265 return Utils.propertyIsAuto("height", box);
268 static isTopAuto(box) {
269 return Utils.propertyIsAuto("top", box);
272 static isLeftAuto(box) {
273 return Utils.propertyIsAuto("left", box);
276 static isBottomAuto(box) {
277 return Utils.propertyIsAuto("bottom", box);
280 static isRightAuto(box) {
281 return Utils.propertyIsAuto("right", box);
285 ASSERT(!Utils.isWidthAuto(box));
286 return parseFloat(window.getComputedStyle(box.node()).width);
290 ASSERT(!Utils.isHeightAuto(box));
291 return parseFloat(window.getComputedStyle(box.node()).height);
295 return parseFloat(box.node().style.top);
299 return parseFloat(box.node().style.bottom);
303 return parseFloat(box.node().style.left);
307 return parseFloat(box.node().style.right);
310 static hasBorderTop(box) {
311 return window.getComputedStyle(box.node()).borderTopWidth != "0px";
314 static hasBorderBottom(box) {
315 return window.getComputedStyle(box.node()).borderBottomWidth != "0px";
318 static hasPaddingTop(box) {
319 return window.getComputedStyle(box.node()).paddingTop != "0px";
322 static hasPaddingBottom(box) {
323 return window.getComputedStyle(box.node()).paddingBottom != "0px";
326 static computedMarginTop(node) {
327 return Utils.computedValue(window.getComputedStyle(node).marginTop);
330 static computedMarginLeft(node) {
331 return Utils.computedValue(window.getComputedStyle(node).marginLeft);
334 static computedMarginBottom(node) {
335 return Utils.computedValue(window.getComputedStyle(node).marginBottom);
338 static computedMarginRight(node) {
339 return Utils.computedValue(window.getComputedStyle(node).marginRight);
342 static computedBorderTopLeft(node) {
343 return new LayoutSize(Utils.computedValue(window.getComputedStyle(node).borderLeftWidth), Utils.computedValue(window.getComputedStyle(node).borderTopWidth));
346 static computedBorderBottomRight(node) {
347 return new LayoutSize(Utils.computedValue(window.getComputedStyle(node).borderRightWidth), Utils.computedValue(window.getComputedStyle(node).borderBottomWidth));
350 static computedPaddingTopLeft(node) {
351 return new LayoutSize(Utils.computedValue(window.getComputedStyle(node).paddingLeft), Utils.computedValue(window.getComputedStyle(node).paddingTop));
354 static computedPaddingBottomRight(node) {
355 return new LayoutSize(Utils.computedValue(window.getComputedStyle(node).paddingRight), Utils.computedValue(window.getComputedStyle(node).paddingBottom));
358 static computedBorderAndPaddingTop(node) {
359 return Utils.computedBorderTopLeft(node).height() + Utils.computedPaddingTopLeft(node).height();
362 static computedBorderAndPaddingLeft(node) {
363 return Utils.computedBorderTopLeft(node).width() + Utils.computedPaddingTopLeft(node).width();
366 static computedBorderAndPaddingTop(node) {
367 return Utils.computedBorderTopLeft(node).height() + Utils.computedPaddingTopLeft(node).height();
370 static computedBorderAndPaddingLeft(node) {
371 return Utils.computedBorderTopLeft(node).width() + Utils.computedPaddingTopLeft(node).width();
374 static computedBorderAndPaddingBottom(node) {
375 return Utils.computedBorderBottomRight(node).height() + Utils.computedPaddingBottomRight(node).height();
378 static computedBorderAndPaddingRight(node) {
379 return Utils.computedBorderBottomRight(node).width() + Utils.computedPaddingBottomRight(node).width();
382 static computedHorizontalBorderAndPadding(node) {
383 return this.computedBorderAndPaddingLeft(node) + this.computedBorderAndPaddingRight(node);
386 static computedVerticalBorderAndPadding(node) {
387 return this.computedBorderAndPaddingTop(node) + this.computedBorderAndPaddingBottom(node);
390 static computedLineHeight(node) {
391 return Utils.computedValue(window.getComputedStyle(node).lineHeight);
394 static hasClear(box) {
395 return Utils.hasClearLeft(box) || Utils.hasClearRight(box) || Utils.hasClearBoth(box);
398 static hasClearLeft(box) {
399 return window.getComputedStyle(box.node()).clear == "left";
402 static hasClearRight(box) {
403 return window.getComputedStyle(box.node()).clear == "right";
406 static hasClearBoth(box) {
407 return window.getComputedStyle(box.node()).clear == "both";
410 static isBlockLevelElement(node) {
413 let display = window.getComputedStyle(node).display;
414 return display == "block" || display == "list-item" || display == "table";
417 static isBlockContainerElement(node) {
418 if (node.nodeType != Node.ELEMENT_NODE)
420 let display = window.getComputedStyle(node).display;
421 return display == "block" || display == "list-item" || display == "inline-block" || display == "table-cell" || display == "table-caption"; //TODO && !replaced element
424 static isInlineLevelElement(node) {
425 let display = window.getComputedStyle(node).display;
426 return display == "inline" || display == "inline-block" || display == "inline-table";
429 static isTableElement(node) {
430 let display = window.getComputedStyle(node).display;
431 return display == "table" || display == "inline-table";
434 static isRelativelyPositioned(box) {
435 if (box.isAnonymous())
437 let node = box.node();
438 return window.getComputedStyle(node).position == "relative";
441 static isAbsolutelyPositioned(box) {
442 if (box.isAnonymous())
444 let node = box.node();
445 return window.getComputedStyle(node).position == "absolute";
448 static isFixedPositioned(box) {
449 if (box.isAnonymous())
451 let node = box.node();
452 return window.getComputedStyle(node).position == "fixed";
455 static isStaticallyPositioned(box) {
456 if (box.isAnonymous())
458 let node = box.node();
459 return (Utils.propertyIsAuto("top", box) && Utils.propertyIsAuto("bottom", box)) || (Utils.propertyIsAuto("left", box) && Utils.propertyIsAuto("right", box));
462 static isOverflowVisible(box) {
463 return window.getComputedStyle(box.node()).overflow == "visible";
466 static isFloatingPositioned(box) {
467 if (box.isAnonymous())
469 let node = box.node();
470 return window.getComputedStyle(node).float != "none";
473 static isFloatingLeft(box) {
474 let node = box.node();
475 return window.getComputedStyle(node).float == "left";
478 static mapPosition(position, box, container) {
479 ASSERT(box instanceof Display.Box);
480 ASSERT(container instanceof Display.Box);
482 if (box == container)
484 for (let ascendant = box.parent(); ascendant && ascendant != container; ascendant = ascendant.parent())
485 position.moveBy(ascendant.topLeft());
489 static marginBox(box, container) {
490 let marginBox = box.marginBox();
491 let mappedPosition = Utils.mapPosition(marginBox.topLeft(), box, container);
492 return new LayoutRect(mappedPosition, marginBox.size());
495 static borderBox(box, container) {
496 let borderBox = box.borderBox();
497 let mappedPosition = Utils.mapPosition(box.topLeft(), box, container);
498 mappedPosition.moveBy(borderBox.topLeft());
499 return new LayoutRect(mappedPosition, borderBox.size());
502 static contentBox(box, container) {
503 let contentBox = box.contentBox();
504 let mappedPosition = Utils.mapPosition(box.topLeft(), box, container);
505 mappedPosition.moveBy(contentBox.topLeft());
506 return new LayoutRect(mappedPosition, contentBox.size());
509 static textRuns(text, container) {
510 return window.collectTextRuns(text, container.node());
513 static textRunsForLine(text, availableSpace, container) {
514 return window.collectTextRuns(text, container.node(), availableSpace);
517 static nextBreakingOpportunity(textBox, currentPosition)
519 return window.nextBreakingOpportunity(textBox.content(), currentPosition);
522 static measureText(texBox, start, end)
524 return texBox.node().textWidth(start, end);
527 static textHeight(textBox)
529 return textBox.text().node().textHeight();
532 // "RenderView at (0,0) size 1317x366\n HTML RenderBlock at (0,0) size 1317x116\n BODY RenderBody at (8,8) size 1301x100\n DIV RenderBlock at (0,0) size 100x100\n";
533 static layoutTreeDump(initialContainingBlock, layoutState) {
534 return this._dumpBox(layoutState, initialContainingBlock, 1) + this._dumpTree(layoutState, initialContainingBlock, 2);
537 static _findDisplayBox(layoutState, box) {
538 for (let formattingState of layoutState.formattingStates()) {
539 let displayBox = formattingState[1].displayBoxMap().get(box);
543 ASSERT(!box.parent());
544 return layoutState.initialDisplayBox();
547 static _dumpBox(layoutState, box, level) {
548 // Skip anonymous boxes for now -This is the case where WebKit does not generate an anon inline container for text content where the text is a direct child
549 // of a block container.
550 let indentation = " ".repeat(level);
551 if (box instanceof Layout.InlineBox) {
553 return indentation + "#text RenderText\n";
555 if (box.isAnonymous())
557 let displayBox = Utils._findDisplayBox(layoutState, box);
558 let boxRect = displayBox.rect();
559 return indentation + (box.node().tagName ? (box.node().tagName + " ") : "") + box.name() + " at (" + boxRect.left() + "," + boxRect.top() + ") size " + boxRect.width() + "x" + boxRect.height() + "\n";
562 static _dumpLines(layoutState, root, level) {
563 ASSERT(root.establishesInlineFormattingContext());
564 let inlineFormattingState = layoutState.formattingState(root);
565 let lines = inlineFormattingState.lines();
567 let indentation = " ".repeat(level);
568 lines.forEach(function(line) {
569 let lineRect = line.rect();
570 content += indentation + "RootInlineBox at (" + lineRect.left() + "," + lineRect.top() + ") size " + Utils.precisionRound(lineRect.width(), 2) + "x" + lineRect.height() + "\n";
571 line.lineBoxes().forEach(function(lineBox) {
572 let indentation = " ".repeat(level + 1);
573 content += indentation + "InlineTextBox at (" + Utils.precisionRound(lineBox.lineBoxRect.left(), 2) + "," + Utils.precisionRound(lineBox.lineBoxRect.top(), 2) + ") size " + Utils.precisionRound(lineBox.lineBoxRect.width(), 2) + "x" + lineBox.lineBoxRect.height() + "\n";
579 static _dumpTree(layoutState, root, level) {
581 if (root.isBlockContainerBox() && root.establishesInlineFormattingContext())
582 content += this._dumpLines(layoutState, root, level);
583 for (let child = root.firstChild(); child; child = child.nextSibling()) {
584 content += this._dumpBox(layoutState, child, level);
585 if (child.isContainer())
586 content += this._dumpTree(layoutState, child, level + 1, content);
591 static precisionRound(number, precision) {
592 var factor = Math.pow(10, precision);
593 return Math.round(number * factor) / factor;