summaryrefslogtreecommitdiffstats
path: root/dom/flex
diff options
context:
space:
mode:
Diffstat (limited to 'dom/flex')
-rw-r--r--dom/flex/Flex.cpp67
-rw-r--r--dom/flex/Flex.h49
-rw-r--r--dom/flex/FlexItemValues.cpp96
-rw-r--r--dom/flex/FlexItemValues.h67
-rw-r--r--dom/flex/FlexLineValues.cpp73
-rw-r--r--dom/flex/FlexLineValues.h58
-rw-r--r--dom/flex/moz.build28
-rw-r--r--dom/flex/test/chrome.toml15
-rw-r--r--dom/flex/test/chrome/test_flex_axis_directions.html204
-rw-r--r--dom/flex/test/chrome/test_flex_item_clamp.html169
-rw-r--r--dom/flex/test/chrome/test_flex_item_rect.html124
-rw-r--r--dom/flex/test/chrome/test_flex_items.html316
-rw-r--r--dom/flex/test/chrome/test_flex_lines.html336
-rw-r--r--dom/flex/test/chrome/test_flex_object.html132
-rw-r--r--dom/flex/test/chrome/test_flex_parent.html133
15 files changed, 1867 insertions, 0 deletions
diff --git a/dom/flex/Flex.cpp b/dom/flex/Flex.cpp
new file mode 100644
index 0000000000..898e81d4f5
--- /dev/null
+++ b/dom/flex/Flex.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Flex.h"
+
+#include "FlexLineValues.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/FlexBinding.h"
+#include "nsFlexContainerFrame.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Flex, mParent, mLines)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Flex)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Flex)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Flex)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Flex::Flex(Element* aParent, nsFlexContainerFrame* aFrame) : mParent(aParent) {
+ MOZ_ASSERT(aFrame,
+ "Should never be instantiated with a null nsFlexContainerFrame");
+
+ // Eagerly create property values from aFrame, because we're not
+ // going to keep it around.
+ const ComputedFlexContainerInfo* containerInfo =
+ aFrame->GetFlexContainerInfo();
+ if (!containerInfo) {
+ // It's weird but possible to fail to get a ComputedFlexContainerInfo
+ // structure. Assign sensible default values.
+ mMainAxisDirection = FlexPhysicalDirection::Horizontal_lr;
+ mCrossAxisDirection = FlexPhysicalDirection::Vertical_tb;
+ return;
+ }
+ mLines.SetLength(containerInfo->mLines.Length());
+ uint32_t index = 0;
+ for (auto&& l : containerInfo->mLines) {
+ FlexLineValues* line = new FlexLineValues(this, &l);
+ mLines.ElementAt(index) = line;
+ index++;
+ }
+
+ mMainAxisDirection = containerInfo->mMainAxisDirection;
+ mCrossAxisDirection = containerInfo->mCrossAxisDirection;
+}
+
+JSObject* Flex::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return Flex_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void Flex::GetLines(nsTArray<RefPtr<FlexLineValues>>& aResult) {
+ aResult.AppendElements(mLines);
+}
+
+FlexPhysicalDirection Flex::MainAxisDirection() const {
+ return mMainAxisDirection;
+}
+
+FlexPhysicalDirection Flex::CrossAxisDirection() const {
+ return mCrossAxisDirection;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/flex/Flex.h b/dom/flex/Flex.h
new file mode 100644
index 0000000000..7476585a3c
--- /dev/null
+++ b/dom/flex/Flex.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Flex_h
+#define mozilla_dom_Flex_h
+
+#include "mozilla/dom/FlexBinding.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+
+class nsFlexContainerFrame;
+
+namespace mozilla::dom {
+
+class Element;
+class FlexLineValues;
+
+class Flex : public nsISupports, public nsWrapperCache {
+ public:
+ explicit Flex(Element* aParent, nsFlexContainerFrame* aFrame);
+
+ protected:
+ virtual ~Flex() = default;
+
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Flex)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ Element* GetParentObject() { return mParent; }
+
+ void GetLines(nsTArray<RefPtr<FlexLineValues>>& aResult);
+ FlexPhysicalDirection MainAxisDirection() const;
+ FlexPhysicalDirection CrossAxisDirection() const;
+
+ protected:
+ nsCOMPtr<Element> mParent;
+ nsTArray<RefPtr<FlexLineValues>> mLines;
+ FlexPhysicalDirection mMainAxisDirection;
+ FlexPhysicalDirection mCrossAxisDirection;
+};
+
+} // namespace mozilla::dom
+
+#endif /* mozilla_dom_Flex_h */
diff --git a/dom/flex/FlexItemValues.cpp b/dom/flex/FlexItemValues.cpp
new file mode 100644
index 0000000000..9a9be8412f
--- /dev/null
+++ b/dom/flex/FlexItemValues.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FlexItemValues.h"
+
+#include "mozilla/dom/DOMRect.h"
+#include "mozilla/dom/FlexBinding.h"
+#include "mozilla/dom/FlexLineValues.h"
+#include "nsFlexContainerFrame.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FlexItemValues, mParent, mNode,
+ mFrameRect)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(FlexItemValues)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(FlexItemValues)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FlexItemValues)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
+ * Utility function to convert a nscoord size to CSS pixel values, with
+ * graceful treatment of NS_UNCONSTRAINEDSIZE (which this function converts to
+ * the double "positive infinity" value).
+ *
+ * This function is suitable for converting quantities that are expected to
+ * sometimes legitimately (rather than arbitrarily/accidentally) contain the
+ * sentinel value NS_UNCONSTRAINEDSIZE -- e.g. to handle "max-width: none".
+ */
+static double ToPossiblyUnconstrainedPixels(nscoord aSize) {
+ if (aSize == NS_UNCONSTRAINEDSIZE) {
+ return std::numeric_limits<double>::infinity();
+ }
+ return nsPresContext::AppUnitsToDoubleCSSPixels(aSize);
+}
+
+FlexItemValues::FlexItemValues(FlexLineValues* aParent,
+ const ComputedFlexItemInfo* aItem)
+ : mParent(aParent) {
+ MOZ_ASSERT(aItem,
+ "Should never be instantiated with a null ComputedFlexLineInfo.");
+
+ // Eagerly copy values from aItem, because we're not
+ // going to keep it around.
+ mNode = aItem->mNode;
+
+ // Since mNode might be null, we associate the mFrameRect with
+ // our parent.
+ mFrameRect = new DOMRectReadOnly(
+ mParent, nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mFrameRect.X()),
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mFrameRect.Y()),
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mFrameRect.Width()),
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mFrameRect.Height()));
+
+ // Convert app unit sizes to css pixel sizes.
+ mMainBaseSize =
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mMainBaseSize);
+ mMainDeltaSize =
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mMainDeltaSize);
+ mMainMinSize = nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mMainMinSize);
+ mMainMaxSize = ToPossiblyUnconstrainedPixels(aItem->mMainMaxSize);
+ mCrossMinSize =
+ nsPresContext::AppUnitsToDoubleCSSPixels(aItem->mCrossMinSize);
+ mCrossMaxSize = ToPossiblyUnconstrainedPixels(aItem->mCrossMaxSize);
+
+ mClampState = aItem->mClampState;
+}
+
+JSObject* FlexItemValues::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return FlexItemValues_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsINode* FlexItemValues::GetNode() const { return mNode; }
+
+DOMRectReadOnly* FlexItemValues::FrameRect() const { return mFrameRect; }
+
+double FlexItemValues::MainBaseSize() const { return mMainBaseSize; }
+
+double FlexItemValues::MainDeltaSize() const { return mMainDeltaSize; }
+
+double FlexItemValues::MainMinSize() const { return mMainMinSize; }
+
+double FlexItemValues::MainMaxSize() const { return mMainMaxSize; }
+
+double FlexItemValues::CrossMinSize() const { return mCrossMinSize; }
+
+double FlexItemValues::CrossMaxSize() const { return mCrossMaxSize; }
+
+FlexItemClampState FlexItemValues::ClampState() const { return mClampState; }
+
+} // namespace mozilla::dom
diff --git a/dom/flex/FlexItemValues.h b/dom/flex/FlexItemValues.h
new file mode 100644
index 0000000000..2853a922bb
--- /dev/null
+++ b/dom/flex/FlexItemValues.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FlexItemValues_h
+#define mozilla_dom_FlexItemValues_h
+
+#include "mozilla/dom/FlexBinding.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+
+struct ComputedFlexItemInfo;
+
+class nsINode;
+
+namespace mozilla::dom {
+
+class DOMRectReadOnly;
+
+class FlexLineValues;
+
+class FlexItemValues : public nsISupports, public nsWrapperCache {
+ public:
+ explicit FlexItemValues(FlexLineValues* aParent,
+ const ComputedFlexItemInfo* aItem);
+
+ protected:
+ virtual ~FlexItemValues() = default;
+
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FlexItemValues)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ FlexLineValues* GetParentObject() { return mParent; }
+
+ nsINode* GetNode() const;
+ DOMRectReadOnly* FrameRect() const;
+ double MainBaseSize() const;
+ double MainDeltaSize() const;
+ double MainMinSize() const;
+ double MainMaxSize() const;
+ double CrossMinSize() const;
+ double CrossMaxSize() const;
+ FlexItemClampState ClampState() const;
+
+ protected:
+ RefPtr<FlexLineValues> mParent;
+ RefPtr<nsINode> mNode;
+ RefPtr<DOMRectReadOnly> mFrameRect;
+
+ // These sizes are all CSS pixel units.
+ double mMainBaseSize;
+ double mMainDeltaSize;
+ double mMainMinSize;
+ double mMainMaxSize;
+ double mCrossMinSize;
+ double mCrossMaxSize;
+ FlexItemClampState mClampState;
+};
+
+} // namespace mozilla::dom
+
+#endif /* mozilla_dom_FlexItemValues_h */
diff --git a/dom/flex/FlexLineValues.cpp b/dom/flex/FlexLineValues.cpp
new file mode 100644
index 0000000000..aac403d58a
--- /dev/null
+++ b/dom/flex/FlexLineValues.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FlexLineValues.h"
+
+#include "Flex.h"
+#include "FlexItemValues.h"
+#include "mozilla/dom/FlexBinding.h"
+#include "nsFlexContainerFrame.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FlexLineValues, mParent, mItems)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(FlexLineValues)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(FlexLineValues)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FlexLineValues)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+FlexLineValues::FlexLineValues(Flex* aParent, const ComputedFlexLineInfo* aLine)
+ : mParent(aParent) {
+ MOZ_ASSERT(aLine,
+ "Should never be instantiated with a null ComputedFlexLineInfo.");
+
+ // Eagerly copy values from aLine, because we're not
+ // going to keep it around.
+ mGrowthState = aLine->mGrowthState;
+
+ // Convert all the app unit values into css pixels.
+ mCrossStart = nsPresContext::AppUnitsToDoubleCSSPixels(aLine->mCrossStart);
+ mCrossSize = nsPresContext::AppUnitsToDoubleCSSPixels(aLine->mCrossSize);
+ mFirstBaselineOffset =
+ nsPresContext::AppUnitsToDoubleCSSPixels(aLine->mFirstBaselineOffset);
+ mLastBaselineOffset =
+ nsPresContext::AppUnitsToDoubleCSSPixels(aLine->mLastBaselineOffset);
+
+ mItems.SetLength(aLine->mItems.Length());
+ uint32_t index = 0;
+ for (auto&& i : aLine->mItems) {
+ FlexItemValues* item = new FlexItemValues(this, &i);
+ mItems.ElementAt(index) = item;
+ index++;
+ }
+}
+
+JSObject* FlexLineValues::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return FlexLineValues_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+FlexLineGrowthState FlexLineValues::GrowthState() const { return mGrowthState; }
+
+double FlexLineValues::CrossStart() const { return mCrossStart; }
+
+double FlexLineValues::CrossSize() const { return mCrossSize; }
+
+double FlexLineValues::FirstBaselineOffset() const {
+ return mFirstBaselineOffset;
+}
+
+double FlexLineValues::LastBaselineOffset() const {
+ return mLastBaselineOffset;
+}
+
+void FlexLineValues::GetItems(nsTArray<RefPtr<FlexItemValues>>& aResult) {
+ aResult.AppendElements(mItems);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/flex/FlexLineValues.h b/dom/flex/FlexLineValues.h
new file mode 100644
index 0000000000..340af2e7e5
--- /dev/null
+++ b/dom/flex/FlexLineValues.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FlexLineValues_h
+#define mozilla_dom_FlexLineValues_h
+
+#include "mozilla/dom/FlexBinding.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+
+struct ComputedFlexLineInfo;
+
+namespace mozilla::dom {
+
+class Flex;
+class FlexItemValues;
+
+class FlexLineValues : public nsISupports, public nsWrapperCache {
+ public:
+ explicit FlexLineValues(Flex* aParent, const ComputedFlexLineInfo* aLine);
+
+ protected:
+ virtual ~FlexLineValues() = default;
+
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FlexLineValues)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ Flex* GetParentObject() { return mParent; }
+
+ FlexLineGrowthState GrowthState() const;
+ double CrossStart() const;
+ double CrossSize() const;
+ double FirstBaselineOffset() const;
+ double LastBaselineOffset() const;
+
+ void GetItems(nsTArray<RefPtr<FlexItemValues>>& aResult);
+
+ protected:
+ RefPtr<Flex> mParent;
+
+ FlexLineGrowthState mGrowthState;
+ double mCrossStart;
+ double mCrossSize;
+ double mFirstBaselineOffset;
+ double mLastBaselineOffset;
+
+ nsTArray<RefPtr<FlexItemValues>> mItems;
+};
+
+} // namespace mozilla::dom
+
+#endif /* mozilla_dom_FlexLineValues_h */
diff --git a/dom/flex/moz.build b/dom/flex/moz.build
new file mode 100644
index 0000000000..073fa269da
--- /dev/null
+++ b/dom/flex/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
+
+MOCHITEST_CHROME_MANIFESTS += ["test/chrome.toml"]
+
+EXPORTS.mozilla.dom += [
+ "Flex.h",
+ "FlexItemValues.h",
+ "FlexLineValues.h",
+]
+
+UNIFIED_SOURCES += [
+ "Flex.cpp",
+ "FlexItemValues.cpp",
+ "FlexLineValues.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/layout/generic",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/flex/test/chrome.toml b/dom/flex/test/chrome.toml
new file mode 100644
index 0000000000..477f9d2476
--- /dev/null
+++ b/dom/flex/test/chrome.toml
@@ -0,0 +1,15 @@
+[DEFAULT]
+
+["chrome/test_flex_axis_directions.html"]
+
+["chrome/test_flex_item_clamp.html"]
+
+["chrome/test_flex_item_rect.html"]
+
+["chrome/test_flex_items.html"]
+
+["chrome/test_flex_lines.html"]
+
+["chrome/test_flex_object.html"]
+
+["chrome/test_flex_parent.html"]
diff --git a/dom/flex/test/chrome/test_flex_axis_directions.html b/dom/flex/test/chrome/test_flex_axis_directions.html
new file mode 100644
index 0000000000..cc9f85eaf6
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_axis_directions.html
@@ -0,0 +1,204 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+ f {
+ display: flex;
+ width: 400px;
+ height: 50px;
+ margin-bottom: 2px;
+ }
+
+ b {
+ background-color: gold;
+ width: 100px;
+ height: 15px;
+ }
+ b::after {
+ content: "B";
+ }
+
+ c {
+ background-color: yellow;
+ width: 100px;
+ height: 15px;
+ }
+ c::after {
+ content: "C";
+ }
+
+ d {
+ background-color: orange;
+ width: 100px;
+ height: 15px;
+ }
+ d::after {
+ content: "D";
+ }
+
+ .fdR {
+ flex-direction: row;
+ background-color: lightgrey;
+ }
+ .fdRR {
+ flex-direction: row-reverse;
+ background-color: lightgreen;
+ }
+ .fdC {
+ flex-direction: column;
+ background-color: lightblue;
+ }
+ .fdCR {
+ flex-direction: column-reverse;
+ background-color: lavender;
+ }
+
+ .wmHTB {
+ writing-mode: horizontal-tb;
+ }
+ .wmVLR {
+ writing-mode: vertical-lr;
+ }
+ .wmVRL {
+ writing-mode: vertical-rl;
+ }
+ .wmSLR {
+ writing-mode: sideways-lr;
+ }
+ .wmSRL {
+ writing-mode: sideways-rl;
+ }
+
+ .dLR {
+ direction: ltr;
+ }
+ .dRL {
+ direction: rtl;
+ }
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function testContainerMatchesExpectedValues(flex, values, flexIndex) {
+ is(flex.mainAxisDirection, values.m, "Flex index " + flexIndex + " should have expected mainAxisDirection.");
+ is(flex.crossAxisDirection, values.c, "Flex index " + flexIndex + " should have expected crossAxisDirection.");
+}
+
+function runTests() {
+ const expectedValues = [
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "vertical-tb", c: "horizontal-lr" },
+ { m: "vertical-bt", c: "horizontal-lr" },
+ { m: "vertical-tb", c: "horizontal-rl" },
+ { m: "vertical-bt", c: "horizontal-rl" },
+ { m: "vertical-bt", c: "horizontal-lr" },
+ { m: "vertical-tb", c: "horizontal-lr" },
+ { m: "vertical-tb", c: "horizontal-rl" },
+ { m: "vertical-bt", c: "horizontal-rl" },
+
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "vertical-bt", c: "horizontal-lr" },
+ { m: "vertical-tb", c: "horizontal-lr" },
+ { m: "vertical-bt", c: "horizontal-rl" },
+ { m: "vertical-tb", c: "horizontal-rl" },
+ { m: "vertical-tb", c: "horizontal-lr" },
+ { m: "vertical-bt", c: "horizontal-lr" },
+ { m: "vertical-bt", c: "horizontal-rl" },
+ { m: "vertical-tb", c: "horizontal-rl" },
+
+ { m: "vertical-tb", c: "horizontal-lr" },
+ { m: "vertical-tb", c: "horizontal-rl" },
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "horizontal-lr", c: "vertical-bt" },
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "horizontal-rl", c: "vertical-bt" },
+ { m: "horizontal-lr", c: "vertical-bt" },
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "horizontal-rl", c: "vertical-bt" },
+
+ { m: "vertical-bt", c: "horizontal-lr" },
+ { m: "vertical-bt", c: "horizontal-rl" },
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "horizontal-rl", c: "vertical-bt" },
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "horizontal-lr", c: "vertical-bt" },
+ { m: "horizontal-rl", c: "vertical-bt" },
+ { m: "horizontal-rl", c: "vertical-tb" },
+ { m: "horizontal-lr", c: "vertical-tb" },
+ { m: "horizontal-lr", c: "vertical-bt" },
+ ];
+
+ const children = document.body.children;
+ is(children.length, expectedValues.length, "Document should have expected number of flex containers.");
+
+ for (let i = 0; i < children.length; ++i) {
+ const flex = children.item(i).getAsFlexContainer();
+ ok(flex, "Document child index " + i + " should be a flex container.");
+ if (flex) {
+ const values = expectedValues[i];
+ testContainerMatchesExpectedValues(flex, values, i);
+ }
+ }
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+
+<f class="fdR wmHTB dLR"><b></b><c></c><d></d></f>
+<f class="fdR wmHTB dRL"><b></b><c></c><d></d></f>
+<f class="fdR wmVLR dLR"><b></b><c></c><d></d></f>
+<f class="fdR wmVLR dRL"><b></b><c></c><d></d></f>
+<f class="fdR wmVRL dLR"><b></b><c></c><d></d></f>
+<f class="fdR wmVRL dRL"><b></b><c></c><d></d></f>
+<f class="fdR wmSLR dLR"><b></b><c></c><d></d></f>
+<f class="fdR wmSLR dRL"><b></b><c></c><d></d></f>
+<f class="fdR wmSRL dLR"><b></b><c></c><d></d></f>
+<f class="fdR wmSRL dRL"><b></b><c></c><d></d></f>
+
+<f class="fdRR wmHTB dLR"><b></b><c></c><d></d></f>
+<f class="fdRR wmHTB dRL"><b></b><c></c><d></d></f>
+<f class="fdRR wmVLR dLR"><b></b><c></c><d></d></f>
+<f class="fdRR wmVLR dRL"><b></b><c></c><d></d></f>
+<f class="fdRR wmVRL dLR"><b></b><c></c><d></d></f>
+<f class="fdRR wmVRL dRL"><b></b><c></c><d></d></f>
+<f class="fdRR wmSLR dLR"><b></b><c></c><d></d></f>
+<f class="fdRR wmSLR dRL"><b></b><c></c><d></d></f>
+<f class="fdRR wmSRL dLR"><b></b><c></c><d></d></f>
+<f class="fdRR wmSRL dRL"><b></b><c></c><d></d></f>
+
+<f class="fdC wmHTB dLR"><b></b><c></c><d></d></f>
+<f class="fdC wmHTB dRL"><b></b><c></c><d></d></f>
+<f class="fdC wmVLR dLR"><b></b><c></c><d></d></f>
+<f class="fdC wmVLR dRL"><b></b><c></c><d></d></f>
+<f class="fdC wmVRL dLR"><b></b><c></c><d></d></f>
+<f class="fdC wmVRL dRL"><b></b><c></c><d></d></f>
+<f class="fdC wmSLR dLR"><b></b><c></c><d></d></f>
+<f class="fdC wmSLR dRL"><b></b><c></c><d></d></f>
+<f class="fdC wmSRL dLR"><b></b><c></c><d></d></f>
+<f class="fdC wmSRL dRL"><b></b><c></c><d></d></f>
+
+<f class="fdCR wmHTB dLR"><b></b><c></c><d></d></f>
+<f class="fdCR wmHTB dRL"><b></b><c></c><d></d></f>
+<f class="fdCR wmVLR dLR"><b></b><c></c><d></d></f>
+<f class="fdCR wmVLR dRL"><b></b><c></c><d></d></f>
+<f class="fdCR wmVRL dLR"><b></b><c></c><d></d></f>
+<f class="fdCR wmVRL dRL"><b></b><c></c><d></d></f>
+<f class="fdCR wmSLR dLR"><b></b><c></c><d></d></f>
+<f class="fdCR wmSLR dRL"><b></b><c></c><d></d></f>
+<f class="fdCR wmSRL dLR"><b></b><c></c><d></d></f>
+<f class="fdCR wmSRL dRL"><b></b><c></c><d></d></f>
+
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_item_clamp.html b/dom/flex/test/chrome/test_flex_item_clamp.html
new file mode 100644
index 0000000000..6624b3ee20
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_item_clamp.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+f {
+ display: flex;
+ background-color: grey;
+ width: 400px;
+ height: 20px;
+ margin-bottom: 5px;
+}
+
+b {
+ flex-basis: 100px;
+ flex-grow: 1;
+ flex-shrink: 1;
+ background-color: gold;
+}
+
+c {
+ flex-basis: 100px;
+ flex-grow: 1;
+ flex-shrink: 1;
+ background-color: yellow;
+}
+
+d {
+ flex: none;
+ background-color: orange;
+}
+
+b::after, c::after, d::after {
+ content: "";
+ display: block;
+ width: 10px;
+ height: 10px;
+ border: 1px solid teal;
+}
+
+
+.min50 {
+ min-width: 50px;
+}
+.min370 {
+ min-width: 370px;
+}
+.min400 {
+ min-width: 400px;
+}
+
+.max5 {
+ max-width: 5px;
+}
+.max50 {
+ max-width: 50px;
+}
+
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+const TEXT_NODE = Node.TEXT_NODE;
+
+function testItemMatchesExpectedValues(item, values, index) {
+ is(item.clampState, values.cs, "Item index " + index + " should have expected clampState.");
+}
+
+function runTests() {
+ /**
+ * The expectedValues array contains one element for each flex container child of
+ * of the body. The values in this object are compared against the returned flex
+ * API values of the first flex item in the first line of the corresponding flex
+ * container. The "cs" value is compared against the flex item's clampState.
+ **/
+ const expectedValues = [
+ { cs: "unclamped" },
+ { cs: "unclamped" },
+ { cs: "unclamped" },
+ { cs: "unclamped" },
+
+ { cs: "clamped_to_min" },
+ { cs: "clamped_to_min" },
+ { cs: "clamped_to_min" },
+ { cs: "clamped_to_min" },
+ { cs: "clamped_to_min" },
+
+ { cs: "clamped_to_max" },
+ { cs: "clamped_to_max" },
+ { cs: "clamped_to_max" },
+ { cs: "clamped_to_max" },
+ { cs: "clamped_to_max" },
+ ];
+
+ let children = document.body.children;
+ is(children.length, expectedValues.length, "Document should have expected number of flex containers.");
+
+ for (let i = 0; i < children.length; ++i) {
+ const flex = children.item(i).getAsFlexContainer();
+ ok(flex, "Document child index " + i + " should be a flex container.");
+ if (flex) {
+ const values = expectedValues[i];
+ const item = flex.getLines()[0].getItems()[0];
+ testItemMatchesExpectedValues(item, values, i);
+ }
+ }
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+ <!-- unclamped cases -->
+ <!-- a flex:none item -->
+ <f><d></d></f>
+
+ <!-- a flex-grow item with room to grow -->
+ <f><b class="min370"></b></f>
+
+ <!-- a flex-shrink item with room to shrink -->
+ <f><b class="max50"></b><c class="min370"></c></f>
+
+ <!-- a flex-grow basis 100px item paired with a basis 200px item, where the second item is clamped,
+ and the first item then can grow past its minimum -->
+ <f><b style="min-width: 170px"></b><c class="max50" style="flex-basis:200px"></c></f>
+
+
+ <!-- clamped_to_min cases -->
+ <!-- a flex-grow item with a min smaller than the container -->
+ <f><b class="min370"></b><c></c></f>
+
+ <!-- a flex-shrink item with a min, paired with another that in total exceeds the container -->
+ <f><b class="min50"></b><c class="min370"></c></f>
+
+ <!-- a flex-shrink item shrunk to its (content-based) automatic minimum size -->
+ <f><b></b><c class="min400"></c></f>
+
+ <!-- a flex:none item with a min that is larger than its flex base size -->
+ <f><d class="min50"></d><c></c></f>
+
+ <!-- a flex-grow item paired with another flex-grow item that have equal-sized violations of
+ the first item's min with the second item's max -->
+ <f><b style="min-width: 200px"></b><c style="flex-basis:150px; max-width:200px"></c></f>
+
+
+ <!-- clamped_to_max cases -->
+ <!-- a flexible item with a max -->
+ <f><b class="max50"></b></f>
+
+ <!-- a flexible item with a max, paired with another flex-grow item -->
+ <f><b class="max50"></b><c></c></f>
+
+ <!-- a flexible item with a max smaller than its content size -->
+ <f><b class="max5"></b><c></c></f>
+
+ <!-- a flex:none item with a max smaller than its content size -->
+ <f><d class="max5"></d><c></c></f>
+
+ <!-- a flex-grow item paired with another flex-grow item that have equal-sized violations of
+ the first item's max with the second item's min -->
+ <f><b style="flex-basis:150px; max-width:200px"></b><c style="min-width: 200px"></c></f>
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_item_rect.html b/dom/flex/test/chrome/test_flex_item_rect.html
new file mode 100644
index 0000000000..c535ea756b
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_item_rect.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+f {
+ display: flex;
+ background-color: grey;
+ width: 400px;
+ height: 25px;
+ margin-bottom: 5px;
+}
+
+f > * {
+ width: 100px;
+ height: 10px;
+}
+
+b {
+ background-color: gold;
+}
+
+c {
+ background-color: yellow;
+}
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function testItemMatchesExpectedValues(item, values, index) {
+ is(item.frameRect.x, values.x,
+ "Item index " + index + " should have expected frameRect.x.");
+ is(item.frameRect.y, values.y,
+ "Item index " + index + " should have expected frameRect.y.");
+ if (typeof(values.width) != "undefined") {
+ is(item.frameRect.width, values.width,
+ "Item index " + index + " should have expected frameRect.width.");
+ }
+ is(item.frameRect.height, values.height,
+ "Item index " + index + " should have expected frameRect.height.");
+}
+
+function runTests() {
+ /**
+ * The expectedValues array contains one rect for each flex container child of
+ * of the body. The values in this object are compared against the returned flex
+ * API values of the LAST flex item in the first line of the corresponding flex
+ * container.
+ **/
+ const expectedValues = [
+ { x: 0, y: 0, width: 100, height: 10 },
+ { x: 0, y: 0, width: undefined /* not tested */, height: 25 /* stretched to container */ },
+ { x: 0, y: 0, width: 100, height: 10 },
+ { x: 0, y: 0, width: 100, height: 10 },
+ { x: 100, y: 0, width: 100, height: 10 },
+ { x: 10, y: 10, width: 100, height: 10 },
+ { x: 10, y: 10, width: 100, height: 10 },
+ { x: 0, y: 0, width: 200, height: 20 },
+ { x: 0, y: 0, width: 400, height: 25 },
+ { x: 0, y: 0, width: 100, height: 10 },
+ { x: 0, y: 0, width: 100, height: 10 },
+ ];
+
+ let children = document.body.children;
+ is(children.length, expectedValues.length, "Document should have expected number of flex containers.");
+
+ for (let i = 0; i < children.length; ++i) {
+ const flex = children.item(i).getAsFlexContainer();
+ ok(flex, "Document child index " + i + " should be a flex container.");
+ if (flex) {
+ const values = expectedValues[i];
+ const firstLine = flex.getLines()[0];
+ const items = firstLine.getItems();
+ const lastItem = items[items.length - 1];
+ testItemMatchesExpectedValues(lastItem, values, i);
+ }
+ }
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+ <!-- a single item -->
+ <f><b></b></f>
+
+ <!-- an anonymous box item around a text node -->
+ <f style="font-size: 10px">anonymous</f>
+
+ <!-- a table item -->
+ <f><table></table></f>
+
+ <!-- a display:table-cell item -->
+ <f><b style="display: table-cell"></b></f>
+
+ <!-- an item after a fixed size item -->
+ <f><b></b><c></c></f>
+
+ <!-- a relatively-positioned item -->
+ <f><b style="position: relative; top: 10px; left: 10px"></b></f>
+
+ <!-- a margin-adjusted item -->
+ <f><b style="margin-top: 10px; margin-left: 10px"></b></f>
+
+ <!-- an item sized with inline styles -->
+ <f><b style="width: 200px; height: 20px"></b></f>
+
+ <!-- an item that is flexed/stretched, in both axes -->
+ <f style="align-items: stretch"><b style="flex-grow: 1; height: auto"></b></f>
+
+ <!-- These stylings should have no effect on the frameRect. -->
+ <!-- a transform:scale item -->
+ <f><b style="transform: scale(2.0)"></b></f>
+
+ <!-- a transform:translate item -->
+ <f><b style="transform: translate(10px, 10px)"></b></f>
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_items.html b/dom/flex/test/chrome/test_flex_items.html
new file mode 100644
index 0000000000..0bed577ae7
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_items.html
@@ -0,0 +1,316 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+ .container {
+ display: flex;
+ background-color: grey;
+ font: 14px sans-serif;
+ height: 50px;
+ }
+ #flex-sanity {
+ /* This just needs to be large enough so that no shrinking is required. */
+ width: 1600px;
+ }
+ .clamped-huge-item {
+ flex: 1 1 1650px; /* This needs to be bigger than #flex-sanity, to test
+ the scenario it's aiming to test (early clamping of a
+ flex item which, if left unclamped, would've reversed
+ the direction of flexing & made everything shrink). */
+ max-width: 10px;
+ }
+
+ .base { align-self: baseline; }
+ .lastbase { align-self: last baseline; }
+
+ .offset { margin-top: 10px;
+ margin-bottom: 3px; }
+
+ .lime { background: lime; }
+ .yellow { background: yellow; }
+ .orange { background: orange; }
+ .pink { background: pink; }
+ .tan { background: tan; }
+ .white { background: white; }
+
+ .crossMinMax { min-height: 40px;
+ max-height: 120px; }
+
+ .mainMinMax { min-width: 120px;
+ max-width: 500px; }
+
+ .flexGrow { flex-grow: 1; }
+ .spacer150 { width: 150px;
+ box-sizing: border-box;
+ height: 10px;
+ border: 1px solid teal; }
+
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+const TEXT_NODE = Node.TEXT_NODE;
+
+function testItemMatchesExpectedValues(item, values, index) {
+ if (typeof(values.node) != "undefined") {
+ is(item.node, values.node, "Item index " + index + " has expected node.");
+ }
+
+ if (typeof(values.mainBaseSize) != "undefined") {
+ is(item.mainBaseSize, values.mainBaseSize, "Item index " + index + " has expected mainBaseSize.");
+ }
+
+ if (typeof(values.mainDeltaSize) != "undefined") {
+ is(item.mainDeltaSize, values.mainDeltaSize, "Item index " + index + " has expected mainDeltaSize.");
+ }
+
+ if (typeof(values.mainMinSize) != "undefined") {
+ is(item.mainMinSize, values.mainMinSize, "Item index " + index + " has expected mainMinSize.");
+ }
+
+ if (typeof(values.mainMaxSize) != "undefined") {
+ is(item.mainMaxSize, values.mainMaxSize, "Item index " + index + " has expected mainMaxSize.");
+ } else {
+ // If we didn't specify an expected mainMaxSize, then it's implied
+ // that the max main-size property (max-width/max-height) is at its
+ // default "none" value, which our FlexItem API represents as +infinity.
+ is(item.mainMaxSize, Number.POSITIVE_INFINITY,
+ "Item index " + index + " has expected (default) mainMaxSize.");
+ }
+
+ if (typeof(values.crossMinSize) != "undefined") {
+ is(item.crossMinSize, values.crossMinSize, "Item index " + index + " has expected crossMinSize.");
+ }
+
+ if (typeof(values.crossMaxSize) != "undefined") {
+ is(item.crossMaxSize, values.crossMaxSize, "Item index " + index + " has expected crossMaxSize.");
+ } else {
+ // As above for mainMaxSize, no-expected-value implies we expect +infinity.
+ is(item.crossMaxSize, Number.POSITIVE_INFINITY,
+ "Item index " + index + " has expected (default) crossMaxSize.");
+ }
+}
+
+// Test for items in "flex-sanity" flex container:
+function testFlexSanity() {
+ let container = document.getElementById("flex-sanity");
+ let flex = container.getAsFlexContainer();
+ let lines = flex.getLines();
+ is(lines.length, 1, "Container should have expected number of lines.");
+
+ let line = lines[0];
+ let containerHeight = container.getBoundingClientRect().height;
+ is(line.crossSize, containerHeight,
+ "Line crossSize should equal the height of the container.");
+
+ // We can't compare baselines precisely, so we'll just confirm that they
+ // appear somewhere within the elements that determine them.
+ // (This assumes the first rect is baseline-aligned.)
+ let firstRect = container.firstElementChild.getBoundingClientRect();
+ ok(line.firstBaselineOffset > firstRect.top &&
+ line.firstBaselineOffset < firstRect.bottom,
+ "Line firstBaselineOffset should land somewhere within the element " +
+ "that determines it.");
+
+ // For last baseline, it's measured from the bottom, so we have to compare
+ // against the element bounds subtracted from the container height.
+ // We use the first node which uses last-baseline ("lb") alignment to
+ // provide the rect:
+ let lbElem = document.querySelector(".lastbase");
+ let lbElemBoundingRect = lbElem.getBoundingClientRect();
+ ok(line.lastBaselineOffset > containerHeight - lbElemBoundingRect.bottom &&
+ line.lastBaselineOffset < containerHeight - lbElemBoundingRect.top,
+ "Line lastBaselineOffset should land somewhere within the element" +
+ "that determines it.");
+
+ let expectedValues = [
+ { crossMinSize: 0 },
+ { mainBaseSize: 100,
+ mainDeltaSize: 0 },
+ { crossMinSize: 40,
+ crossMaxSize: 120,
+ mainDeltaSize: 0 },
+ { mainMinSize: 120,
+ mainMaxSize: 500,
+ mainDeltaSize: 0 },
+ { mainMinSize: 120,
+ mainMaxSize: 500,
+ mainBaseSize: 150,
+ mainDeltaSize: 0 },
+ { mainBaseSize: 10,
+ mainMaxSize: 5,
+ mainDeltaSize: 0 },
+ { mainBaseSize: 10,
+ mainMinSize: 15,
+ mainDeltaSize: 0 },
+ { mainBaseSize: 50,
+ mainMaxSize: 10 },
+ { mainBaseSize: 1650,
+ mainMaxSize: 10,
+ mainDeltaSize: 0 },
+ { mainDeltaSize: 0 },
+ { /* final item is anonymous flex item */ },
+ ];
+
+ let items = line.getItems();
+ is(items.length, expectedValues.length,
+ "Line should have expected number of items.");
+ is(items.length, container.children.length + 1,
+ "Line should have as many items as the flex container has child elems, " +
+ "plus 1 for anonymous flex item");
+
+ for (let i = 0; i < items.length; ++i) {
+ let item = items[i];
+ let values = expectedValues[i];
+ // set the expected node to the node we're currently iterating over,
+ // except for:
+ // - the display:contents element (whose item is its first child)
+ // - the final item (which is an anonymous flex item around text)
+ if (i < container.children.length) {
+ let curElem = container.children[i];
+ values.node = (curElem.style.display == "contents"
+ ? curElem.firstElementChild
+ : curElem);
+ } else {
+ is(container.lastChild.nodeType, TEXT_NODE,
+ "container's last child should be a text node");
+ values.node = container.lastChild;
+ }
+ testItemMatchesExpectedValues(item, values, i);
+ }
+
+ // Check that the delta size of the first item is (roughly) equal to the
+ // actual size minus the base size.
+ isfuzzy(items[0].mainDeltaSize, firstRect.width - items[0].mainBaseSize, 1e-4,
+ "flex-grow item should have expected mainDeltaSize.");
+}
+
+// Test for items in "flex-growing" flex container:
+function testFlexGrowing() {
+ let expectedValues = [
+ { mainBaseSize: 10,
+ mainDeltaSize: 10,
+ mainMinSize: 35 },
+ { mainBaseSize: 20,
+ mainDeltaSize: 5,
+ mainMinSize: 28 },
+ { mainBaseSize: 30,
+ mainDeltaSize: 7 },
+ { mainBaseSize: 0,
+ mainDeltaSize: 48,
+ mainMaxSize: 20 },
+ ];
+
+ let container = document.getElementById("flex-growing");
+ let items = container.getAsFlexContainer().getLines()[0].getItems();
+ is(items.length, container.children.length,
+ "Line should have as many items as the flex container has child elems");
+
+ for (let i = 0; i < items.length; ++i) {
+ let item = items[i];
+ let values = expectedValues[i];
+ testItemMatchesExpectedValues(item, values, i);
+ }
+}
+
+function runTests() {
+ testFlexSanity();
+ testFlexGrowing();
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+ <!-- First flex container to be tested: "flex-sanity".
+ We test a few general things, e.g.:
+ - accuracy of reported baselines.
+ - accuracy of reported flex base size, min/max sizes, & trivial deltas.
+ - flex items formation for display:contents subtrees & text nodes.
+ -->
+ <div id="flex-sanity" class="container">
+ <div class="lime base flexGrow">one line (first)</div>
+ <div class="yellow lastbase" style="width: 100px">one line (last)</div>
+ <div class="orange offset lastbase crossMinMax">two<br/>lines and offset (last)</div>
+ <div class="pink offset base mainMinMax">offset (first)</div>
+ <!-- Inflexible item w/ content-derived flex base size, which has min/max
+ but doesn't violate them: -->
+ <div class="tan mainMinMax">
+ <div class="spacer150"></div>
+ </div>
+ <!-- Inflexible item that is trivially clamped to smaller max-width: -->
+ <div style="flex: 0 0 10px; max-width: 5px"></div>
+ <!-- Inflexible item that is trivially clamped to larger min-width: -->
+ <div style="flex: 0 0 10px; min-width: 15px"></div>
+ <!-- Item that wants to grow but is trivially clamped to max-width
+ below base size: -->
+ <div style="flex: 1 1 50px; max-width: 10px"></div>
+ <div class="clamped-huge-item"></div>
+ <div style="display:contents">
+ <div class="white">replaced</div>
+ </div>
+ anon item for text
+ </div>
+
+ <!-- Second flex container to be tested, with items that grow by specific
+ predictable amounts. This ends up triggering 4 passes of the main
+ flexbox layout algorithm loop - and note that for each item, we only
+ report (via 'mainDeltaSize') the delta that it wanted in the *last pass
+ of the loop before that item was frozen*.
+
+ Here's what goes on in each pass (and the tentative deltas)
+ * PASS 1
+ - Available space = 120 - 10 - 20 - 30 - 0 = 60px.
+ - Sum of flex values = 1+1+2+16 = 20. So 1 "share" is 60px/20 = 3px.
+ - Deltas (space distributed): +3px, +3px, +6px, +48px
+ VIOLATIONS:
+ item0 is now 10+3 = 13px, which under its min
+ item1 is now 20+3 = 23px, which under its min
+ item3 is now 0+48 = 48px, which over its max
+ - the magnitude of max-violations (how far we're out of bounds) exceeds
+ magnitude of min-violations, so we prioritize the max-violations.
+ - So we freeze item3 at its max-width, 20px, leaving its final "desired"
+ mainDeltaSize at +48px from this pass.
+
+ * PASS 2
+ - Available space = 120 - 10 - 20 - 30 - 20 = 40px.
+ - Sum of flex values = 1+1+2 = 4. So 1 "share" is 40px/4 = 10px.
+ - Deltas (space distributed): +10px, +10px, +20px, +0px.
+ VIOLATIONS:
+ item0 is now 10+10 = 20px, which is under its min
+ - So we freeze item0 at its min-width, 35px, leaving its final "desired"
+ mainDeltaSize at +10px from this pass.
+
+ * PASS 3
+ - Available space = 120 - 35 - 20 - 30 - 20 = 15px.
+ - Sum of flex values = 1+2 = 3. So 1 "share" is 15px/3 = 5px.
+ - Deltas (space distributed): +0px, +5px, +10px, +0px.
+ VIOLATIONS:
+ item1 is now 20+5 = 25px, which is under its min
+ - So we freeze item1 at its min-width, 28px, leaving its final "desired"
+ mainDeltaSize at +5px from this pass.
+
+ * PASS 4
+ - Available space = 120 - 35 - 28 - 30 - 20 = 7px.
+ - Sum of flex values = 2. So 1 "share" is 7px/2 = 3.5px
+ - Deltas (space distributed): +0px, +0px, +7px, +0px.
+ VIOLATIONS:
+ None! (So, we'll be done after this pass!)
+ - So we freeze item2 (the only unfrozen item) at its flexed size, 37px,
+ and set its final "desired" mainDeltaSize to +7px from this pass.
+ - And we're done!
+ -->
+ <div id="flex-growing" class="container" style="width: 120px">
+ <div style="flex: 1 10px; min-width: 35px"></div>
+ <div style="flex: 1 20px; min-width: 28px"></div>
+ <div style="flex: 2 30px; min-width: 0"></div>
+ <div style="flex: 16 0px; max-width: 20px"></div>
+ </div>
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_lines.html b/dom/flex/test/chrome/test_flex_lines.html
new file mode 100644
index 0000000000..aef5a5394b
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_lines.html
@@ -0,0 +1,336 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+ f {
+ display: flex;
+ background-color: grey;
+ font: 12px sans-serif;
+ width: 800px;
+ height: 42px;
+ margin-bottom: 5px;
+ }
+
+ .withZ::after {
+ background-color: pink;
+ content: "Z";
+ width: 100px;
+ height: 10px;
+ }
+
+ .wrap {
+ flex-wrap: wrap;
+ }
+
+ .wrapReverse {
+ flex-wrap: wrap-reverse;
+ }
+
+ b {
+ background-color: gold;
+ min-width: 100px;
+ height: 20px;
+ flex-grow: 1;
+ }
+ b::after {
+ content: "B";
+ }
+
+ c {
+ background-color: yellow;
+ width: 200px;
+ height: 15px;
+ }
+ c::after {
+ content: "C";
+ }
+
+ d {
+ background-color: orange;
+ width: 300px;
+ height: 10px;
+ }
+ d::after {
+ content: "D";
+ }
+
+ e {
+ background-color: silver;
+ width: 300px;
+ height: 10px;
+ flex-shrink: 2;
+ }
+ e::after {
+ content: "E";
+ }
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function testLineMatchesExpectedValues(line, values, lineIndex, flexIndex) {
+ if (typeof(values.growthState) != "undefined") {
+ is(line.growthState, values.growthState, "Flex index " + flexIndex + " line index " + lineIndex + " has expected growthState.");
+ }
+
+ if (typeof(values.crossStart) != "undefined") {
+ is(line.crossStart, values.crossStart, "Flex index " + flexIndex + " line index " + lineIndex + " has expected crossStart.");
+ }
+
+ if (typeof(values.crossSize) != "undefined") {
+ is(line.crossSize, values.crossSize, "Flex index " + flexIndex + " line index " + lineIndex + " has expected crossSize.");
+ }
+
+ if (typeof(values.itemCount) != "undefined") {
+ is(line.getItems().length, values.itemCount, "Flex index " + flexIndex + " line index " + lineIndex + " has expected number of items.");
+ }
+}
+
+function runTests() {
+ let expectedValues = [
+ // items don't fill container, no grow, shrink, or min-width
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 2,
+ growthState: "growing" }],
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 3,
+ growthState: "growing" }],
+
+ // items don't fill container, no grow, shrink, or min-width, with wrap and align-content:center -->
+ [{ crossStart: 13.5,
+ crossSize: 15,
+ itemCount: 2,
+ growthState: "growing" }],
+ [{ crossStart: 13.5,
+ crossSize: 15,
+ itemCount: 3,
+ growthState: "growing" }],
+
+ // items don't fill container, with grow
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 3,
+ growthState: "growing" }],
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 4,
+ growthState: "growing" }],
+
+ // items overfill container, with min-width, and sometimes with wrap
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 5,
+ growthState: "shrinking" }],
+ [{ crossStart: 0,
+ crossSize: 21,
+ itemCount: 3,
+ growthState: "growing" },
+ { crossStart: 21,
+ crossSize: 21,
+ itemCount: 2,
+ growthState: "growing" }],
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 6,
+ growthState: "shrinking" }],
+ [{ crossStart: 0,
+ crossSize: 21,
+ itemCount: 3,
+ growthState: "growing" },
+ { crossStart: 21,
+ crossSize: 21,
+ itemCount: 3,
+ growthState: "growing" }],
+
+ // items overfill container, with shrink and sometimes with wrap
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 3,
+ growthState: "shrinking" }],
+ [{ crossStart: 0,
+ crossSize: 21,
+ itemCount: 2,
+ growthState: "growing" },
+ { crossStart: 21,
+ crossSize: 21,
+ itemCount: 1,
+ growthState: "growing" }],
+ [{ crossStart: 0,
+ crossSize: 42,
+ itemCount: 4,
+ growthState: "shrinking" }],
+ [{ crossStart: 0,
+ crossSize: 21,
+ itemCount: 2,
+ growthState: "growing" },
+ { crossStart: 21,
+ crossSize: 21,
+ itemCount: 2,
+ growthState: "growing" }],
+
+ // items overfill container, with wrap and different types of align-content
+ [{ crossStart: 0,
+ crossSize: 26,
+ itemCount: 3 },
+ { crossStart: 26,
+ crossSize: 16,
+ itemCount: 1 }],
+ [{ crossStart: 0,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 20,
+ crossSize: 10,
+ itemCount: 1 }],
+ [{ crossStart: 12,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 32,
+ crossSize: 10,
+ itemCount: 1 }],
+ [{ crossStart: 6,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 26,
+ crossSize: 10,
+ itemCount: 1 }],
+ [{ crossStart: 0,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 32,
+ crossSize: 10,
+ itemCount: 1 }],
+ [{ crossStart: 3,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 29,
+ crossSize: 10,
+ itemCount: 1 }],
+
+ // items overfill container, with wrap-reverse and different types of align-content
+ [{ crossStart: 0,
+ crossSize: 26,
+ itemCount: 3 },
+ { crossStart: 26,
+ crossSize: 16,
+ itemCount: 2 }],
+ [{ crossStart: 0,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 20,
+ crossSize: 10,
+ itemCount: 2 }],
+ [{ crossStart: 12,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 32,
+ crossSize: 10,
+ itemCount: 2 }],
+ [{ crossStart: 6,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 26,
+ crossSize: 10,
+ itemCount: 2 }],
+ [{ crossStart: 0,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 32,
+ crossSize: 10,
+ itemCount: 2 }],
+ [{ crossStart: 3,
+ crossSize: 20,
+ itemCount: 3 },
+ { crossStart: 29,
+ crossSize: 10,
+ itemCount: 2 }],
+
+ // other strange types of flex containers
+ [{ itemCount: 3 }],
+ [{ crossStart: 100,
+ crossSize: 300,
+ mainSize: 40,
+ itemCount: 3 },
+ { crossStart: 400,
+ crossSize: 300,
+ mainSize: 20,
+ itemCount: 2 }],
+ ];
+
+ let children = document.body.children;
+ is(children.length, expectedValues.length, "Document has expected number of flex containers.");
+
+ for (let i = 0; i < children.length; ++i) {
+ let flex = children.item(i).getAsFlexContainer();
+ ok(flex, "Document child index " + i + " is a flex container.");
+ if (flex) {
+ let values = expectedValues[i];
+
+ let lines = flex.getLines();
+ is(lines.length, values.length, "Flex index " + i + " has expected number of lines.");
+
+ for (let j = 0; j < lines.length; ++j) {
+ testLineMatchesExpectedValues(lines[j], values[j], j, i);
+ }
+ }
+ }
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+
+<!-- items don't fill container, no grow, shrink, or min-width -->
+<f><c></c><d></d></f>
+<f class="withZ"><c></c><d></d></f>
+
+<!-- items don't fill container, no grow, shrink, or min-width, with wrap and align-content:center -->
+<f class="wrap" style="align-content:center"><c></c><d></d></f>
+<f class="withZ wrap" style="align-content:center"><c></c><d></d></f>
+
+<!-- items don't fill container, with grow -->
+<f><b></b><c></c><d></d></f>
+<f class="withZ"><b></b><c></c><d></d></f>
+
+<!-- items overfill container, with min-width, and sometimes with wrap -->
+<f><b></b><d></d><d></d><d></d><b></b></f>
+<f class="wrap"><b></b><d></d><d></d><d></d><b></b></f>
+<f class="withZ"><b></b><d></d><d></d><d></d><b></b></f>
+<f class="wrap withZ"><b></b><d></d><d></d><d></d><b></b></f>
+
+<!-- items overfill container, with shrink and sometimes with wrap -->
+<f><d></d><d></d><e></e></f>
+<f class="wrap"><d></d><d></d><e></e></f>
+<f class="withZ"><d></d><d></d><e></e></f>
+<f class="wrap withZ"><d></d><d></d><e></e></f>
+
+<!-- items overfill container, with wrap and different types of align-content -->
+<f class="wrap"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:flex-start"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:flex-end"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:center"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:space-between"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:space-around"><b></b><c></c><d></d><e></e></f>
+
+<!-- items overfill container, with wrap-reverse and different types of align-content -->
+<f class="wrapReverse withZ"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:flex-start"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:flex-end"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:center"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:space-between"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:space-around"><b></b><c></c><d></d><e></e></f>
+
+<!-- other strange types of flex containers -->
+<f style="overflow:scroll"><d></d><d></d><e></e></f>
+<f class="wrap" style="flex-direction:column; align-content:center"><c></c><d></d><c></c><d></d><e></e></f>
+
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_object.html b/dom/flex/test/chrome/test_flex_object.html
new file mode 100644
index 0000000000..c984663fdf
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_object.html
@@ -0,0 +1,132 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+ margin: 40px;
+}
+.flexWrapper {
+ display: flex;
+ width: 400px;
+}
+.nonFlexWrapper {
+ display: block;
+ width: 400px;
+}
+.oh {
+ overflow: hidden;
+}
+.box {
+ background-color: #444;
+ color: #fff;
+}
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ // Test 1: elements styled with display:flex
+ let idsWithFlex = [
+ "flexDiv",
+ "flexDivOh",
+
+ "flexFieldset",
+ "flexFieldsetEmpty",
+ "flexFieldsetOh",
+
+ "flexButton",
+ "flexButtonOh",
+ ];
+
+ for (let id of idsWithFlex) {
+ let wrapper = document.getElementById(id);
+
+ // Test that we got a flex info object from the element.
+ let flex = wrapper.getAsFlexContainer();
+ ok(flex, `Expected that element id ${id} would have flex info.`);
+ }
+
+ // Test 2: elements styled without display:flex
+ let idsWithoutFlex = [
+ "boxA",
+
+ "nonFlexFieldset",
+ "nonFlexFieldsetOh",
+
+ "nonFlexButton",
+ "nonFlexButtonOh",
+ ];
+
+ for (let id of idsWithoutFlex) {
+ let wrapper = document.getElementById(id);
+
+ // Test that we didn't get a flex info object from the element.
+ let flex = wrapper.getAsFlexContainer();
+ ok(!flex, `Expected that element id ${id} would not have flex info.`);
+ }
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+ <div id="flexDiv" class="flexWrapper">
+ <div id="boxA" class="box">A</div>
+ </div>
+
+ <div id="flexDivOh" class="flexWrapper oh">
+ <div id="boxAOh" class="box">A</div>
+ </div>
+
+ <fieldset id="flexFieldset" class="flexWrapper">
+ <legend>a fieldset</legend>
+ <label for="name">name</label>
+ <input id="name">
+ </fieldset>
+
+ <fieldset id="flexFieldsetEmpty" class="flexWrapper">
+ </fieldset>
+
+ <fieldset id="flexFieldsetOh" class="flexWrapper oh">
+ <legend>a fieldset</legend>
+ <label for="nameOh">name</label>
+ <input id="nameOh">
+ </fieldset>
+
+ <button id="flexButton" class="flexWrapper">
+ <span>test</span>
+ </button>
+
+ <button id="flexButtonOh" class="flexWrapper oh">
+ <span>test</span>
+ </button>
+
+ <fieldset id="nonFlexFieldset" class="nonFlexWrapper">
+ <legend>a fieldset</legend>
+ <label for="name">name</label>
+ <input id="name">
+ </fieldset>
+
+ <fieldset id="nonFlexFieldsetOh" class="nonFlexWrapper oh">
+ <legend>a fieldset</legend>
+ <label for="name">name</label>
+ <input id="name">
+ </fieldset>
+
+ <button id="nonFlexButton" class="nonFlexWrapper">
+ <span>test</span>
+ </button>
+
+ <button id="nonFlexButtonOh" class="nonFlexWrapper oh">
+ <span>test</span>
+ </button>
+
+</body>
+</html>
diff --git a/dom/flex/test/chrome/test_flex_parent.html b/dom/flex/test/chrome/test_flex_parent.html
new file mode 100644
index 0000000000..6ab050e735
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_parent.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<html id="nonitem0">
+<head>
+<meta charset="utf-8">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+ .container {
+ display: flex;
+ background-color: grey;
+ font: 14px sans-serif;
+ height: 20px;
+ }
+
+ .lime { background: lime; }
+ .yellow { background: yellow; }
+ .orange { background: orange; }
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function testFlexParentExpectedValues(values) {
+ let expectedParent;
+ if (values.parent) {
+ expectedParent = document.getElementById(values.parent);
+ }
+
+ let item, itemLabel;
+
+ if (values.id) {
+ item = document.getElementById(values.id);
+ itemLabel = values.id;
+ } else {
+ item = expectedParent.firstChild;
+ itemLabel = "[text node]";
+ }
+
+ const actualParent = item.parentFlexElement;
+
+ if (values.parent) {
+ is(actualParent, expectedParent, "Item " + itemLabel + " should have a parent with id " + values.parent + ".");
+ } else {
+ is(actualParent, null, "Non-item " + itemLabel + " should not have a parent.");
+ }
+}
+
+function testFlexParents() {
+ let expectedValues = [
+ // These items expect to have a flex parent.
+ { id: "item0", parent: "container0" },
+ { id: "item1", parent: "container1" },
+ { id: "item2", parent: "container2" },
+ { id: "item3", parent: "container3" },
+ { id: "item4", parent: "container4" },
+ { id: null /* choose the first child of the expected parent */, parent: "container5" },
+
+
+ // These elements do NOT expect to have a flex parent.
+ { id: "nonitem0" },
+ { id: "nonitem1" },
+ { id: "nonitem2" },
+ { id: "nonitem3" },
+ ];
+
+ for (let i = 0; i < expectedValues.length; ++i) {
+ testFlexParentExpectedValues(expectedValues[i]);
+ }
+}
+
+function runTests() {
+ testFlexParents();
+ SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+ <!-- These items expect to have a flex parent. -->
+ <div id="container0" class="container">
+ <div id="item0" class="lime">first item</div>
+ </div>
+
+ <div id="container1" class="container">
+ <div class="orange">first item</div>
+ <div id="item1" class="lime">second item</div>
+ </div>
+
+ <div id="container2" class="container">
+ <div style="display:contents">
+ <div id="item2" class="lime">display-contents-child item</div>
+ </div>
+ </div>
+
+ <div id="container3" class="container">
+ A <span id="item3" class="lime">middle item</span> surrounded by anonymous text items</div>
+ </div>
+
+ <div id="container4" class="container">
+ <div id="item4" style="display: table-cell">display: table-cell item</div>
+ </div>
+
+ <div id="container5" class="container">
+ Text that gets wrapped in anonymous flex item
+ </div>
+
+ <!-- These elements do NOT expect to have a flex parent. -->
+
+ <!-- nonitem0 is the root html element -->
+
+ <div class="container">
+ <div>
+ <div id="nonitem1" class="yellow">child element of an item</div>
+ </div>
+ </div>
+
+ <div class="container">
+ <div>
+ <div id="nonitem2" style="position: absolute" class="yellow">position: absolute element</div>
+ </div>
+ </div>
+
+ <div class="container" style="position:relative">
+ <div>
+ <div id="nonitem3" style="position: absolute" class="yellow">
+ position: absolute element, with flex as containing block
+ </div>
+ </div>
+ </div>
+</body>
+</html>