From b3541682d04ab9c23791de53c8449a6b58e77ca9 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Mon, 21 Dec 2020 14:13:41 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - bug 1157569 part 1 - Rename BC_BORDER_{TOP,RIGHT,BOTTOM,LEFT}_HALF* to BC_BORDER_{START,END}_HALF*. r=roc (2ef82ce76) - Bug 1157569 part 2 - Convert output parameters of GetColorAndStyle/GetPaintStyleInfo in nsTableFrame to use pointers. r=roc (c58bbe734) - Bug 1157569 part 3 - Merge two GetColorAndStyle functions in nsTableFrame. r=roc (009df4d97) - Bug 1157569 part 4 - Replace mTableIsLTR with mTableWM in BCMapCellInfo & BCPaintBorderIterator. r=roc (9784a46eb) - Bug 1157569 part 5 - Convert GetColorAndStyle, GetPaintStyleInfo, and CompareBorders in nsTableFrame to accept writing mode and logical side. r=roc (aaddc4a80) - Bug 1157569 part 6 - Rename methods and fields in BCMapCellInfo from physicals to logicals. r=roc (aeaa0b18c) - Bug 1157569 part 7 - Rename methods and fields in nsTableColFrame from physicals to logicals. r=roc (fd4e1e667) - Bug 1157569 part 8 - Move some code in BCPaintBorderIterator::SetDamageArea for less computation. r=roc (634f19ca7) - missing bit of Bug 895096 - Part 1 (a96e3d35c) - Bug 1157569 part 9 - Rename methods and fields in nsTableRowFrame from physicals to logicals. r=roc (d097f1e4f) - Bug 1157569 part 10 - Add operator+= for LogicalMargin. r=jfkthame (c651b685f) - Bug 1159101 part 4 - Override GetWritingMode() in nsTable{{Col,Row}{,Group},Cell}Frame. r=roc (34d90d10b) - Bug 1159101 part 5 - Initialize writing mode of desired mode of table cell frame from its table frame on a CLOSED TREE. r=roc (8bc6978ca) - Bug 1157569 part 11 - Rename methods and fields in nsTableCellFrame from physicals to logicals. r=roc (bcfd4514b) - Bug 1157569 part 12 - Remove useless m{Start,End}Side from BCMapCellInfo. r=roc (1df59d670) - Bug 1159127 - Always redirect inserting col group frame to first-in-flow table frame. r=mats (2e41bbeeb) - Bug 1167765: Misc whitespace cleanup in nsTableFrame.cpp. whitespace/rewrapping-only, no review, DONTBUILD (f256939c9) - Bug 1167696 part 1: Remove nsTableReflowState's unused constructor & nsPresContext arg. r=mats (5b8beadb9) - Bug 1167696 part 2: Remove nsTableReflowState's frame constructor-arg; get it from passed-in reflow state instead. r=mats (205dad74f) - Bug 1155412: Use mozilla::Maybe instead of hardcoded placement-new, for reflow state created in nsTableOuterFrame::OuterBeginReflowChild() and used by its caller. r=dbaron (ebf9e9891) - Bug 1147834: Use abstract coordinates in nsHTMLReflowState, r=jfkthame (30e644a14) - Bug 1169432 part 2: Refactor nsTableFrame::Init. r=mats (779928597) - Bug 1169432 part 3: Use nsTArray::Contains instead of nsTArray::IndexOf(), for brevity, in nsTableFrame. r=mats (aa6aee3c4) - Bug 1169432 part 4: Convert NS_ASSERTION(false, ...) to NS_ERROR(...) in nsTableFrame. r=mats (9cbb2f53f) - Bug 1169432 part 5: Use range-based 'for' loops when walking frame lists, in nsTableFrame. r=mats (bd4e7d0d4) - Bug 1171328 - Convert nsTableFrame::GetChildAreaOffset() and its friends to use LogicalMargin. r=dholbert (196955d0c) - Remove unneeded code from SmoothScrollAnimation. (bug 1139220 part 4, r=kats) (68c6af9f8) - Use stronger typing for GetScrollWheelDelta. (bug 1139220 part 5, r=kats) (4669d1f38) - When APZ is enabled, only apply the root scroll delta override to root frames. (bug 1155800, r=kats) (2a738ee82) - Bug 1168629. Clear frame metrics on the root layer if we aren't setting new frame metrics because the layer may be recycled and still have frame metrics from last time. r=mstange (dc0889fae) - Bug 1158424 - Rename FrameMetrics::mIsRoot to mIsRootContent. r=kats (0005fb016) - Bug 1148078: Replace the word "Logical" with "Flex-Relative" in some vars & functions in flexbox layout. r=mats (601497c55) - Bug 1148298 part 1: Convert most flexbox-layout IsAxisHorizontal() calls into IsMainAxisHorizontal()/IsCrossAxisHorizontal(). r=mats (ff2aaa5b3) - Bug 1148298 part 3: Make flexbox helper GetBaselineOffsetFromOuterCrossEdge take a FlexboxAxisTracker instead of an explicit axis. r=mats (009dd9f7e) - Bug 1148298 part 4: Convert flexbox helper-function IsAxisHorizontal() to be a private implementation detail of FlexboxAxisTracker. r=mats (901f830d6) - Bug 1148298 part 2: Replace flexbox static helper-function GetSizeProBug 1148298 part 2: Replace flexbox static helper-function GetSizePro (3d2940f88) - Bug 1158290 part 1: Use AvailableBSize instead of AvailableHeight, throughout flex layout. r=mats (49e2075f8) - Bug 1148294 part 1: Determine flex axes (in FlexboxAxisTracker) using the flex container's writing mode. r=mats (67f12cf58) - Bug 1148294 part 2: Add one reftest for writing-mode & direction properties' influence on flex axes. (85e0ede9d) - Bug 1148294 part 3: Add more reftests for writing-mode & direction properties' influence on flex axes (as modified copies of first test). (fac965a24) - Bug 1148294 followup: fix spec links in reftests flexbox-writing-mode-* to point to TR instead of ED spec version. (no review) (84333c728) - Bug 1158290 part 2: Use logical (not physical) skipsides & borderpadding when chipping away from available BSize. r=mats (987b4edfa) - Bug 1152913 part 1: Make FlexboxAxisTracker store the WritingMode & representation of how flex axes map to it. r=mats (675737981) - Bug 1152951: Delete the copy-constructor and reassignment operator for FlexboxAxisTracker & PositionTracker state-management classes. r=mats (71c88e7e9) - Bug 1152913 part 2: Convert flexbox 'IsAxisHorizontal' checks to use new logical-axis member data. r=mats (122b434a5) - Bug 1158290 part 3: Convert GenerateFlexLines to use logical axes & sizes. r=mats (367cb1c01) - Bug 1158290 part 4: Clarify documentation & naming of ClampFlexContainerMainSize (now called ResolveFlexContainerMainSize). r=mats (5af11ff23) - Bug 1149383 part 1: Make flexbox writing-mode tests use different widths vs. heights to reveal bug with vertical writing-modes. (test-only) (c4782feaa) - Bug 1149383 part 2: Reorder width/height decls in flexbox-writing-mode reftests for consistency. (test-only) (bba907b70) - Bug 1158290 part 5: Use logical coords & axes in methods that computeBug 1158290 part 5: Use logical coords & axes in methods that compute (b78726528) - Bug 1158290 part 6: Use new GET_[MAIN|CROSS]_COMPONENT_LOGICAL macros, to reduce explicit width/height usage. r=mats (25d2e20a3) - Bug 1152913 part 3: Make flexbox's PositionTracker classes track whether axis is reversed, & directly convert flex item final sizes/positions to logical coords. r=mats (1feee7e6d) - Bug 1174507 - Replace the frame flag CONTAINS_RELATIVE_HEIGHT with CONTAINS_RELATIVE_BSIZE, and adjust callsites appropriately. r=dholbert (0e981d5ac) - Clip async scrollframes based on the displaylist clip, not the composition bounds. (bug 1148582 part 4, r=tn) (f2b1196ed) - Bug 1148582 - Add mask layers to FrameMetrics for ancestor scroll frame clips. r=mattwoodrow (008239305) - Bug 1148582 - Factor mask layer creation out of ContainerState::SetupMaskLayer. r=mstange (1d739614e) - piece of Bug 1138442 (e98de18dd) - Bug 1173307 - Convert nsTableCellFrame to work with logical coordinates. r=dholbert (81d0aa811) - Bug 1174700 - patch 1 - Convert nsTableRowFrame and nsTableRowGroupFrame to work with logical coordinates. r=dholbert (09ee9c814) - Bug 1174700 - patch 2 - Convert nsTableFrame::SetColumnDimensions to work with logical-order iteration over the table, so that column coordinates and collapsed borders are correct. r=dholbert (7957e377b) - Bug 1129040 - Provide a way for content processes to query the chrome side blocklist service. r=billm (5ea750b8b) - Bug 1174711 - patch 1 - Rename nsIPercentHeightObserver to nsIPercentBSizeObserver, and update related frame methods to match. r=dholbert (7a17fbe4f) - Bug 1174711 - patch 2 - Convert nsTableFrame to work with logical coordinates. r=dholbert (329776abe) - Bug 1174711 - patch 3 - Rename a couple more frame-state bits from physical to logical. r=dholbert (d61c9624d) - Bug 1174711 - patch 4 - Rename mSpecialHeightReflow to mSpecialBSizeReflow, and update comments to match. r=dholbert (49969e512) - Bug 1174711 patch 5 - Copy inline-size rather than width from prev-in-flow when initializing nsTableFrame. r=dholbert (7afb6c80a) - pointer style (52cd6f9af) - pointer style (ba99b3f74) - Bug 1149797 - Use the loop's *static* block object when freshening a loop's block object, then copy in values from the old cloned block. Using the old cloned block directly isn't valid when the cloned block might be extended with additional variables created by eval or added by nested function statements. r=shu (deba22790) - Bug 1165486 - Cleanup: use standard object allocation functions when allocating scope objects. (r=terrence) (b35a4d7aa) - pointer style (74ea6d085) - Bug 1165486 - Add StaticNonSyntacticScopeObjects and teach scope iterators about it. (r=luke) (18a526ed3) - Bug 1144371: Implement DEBUG-only JS shell function |dumpStringRepresentation|. r=jandem (35524cf42) - Bug 1148963 - OdinMonkey: add CompileOptions::lazyParsingDisabled and testing function setDiscardSource (r=bz) (53f9c8fe6) - pointer style (edaa05d69) - Bug 1165486 - Rename hasPollutedGlobalScope to hasNonSyntacticScope. (r=luke) (505fc8d05) - pointer style (3c4435038) - Bug 1165486 - Remove PollutedGlobalScopeOption in favor of using the static scope chain to detect non-syntactic scopes. (r=luke) (73ca126f5) - Bug 1150106 - Async loadSubscript - Part 1, add `async` to loadSubscriptWithOptions r=bholley,mcrr8 (a0ffeed0d) - Bug 1150106 - Add an async version of loadSubscript - Part 2, tests r=bholley (6aeaa93e4) - Bug 1165486 - Use JS::CompileForNonSyntacticScope in Gecko where we used to set polluted global scope. (r=bz, rs=bholley) (073332ac9) - Bug 1165486 - Split JS::Compile into JS::Compile and JS::CompileForNonSyntacticScope. (r=luke) (0b82c73c4) - Bug 1165486 - Restructure function and script cloning in light of PollutingGlobal scope changes. (r=Waldo) (b4e39652c) - Bug 1141865 - Part 7: Make new.target work in generator functions. (r=jorendorff, r=jandem) (a9f5ce1ad) - Bug 1165486 - Detect with scopes at parse time using the static scope chain for non-function scripts. Also cache static scope properties on SharedGlobalContext. (r=efaust) (a7d4283cb) - Bug 1176070 - Optimize nsTableFrame's FirstInFlow() lookups from calls to GetColumnISize(). r=dholbert (beed47b93) - fix misspatch of Bug 1141862 - Part 6 (d091f70a2) - Bug 1169736 - Temporarily disallow eval and arrow functions inside derived class constructors. (r=jorendorff) (6cf8a2db1) - Bug 1150855 - Disallow method syntax without curly brackets. r=efaust (26086ee95) - part of Bug 1150855 - Remove uses of the curly syntax. (bfe86ea19) - Bug 1169853 - Require semicolon after export default AssignmentExpression. r=shu (580ff7e30) - Bug 1174009 - Remove a couple of unused AutoRooter types; r=sfink (113aee16a) --- dom/apps/UserCustomizations.jsm | 7 +- dom/apps/tests/addons/script.js | 2 +- dom/apps/tests/addons/script2.js | 2 +- dom/base/nsFrameMessageManager.cpp | 3 +- dom/ipc/ContentParent.cpp | 22 + dom/ipc/ContentParent.h | 1 + dom/ipc/PBrowser.ipdl | 1 - dom/ipc/PContent.ipdl | 5 + dom/ipc/TabChild.cpp | 11 +- dom/ipc/TabChild.h | 3 - dom/ipc/TabParent.cpp | 18 - dom/ipc/TabParent.h | 4 - dom/plugins/base/nsPluginHost.cpp | 3 +- dom/plugins/base/nsPluginHost.h | 6 +- dom/plugins/base/nsPluginTags.cpp | 41 +- gfx/ipc/GfxMessageUtils.h | 6 +- gfx/layers/FrameMetrics.h | 29 +- gfx/layers/LayerTreeInvalidation.cpp | 35 +- gfx/layers/Layers.cpp | 11 +- gfx/layers/Layers.h | 26 + gfx/layers/ReadbackProcessor.cpp | 4 +- .../apz/public/GeckoContentController.h | 14 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 97 +- gfx/layers/apz/src/AsyncPanZoomController.h | 4 +- gfx/layers/apz/util/APZEventState.cpp | 11 - gfx/layers/apz/util/APZEventState.h | 4 - .../apz/util/ChromeProcessController.cpp | 15 - gfx/layers/apz/util/ChromeProcessController.h | 4 +- gfx/layers/client/ClientCanvasLayer.cpp | 4 +- gfx/layers/client/ClientColorLayer.cpp | 4 +- gfx/layers/client/ClientContainerLayer.h | 4 +- gfx/layers/client/ClientImageLayer.cpp | 4 +- gfx/layers/client/ClientLayerManager.h | 10 + gfx/layers/client/ClientPaintedLayer.cpp | 4 +- gfx/layers/client/ClientTiledPaintedLayer.cpp | 6 +- .../composite/LayerManagerComposite.cpp | 2 +- gfx/layers/ipc/CompositorParent.cpp | 3 + gfx/layers/ipc/LayerTransactionParent.cpp | 7 + gfx/layers/ipc/LayersMessages.ipdlh | 1 + gfx/layers/ipc/ShadowLayers.cpp | 4 + .../gtest/TestAsyncPanZoomController.cpp | 18 +- js/src/builtin/Eval.cpp | 34 +- js/src/builtin/TestingFunctions.cpp | 57 +- js/src/frontend/BytecodeCompiler.cpp | 10 +- js/src/frontend/BytecodeEmitter.cpp | 37 +- js/src/frontend/FullParseHandler.h | 4 + js/src/frontend/ParseNode.h | 1 + js/src/frontend/Parser.cpp | 65 +- js/src/frontend/Parser.h | 8 +- js/src/frontend/SharedContext.h | 77 +- js/src/frontend/SyntaxParseHandler.h | 1 + js/src/gc/RootMarking.cpp | 24 - .../jit-test/tests/asm.js/testBug1147144.js | 2 +- .../tests/basic/dumpStringRepresentation.js | 70 + js/src/jit-test/tests/basic/eval-scopes.js | 16 +- js/src/jit-test/tests/basic/function-gname.js | 8 +- .../tests/modules/export-declaration.js | 4 + js/src/jit/BaselineBailouts.cpp | 11 +- js/src/jit/BaselineCompiler.cpp | 28 +- js/src/jit/BaselineIC.cpp | 4 +- js/src/jit/BytecodeAnalysis.cpp | 2 +- js/src/jit/CodeGenerator.cpp | 4 +- js/src/jit/Ion.cpp | 8 +- js/src/jit/IonBuilder.cpp | 18 +- js/src/jit/JitFrames.cpp | 2 +- js/src/js.msg | 1 + js/src/jsapi.cpp | 260 ++- js/src/jsapi.h | 46 +- js/src/jscntxt.h | 26 - js/src/jsfun.cpp | 255 ++- js/src/jsfun.h | 22 +- js/src/jsfuninlines.h | 7 +- js/src/jsobj.cpp | 10 +- js/src/jsobj.h | 4 +- js/src/jsobjinlines.h | 23 +- js/src/jspubtd.h | 2 - js/src/jsscript.cpp | 312 ++- js/src/jsscript.h | 74 +- .../Class/derivedConstructorDisabled.js | 37 + js/src/tests/ecma_6/Class/methDefn.js | 3 + .../tests/ecma_6/Class/newTargetGenerators.js | 24 + ...for-loop-with-bindings-added-at-runtime.js | 125 ++ ...claration-and-nested-function-statement.js | 126 ++ js/src/tests/js1_8_5/reflect-parse/classes.js | 18 +- .../tests/js1_8_5/reflect-parse/methodDefn.js | 5 + .../tests/js1_8_5/reflect-parse/newTarget.js | 5 +- js/src/vm/Debugger.cpp | 23 +- js/src/vm/GeneratorObject.cpp | 5 +- js/src/vm/GeneratorObject.h | 16 + js/src/vm/Interpreter-inl.h | 4 +- js/src/vm/Interpreter.cpp | 15 +- js/src/vm/Opcodes.h | 12 +- js/src/vm/ScopeObject-inl.h | 33 +- js/src/vm/ScopeObject.cpp | 246 +-- js/src/vm/ScopeObject.h | 168 +- js/src/vm/SelfHosting.cpp | 21 +- js/src/vm/Stack-inl.h | 19 +- js/src/vm/Stack.cpp | 19 +- js/src/vm/Stack.h | 18 +- js/src/vm/String.cpp | 112 + js/src/vm/String.h | 30 + js/src/vm/Xdr.h | 4 +- js/xpconnect/loader/mozJSSubScriptLoader.cpp | 432 +++- js/xpconnect/loader/mozJSSubScriptLoader.h | 5 + .../tests/unit/bug451678_subscript.js | 3 + js/xpconnect/tests/unit/test_bug1150106.js | 35 + js/xpconnect/tests/unit/xpcshell.ini | 3 +- layout/base/FrameLayerBuilder.cpp | 82 +- layout/base/RestyleManager.cpp | 11 +- layout/base/moz.build | 2 +- layout/base/nsCSSFrameConstructor.cpp | 28 +- layout/base/nsDisplayList.cpp | 7 +- ...htObserver.h => nsIPercentBSizeObserver.h} | 21 +- layout/base/nsLayoutUtils.cpp | 4 +- layout/base/nsPresShell.cpp | 2 +- layout/forms/nsFieldSetFrame.cpp | 4 +- layout/forms/nsTextControlFrame.cpp | 4 +- layout/generic/WritingModes.h | 7 + layout/generic/nsAbsoluteContainingBlock.cpp | 21 +- layout/generic/nsBlockFrame.cpp | 2 +- layout/generic/nsBlockReflowState.cpp | 4 +- layout/generic/nsCanvasFrame.cpp | 8 +- layout/generic/nsColumnSetFrame.cpp | 16 +- layout/generic/nsFlexContainerFrame.cpp | 805 ++++--- layout/generic/nsFlexContainerFrame.h | 6 +- layout/generic/nsFrame.cpp | 37 +- layout/generic/nsFrame.h | 9 +- layout/generic/nsFrameIdList.h | 2 +- layout/generic/nsFrameSetFrame.cpp | 2 +- layout/generic/nsFrameStateBits.h | 14 +- layout/generic/nsGfxScrollFrame.cpp | 57 +- layout/generic/nsGfxScrollFrame.h | 29 +- layout/generic/nsHTMLParts.h | 2 +- layout/generic/nsHTMLReflowState.cpp | 996 ++++----- layout/generic/nsHTMLReflowState.h | 170 +- layout/generic/nsIScrollableFrame.h | 18 +- layout/generic/nsVideoFrame.cpp | 10 +- layout/generic/nsViewportFrame.cpp | 6 +- layout/ipc/RenderFrameParent.cpp | 25 +- layout/mathml/nsMathMLParts.h | 4 +- layout/mathml/nsMathMLmtableFrame.cpp | 12 +- layout/mathml/nsMathMLmtableFrame.h | 8 +- .../flexbox/flexbox-writing-mode-001-ref.html | 76 + .../flexbox/flexbox-writing-mode-001.html | 78 + .../flexbox/flexbox-writing-mode-002-ref.html | 76 + .../flexbox/flexbox-writing-mode-002.html | 78 + .../flexbox/flexbox-writing-mode-003-ref.html | 76 + .../flexbox/flexbox-writing-mode-003.html | 78 + .../flexbox/flexbox-writing-mode-004-ref.html | 76 + .../flexbox/flexbox-writing-mode-004.html | 78 + .../flexbox/flexbox-writing-mode-005-ref.html | 76 + .../flexbox/flexbox-writing-mode-005.html | 78 + .../flexbox/flexbox-writing-mode-006-ref.html | 76 + .../flexbox/flexbox-writing-mode-006.html | 78 + .../w3c-css/submitted/flexbox/reftest.list | 8 + .../w3c-css/submitted/ui3/reftest.list | 7 +- .../ua-style-sheet-size-1-ref.html | 2 + .../writing-mode/ua-style-sheet-size-1.html | 2 + .../ua-style-sheet-size-2-ref.html | 2 + .../writing-mode/ua-style-sheet-size-2.html | 2 + layout/tables/celldata.h | 24 +- layout/tables/nsTableCellFrame.cpp | 331 +-- layout/tables/nsTableCellFrame.h | 94 +- layout/tables/nsTableColFrame.cpp | 16 +- layout/tables/nsTableColFrame.h | 63 +- layout/tables/nsTableColGroupFrame.cpp | 8 +- layout/tables/nsTableColGroupFrame.h | 6 + layout/tables/nsTableFrame.cpp | 1946 +++++++++-------- layout/tables/nsTableFrame.h | 75 +- layout/tables/nsTableOuterFrame.cpp | 172 +- layout/tables/nsTableOuterFrame.h | 11 +- layout/tables/nsTablePainter.cpp | 36 +- layout/tables/nsTableRowFrame.cpp | 817 +++---- layout/tables/nsTableRowFrame.h | 258 ++- layout/tables/nsTableRowGroupFrame.cpp | 546 ++--- layout/tables/nsTableRowGroupFrame.h | 110 +- .../components/places/PlacesTransactions.jsm | 56 +- toolkit/components/places/PlacesUtils.jsm | 5 +- 178 files changed, 7124 insertions(+), 4395 deletions(-) create mode 100644 js/src/jit-test/tests/basic/dumpStringRepresentation.js create mode 100644 js/src/tests/ecma_6/Class/derivedConstructorDisabled.js create mode 100644 js/src/tests/ecma_6/Class/newTargetGenerators.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/for-loop-with-bindings-added-at-runtime.js create mode 100644 js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js create mode 100644 js/xpconnect/tests/unit/test_bug1150106.js rename layout/base/{nsIPercentHeightObserver.h => nsIPercentBSizeObserver.h} (58%) create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006-ref.html create mode 100644 layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006.html diff --git a/dom/apps/UserCustomizations.jsm b/dom/apps/UserCustomizations.jsm index 64d9938ba4..aafa7223f4 100644 --- a/dom/apps/UserCustomizations.jsm +++ b/dom/apps/UserCustomizations.jsm @@ -238,7 +238,12 @@ this.UserCustomizations = { } try { - Services.scriptloader.loadSubScript(aScript, sandbox, "UTF-8"); + let options = { + target: sandbox, + charset: "UTF-8", + async: true + } + Services.scriptloader.loadSubScriptWithOptions(aScript, options); if (!this._loaded[manifestURL]) { this._loaded[manifestURL] = { css: [], scripts: [] }; } diff --git a/dom/apps/tests/addons/script.js b/dom/apps/tests/addons/script.js index aac734710f..608f9b6686 100644 --- a/dom/apps/tests/addons/script.js +++ b/dom/apps/tests/addons/script.js @@ -1,4 +1,4 @@ document.addEventListener("DOMContentLoaded", function() { var head = document.getElementById("header"); head.innerHTML = "Hello World!"; -}, false); \ No newline at end of file +}, false); diff --git a/dom/apps/tests/addons/script2.js b/dom/apps/tests/addons/script2.js index a116f46d35..bba0d37921 100644 --- a/dom/apps/tests/addons/script2.js +++ b/dom/apps/tests/addons/script2.js @@ -1,4 +1,4 @@ document.addEventListener("DOMContentLoaded", function() { var head = document.getElementById("header2"); head.innerHTML = "Customized content"; -}, false); \ No newline at end of file +}, false); diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index d82980ffbe..0351ba9ccb 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1774,8 +1774,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( } } else { // We're going to run these against some non-global scope. - options.setHasPollutedScope(true); - if (!JS::Compile(cx, options, srcBuf, &script)) { + if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) { return; } } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 565dbfb8fa..5fe2287885 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -161,6 +161,9 @@ #include "private/pprio.h" #include "ContentProcessManager.h" #include "mozilla/psm/PSMContentListener.h" +#include "nsPluginHost.h" +#include "nsPluginTags.h" +#include "nsIBlocklistService.h" #include "nsIBidiKeyboard.h" @@ -1091,6 +1094,25 @@ ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy); } +bool +ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId, + uint32_t* aState) +{ + *aState = nsIBlocklistService::STATE_BLOCKED; + + nsRefPtr pluginHost = nsPluginHost::GetInst(); + if (!pluginHost) { + return false; + } + nsPluginTag* tag = pluginHost->PluginWithId(aPluginId); + + if (!tag) { + return false; + } + + return NS_SUCCEEDED(tag->GetBlocklistState(aState)); +} + bool ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, nsTArray* aPlugins, diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index c6e6d629bb..7d9a4770f7 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -177,6 +177,7 @@ public: virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) override; virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override; + virtual bool RecvGetBlocklistState(const uint32_t& aPluginId, uint32_t* aIsBlocklisted) override; virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, nsTArray* aPlugins, uint32_t* aNewPluginEpoch) override; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index a0c87dcd4d..01a624ef1e 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -551,7 +551,6 @@ child: HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid); HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid); HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); - HandleLongTapUp(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid); NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index d04a72c04f..4f7680d339 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -707,6 +707,11 @@ parent: */ sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv); + /** + * Return the current blocklist state for a particular plugin. + */ + sync GetBlocklistState(uint32_t aPluginId) returns (uint32_t aState); + /** * This call returns the set of plugins loaded in the chrome * process. However, in many cases this set will not have changed since the diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 5687cddf05..9b678a2c3c 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -501,7 +501,7 @@ TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) { MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID); - if (aFrameMetrics.GetIsRoot()) { + if (aFrameMetrics.IsRootContent()) { if (nsCOMPtr shell = GetPresShell()) { // Guard against stale updates (updates meant for a pres shell which // has since been torn down and destroyed). @@ -2128,15 +2128,6 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, return true; } -bool -TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid) -{ - if (mGlobal && mTabChildGlobal) { - mAPZEventState->ProcessLongTapUp(aPoint, aModifiers, aGuid, GetPresShellResolution()); - } - return true; -} - bool TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId, const APZStateChange& aChange, diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 4a141e44fc..ebc376ba79 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -339,9 +339,6 @@ public: const Modifiers& aModifiers, const mozilla::layers::ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) override; - virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint, - const Modifiers& aModifiers, - const mozilla::layers::ScrollableLayerGuid& aGuid) override; virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId, const APZStateChange& aChange, const int& aArg) override; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index f71239c6d1..cdbbe92316 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1050,15 +1050,6 @@ void TabParent::HandleLongTap(const CSSPoint& aPoint, } } -void TabParent::HandleLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid &aGuid) -{ - if (!mIsDestroyed) { - unused << SendHandleLongTapUp(aPoint, aModifiers, aGuid); - } -} - void TabParent::NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg) @@ -1301,15 +1292,6 @@ bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModi return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid, aInputBlockId); } -bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid) -{ - if (mIsDestroyed) { - return false; - } - - return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint), aModifiers, aGuid); -} - bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid) { if (mIsDestroyed) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 5b3927a385..3e3f73a3b1 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -260,9 +260,6 @@ public: Modifiers aModifiers, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId); - void HandleLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid& aGuid); void NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg); @@ -323,7 +320,6 @@ public: bool SendRealTouchEvent(WidgetTouchEvent& event); bool SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid); bool SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId); - bool SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid); bool SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid); virtual PDocumentRendererParent* diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 7565278ee6..8655ad03dd 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -106,6 +106,8 @@ #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #endif +#include "npapi.h" + using namespace mozilla; using mozilla::TimeStamp; using mozilla::plugins::PluginTag; @@ -1044,7 +1046,6 @@ nsPluginHost::GetBlocklistStateForType(const nsACString &aMimeType, aExcludeFlags, getter_AddRefs(tag)); NS_ENSURE_SUCCESS(rv, rv); - return tag->GetBlocklistState(aState); } diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h index 9fddf5aae7..09e761ad6b 100644 --- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -11,7 +11,6 @@ #include "nsCOMPtr.h" #include "prlink.h" #include "prclist.h" -#include "npapi.h" #include "nsIPluginTag.h" #include "nsPluginsDir.h" #include "nsPluginDirServiceProvider.h" @@ -30,6 +29,7 @@ #include "nsCRT.h" #ifdef XP_WIN +#include #include "nsIWindowsRegKey.h" #endif @@ -52,6 +52,10 @@ class nsNPAPIPluginStreamListener; class nsIPluginInstanceOwner; class nsIInputStream; class nsIStreamListener; +#ifndef npapi_h_ +struct _NPP; +typedef _NPP* NPP; +#endif class nsInvalidPluginTag : public nsISupports { diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index 759b5393cd..0878269a7e 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -20,6 +20,7 @@ #include "mozilla/unused.h" #include #include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/ContentChild.h" using mozilla::dom::EncodingUtils; using namespace mozilla; @@ -650,27 +651,33 @@ nsPluginTag::GetBlocklistState(uint32_t *aResult) return NS_OK; } - nsCOMPtr blocklist = - do_GetService("@mozilla.org/extensions/blocklist;1"); + if (XRE_GetProcessType() != GeckoProcessType_Default) { + *aResult = nsIBlocklistService::STATE_BLOCKED; + dom::ContentChild* cp = dom::ContentChild::GetSingleton(); + if (!cp->SendGetBlocklistState(mId, aResult)) { + return NS_OK; + } + } else { + nsCOMPtr blocklist = + do_GetService("@mozilla.org/extensions/blocklist;1"); - if (!blocklist) { - *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; - return NS_OK; + if (!blocklist) { + *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; + return NS_OK; + } + + // The EmptyString()s are so we use the currently running application + // and toolkit versions + if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(), + EmptyString(), aResult))) { + *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; + return NS_OK; + } } - // The EmptyString()s are so we use the currently running application - // and toolkit versions - uint32_t state; - if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(), - EmptyString(), &state))) { - *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; - return NS_OK; - } - - MOZ_ASSERT(state <= UINT16_MAX); - mCachedBlocklistState = (uint16_t) state; + MOZ_ASSERT(*aResult <= UINT16_MAX); + mCachedBlocklistState = (uint16_t) *aResult; mCachedBlocklistStateValid = true; - *aResult = state; return NS_OK; } diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 165751f488..cfcb21a26d 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -721,7 +721,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mZoom); WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); WriteParam(aMsg, aParam.mPresShellId); - WriteParam(aMsg, aParam.mIsRoot); + WriteParam(aMsg, aParam.mIsRootContent); WriteParam(aMsg, aParam.mHasScrollgrab); WriteParam(aMsg, aParam.mUpdateScrollOffset); WriteParam(aMsg, aParam.mScrollGeneration); @@ -733,6 +733,7 @@ struct ParamTraits WriteParam(aMsg, aParam.GetPageScrollAmount()); WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel()); WriteParam(aMsg, aParam.mClipRect); + WriteParam(aMsg, aParam.mMaskLayerIndex); WriteParam(aMsg, aParam.mIsLayersIdRoot); WriteParam(aMsg, aParam.mUsesContainerScrolling); WriteParam(aMsg, aParam.GetContentDescription()); @@ -766,7 +767,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mZoom) && ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && ReadParam(aMsg, aIter, &aResult->mPresShellId) && - ReadParam(aMsg, aIter, &aResult->mIsRoot) && + ReadParam(aMsg, aIter, &aResult->mIsRootContent) && ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) && ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) && ReadParam(aMsg, aIter, &aResult->mScrollGeneration) && @@ -778,6 +779,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) && ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) && ReadParam(aMsg, aIter, &aResult->mClipRect) && + ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) && ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) && ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) && ReadContentDescription(aMsg, aIter, aResult)); diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 28351e036f..68c95e2ea8 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -48,7 +48,7 @@ public: , mScrollableRect(0, 0, 0, 0) , mCumulativeResolution() , mDevPixelsPerCSSPixel(1) - , mIsRoot(false) + , mIsRootContent(false) , mHasScrollgrab(false) , mScrollId(NULL_SCROLL_ID) , mScrollParentId(NULL_SCROLL_ID) @@ -89,7 +89,7 @@ public: mCumulativeResolution == aOther.mCumulativeResolution && mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel && mPresShellId == aOther.mPresShellId && - mIsRoot == aOther.mIsRoot && + mIsRootContent == aOther.mIsRootContent && mScrollId == aOther.mScrollId && mScrollParentId == aOther.mScrollParentId && mScrollOffset == aOther.mScrollOffset && @@ -104,6 +104,7 @@ public: mPageScrollAmount == aOther.mPageScrollAmount && mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel && mClipRect == aOther.mClipRect && + mMaskLayerIndex == aOther.mMaskLayerIndex && mIsLayersIdRoot == aOther.mIsLayersIdRoot && mUsesContainerScrolling == aOther.mUsesContainerScrolling; } @@ -122,7 +123,7 @@ public: bool IsRootScrollable() const { - return mIsRoot; + return mIsRootContent; } bool IsScrollable() const @@ -307,14 +308,14 @@ public: return mDevPixelsPerCSSPixel; } - void SetIsRoot(bool aIsRoot) + void SetIsRootContent(bool aIsRootContent) { - mIsRoot = aIsRoot; + mIsRootContent = aIsRootContent; } - bool GetIsRoot() const + bool IsRootContent() const { - return mIsRoot; + return mIsRootContent; } void SetHasScrollgrab(bool aHasScrollgrab) @@ -539,6 +540,13 @@ public: return mClipRect.ref(); } + void SetMaskLayerIndex(const Maybe& aIndex) { + mMaskLayerIndex = aIndex; + } + const Maybe& GetMaskLayerIndex() const { + return mMaskLayerIndex; + } + void SetIsLayersIdRoot(bool aValue) { mIsLayersIdRoot = aValue; } @@ -635,7 +643,7 @@ private: CSSToLayoutDeviceScale mDevPixelsPerCSSPixel; // Whether or not this is the root scroll frame for the root content document. - bool mIsRoot; + bool mIsRootContent; // Whether or not this frame is for an element marked 'scrollgrab'. bool mHasScrollgrab; @@ -728,6 +736,11 @@ private: // The clip rect to use when compositing a layer with this FrameMetrics. Maybe mClipRect; + // An extra clip mask layer to use when compositing a layer with this + // FrameMetrics. This is an index into the MetricsMaskLayers array on + // the Layer. + Maybe mMaskLayerIndex; + // Whether these framemetrics are for the root scroll frame (root element if // we don't have a root scroll frame) for its layers id. bool mIsLayersIdRoot; diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 7154639943..da69db054c 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -83,6 +83,10 @@ NotifySubdocumentInvalidationRecursive(Layer* aLayer, NotifySubDocInvalidationFu if (aLayer->GetMaskLayer()) { NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback); } + for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { + Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i); + NotifySubdocumentInvalidationRecursive(maskLayer, aCallback); + } if (!container) { return; @@ -111,6 +115,10 @@ struct LayerPropertiesBase : public LayerProperties if (aLayer->GetMaskLayer()) { mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true); } + for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { + Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i); + mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true)); + } if (mUseClipRect) { mClipRect = *aLayer->GetClipRect(); } @@ -139,10 +147,22 @@ struct LayerPropertiesBase : public LayerProperties bool transformChanged = !mTransform.FuzzyEqualsMultiplicative(mLayer->GetLocalTransform()) || mLayer->GetPostXScale() != mPostXScale || mLayer->GetPostYScale() != mPostYScale; - Layer* otherMask = mLayer->GetMaskLayer(); const Maybe& otherClip = mLayer->GetClipRect(); nsIntRegion result; + + bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount(); + if (!ancestorMaskChanged) { + for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) { + if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) { + ancestorMaskChanged = true; + break; + } + } + } + + Layer* otherMask = mLayer->GetMaskLayer(); if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask || + ancestorMaskChanged || (mUseClipRect != !!otherClip) || mLayer->GetLocalOpacity() != mOpacity || transformChanged) @@ -162,6 +182,15 @@ struct LayerPropertiesBase : public LayerProperties mTransform); } + for (size_t i = 0; + i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount()); + i++) + { + AddTransformedRegion(result, + mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged), + mTransform); + } + if (mUseClipRect && otherClip) { if (!mClipRect.IsEqualInterior(*otherClip)) { aGeometryChanged = true; @@ -193,6 +222,7 @@ struct LayerPropertiesBase : public LayerProperties nsRefPtr mLayer; UniquePtr mMaskLayer; + nsTArray> mAncestorMaskLayers; nsIntRegion mVisibleRegion; nsIntRegion mInvalidRegion; Matrix4x4 mTransform; @@ -443,6 +473,9 @@ LayerProperties::ClearInvalidations(Layer *aLayer) if (aLayer->GetMaskLayer()) { ClearInvalidations(aLayer->GetMaskLayer()); } + for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { + ClearInvalidations(aLayer->GetAncestorMaskLayerAt(i)); + } ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) { diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 8244a41452..3d5abf92bf 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1190,7 +1190,7 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual); bool useIntermediateSurface; - if (GetMaskLayer() || + if (HasMaskLayers() || GetForceIsolatedGroup()) { useIntermediateSurface = true; #ifdef MOZ_DUMP_PAINTING @@ -1219,7 +1219,7 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS * Nor for a child with a mask layer. */ if ((clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) || - child->GetMaskLayer()) { + child->HasMaskLayers()) { useIntermediateSurface = true; break; } @@ -1521,6 +1521,13 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml) mask->Dump(aStream, pfx.get(), aDumpHtml); } + for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) { + aStream << nsPrintfCString("%s Ancestor mask layer %d:\n", aPrefix, uint32_t(i)).get(); + nsAutoCString pfx(aPrefix); + pfx += " "; + GetAncestorMaskLayerAt(i)->Dump(aStream, pfx.get(), aDumpHtml); + } + #ifdef MOZ_DUMP_PAINTING for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) { const nsCString& str = mExtraDumpInfo[i]; diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 4aad63e8d8..de24d1a584 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -1050,6 +1050,18 @@ public: } } + /** + * CONSTRUCTION PHASE ONLY + * Add a FrameMetrics-associated mask layer. + */ + void SetAncestorMaskLayers(const nsTArray>& aLayers) { + if (aLayers != mAncestorMaskLayers) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AncestorMaskLayers", this)); + mAncestorMaskLayers = aLayers; + Mutated(); + } + } + /** * CONSTRUCTION PHASE ONLY * Tell this layer what its transform should be. The transformation @@ -1263,6 +1275,19 @@ public: bool IsScrollbarContainer() { return mIsScrollbarContainer; } Layer* GetMaskLayer() const { return mMaskLayer; } + // Ancestor mask layers are associated with FrameMetrics, but for simplicity + // in maintaining the layer tree structure we attach them to the layer. + size_t GetAncestorMaskLayerCount() const { + return mAncestorMaskLayers.Length(); + } + Layer* GetAncestorMaskLayerAt(size_t aIndex) const { + return mAncestorMaskLayers.ElementAt(aIndex); + } + + bool HasMaskLayers() const { + return GetMaskLayer() || mAncestorMaskLayers.Length() > 0; + } + /* * Get the combined clip rect of the Layer clip and all clips on FrameMetrics. * This is intended for use in Layout. The compositor needs to apply async @@ -1687,6 +1712,7 @@ protected: Layer* mPrevSibling; void* mImplData; nsRefPtr mMaskLayer; + nsTArray> mAncestorMaskLayers; gfx::UserData mUserData; gfx::IntRect mLayerBounds; nsIntRegion mVisibleRegion; diff --git a/gfx/layers/ReadbackProcessor.cpp b/gfx/layers/ReadbackProcessor.cpp index 5989146e39..808bac377b 100644 --- a/gfx/layers/ReadbackProcessor.cpp +++ b/gfx/layers/ReadbackProcessor.cpp @@ -73,9 +73,11 @@ FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset) return nullptr; if (l->GetEffectiveOpacity() != 1.0 || - l->GetMaskLayer() || + l->HasMaskLayers() || !(l->GetContentFlags() & Layer::CONTENT_OPAQUE)) + { return nullptr; + } // cliprects are post-transform const Maybe& clipRect = l->GetEffectiveClipRect(); diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index 9f31333755..30568962be 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -74,24 +74,12 @@ public: const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId) = 0; - /** - * Requests handling of releasing a long tap. |aPoint| is in CSS pixels, - * relative to the current scroll offset. HandleLongTapUp will always be - * preceeded by HandleLongTap. However not all calls to HandleLongTap will - * be followed by a HandleLongTapUp (for example, if the user drags - * around between the long-tap and lifting their finger, or if content - * notifies the APZ that the long-tap event was prevent-defaulted). - */ - virtual void HandleLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid& aGuid) = 0; - /** * Requests sending a mozbrowserasyncscroll domevent to embedder. * |aContentRect| is in CSS pixels, relative to the current cssPage. * |aScrollableSize| is the current content width/height in CSS pixels. */ - virtual void SendAsyncScrollDOMEvent(bool aIsRoot, + virtual void SendAsyncScrollDOMEvent(bool aIsRootContent, const CSSRect &aContentRect, const CSSSize &aScrollableSize) = 0; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 2b70a87b62..f16c2e4b25 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -683,12 +683,7 @@ public: aSpringConstant, aDampingRatio) , mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y, aSpringConstant, aDampingRatio) - , mSource(aSource) - , mAllowOverscroll(true) { - if (mSource == ScrollSource::Wheel) { - mAllowOverscroll = mApzc.AllowScrollHandoffInWheelTransaction(); - } } /** @@ -733,11 +728,7 @@ public: ParentLayerPoint overscroll; ParentLayerPoint adjustedOffset; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); - - bool forceVerticalOverscroll = mSource == ScrollSource::Wheel && - !aFrameMetrics.AllowVerticalScrollWithWheel(); - mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y, - forceVerticalOverscroll); + mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y); aFrameMetrics.ScrollBy(adjustedOffset / zoom); @@ -745,7 +736,7 @@ public: // This can happen if either the layout.css.scroll-behavior.damping-ratio // preference is set to less than 1 (underdamped) or if a smooth scroll // inherits velocity from a fling gesture. - if (!IsZero(overscroll) && mAllowOverscroll) { + if (!IsZero(overscroll)) { // Hand off a fling with the remaining momentum to the next APZC in the // overscroll handoff chain. @@ -782,8 +773,6 @@ public: private: AsyncPanZoomController& mApzc; AxisPhysicsMSDModel mXAxisModel, mYAxisModel; - ScrollSource mSource; - bool mAllowOverscroll; }; void @@ -1427,63 +1416,60 @@ AsyncPanZoomController::ConvertToGecko(const ParentLayerPoint& aPoint, CSSPoint* return false; } -void -AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent, - double& aOutDeltaX, - double& aOutDeltaY) const +LayoutDevicePoint +AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) const { ReentrantMonitorAutoEnter lock(mMonitor); - aOutDeltaX = aEvent.mDeltaX; - aOutDeltaY = aEvent.mDeltaY; + LayoutDevicePoint delta(aEvent.mDeltaX, aEvent.mDeltaY); switch (aEvent.mDeltaType) { case ScrollWheelInput::SCROLLDELTA_LINE: { LayoutDeviceIntSize scrollAmount = mFrameMetrics.GetLineScrollAmount(); - aOutDeltaX *= scrollAmount.width; - aOutDeltaY *= scrollAmount.height; + delta.x *= scrollAmount.width; + delta.y *= scrollAmount.height; break; } default: MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type"); } - if (gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) { + if (mFrameMetrics.IsRootContent() && gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) { // Only apply delta multipliers if we're increasing the delta. double hfactor = double(gfxPrefs::MouseWheelRootHScrollDeltaFactor()) / 100; double vfactor = double(gfxPrefs::MouseWheelRootVScrollDeltaFactor()) / 100; if (vfactor > 1.0) { - aOutDeltaX *= hfactor; + delta.x *= hfactor; } if (hfactor > 1.0) { - aOutDeltaY *= vfactor; + delta.y *= vfactor; } } LayoutDeviceIntSize pageScrollSize = mFrameMetrics.GetPageScrollAmount(); - if (Abs(aOutDeltaX) > pageScrollSize.width) { - aOutDeltaX = (aOutDeltaX >= 0) - ? pageScrollSize.width - : -pageScrollSize.width; + if (Abs(delta.x) > pageScrollSize.width) { + delta.x = (delta.x >= 0) + ? pageScrollSize.width + : -pageScrollSize.width; } - if (Abs(aOutDeltaY) > pageScrollSize.height) { - aOutDeltaY = (aOutDeltaY >= 0) - ? pageScrollSize.height - : -pageScrollSize.height; + if (Abs(delta.y) > pageScrollSize.height) { + delta.y = (delta.y >= 0) + ? pageScrollSize.height + : -pageScrollSize.height; } + + return delta; } // Return whether or not the underlying layer can be scrolled on either axis. bool AsyncPanZoomController::CanScroll(const ScrollWheelInput& aEvent) const { - double deltaX, deltaY; - GetScrollWheelDelta(aEvent, deltaX, deltaY); - - if (!deltaX && !deltaY) { + LayoutDevicePoint delta = GetScrollWheelDelta(aEvent); + if (!delta.x && !delta.y) { return false; } - return CanScroll(deltaX, deltaY); + return CanScroll(delta.x, delta.y); } bool @@ -1502,11 +1488,10 @@ AsyncPanZoomController::AllowScrollHandoffInWheelTransaction() const nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent) { - double deltaX, deltaY; - GetScrollWheelDelta(aEvent, deltaX, deltaY); + LayoutDevicePoint delta = GetScrollWheelDelta(aEvent); - if ((deltaX || deltaY) && - !CanScroll(deltaX, deltaY) && + if ((delta.x || delta.y) && + !CanScroll(delta.x, delta.y) && mInputQueue->GetCurrentWheelTransaction()) { // We can't scroll this apz anymore, so we simply drop the event. @@ -1532,16 +1517,15 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve // wheel and touchpad scroll gestures, so we invert x/y here. Since the // zoom includes any device : css pixel zoom, we convert to CSS pixels // before applying the zoom. - LayoutDevicePoint devicePixelDelta(-deltaX, -deltaY); - ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.GetDevPixelsPerCSSPixel()) * + ParentLayerPoint panDelta = (-delta / mFrameMetrics.GetDevPixelsPerCSSPixel()) * mFrameMetrics.GetZoom(); PanGestureInput move(PanGestureInput::PANGESTURE_PAN, aEvent.mTime, aEvent.mTimeStamp, aEvent.mOrigin, - ToScreenCoordinates(delta, aEvent.mLocalOrigin), + ToScreenCoordinates(panDelta, aEvent.mLocalOrigin), aEvent.modifiers); move.mLocalPanStartPoint = aEvent.mLocalOrigin; - move.mLocalPanDisplacement = delta; + move.mLocalPanDisplacement = panDelta; OnPan(move, ScrollSource::Wheel, false); PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp, @@ -1564,13 +1548,13 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to // appunits/second - nsPoint delta = - CSSPoint::ToAppUnits(LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.GetDevPixelsPerCSSPixel()); + nsPoint deltaInAppUnits = + CSSPoint::ToAppUnits(delta / mFrameMetrics.GetDevPixelsPerCSSPixel()); nsPoint velocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f; WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation(); - animation->Update(aEvent.mTimeStamp, delta, nsSize(velocity.x, velocity.y)); + animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y)); break; } } @@ -1743,15 +1727,7 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) { APZC_LOG("%p got a long-tap-up in state %d\n", this, mState); - nsRefPtr controller = GetGeckoContentController(); - if (controller) { - CSSPoint geckoScreenPoint; - if (ConvertToGecko(aEvent.mLocalPoint, &geckoScreenPoint)) { - controller->HandleLongTapUp(geckoScreenPoint, aEvent.modifiers, GetGuid()); - return nsEventStatus_eConsumeNoDefault; - } - } - return nsEventStatus_eIgnore; + return GenerateSingleTap(aEvent.mLocalPoint, aEvent.modifiers); } nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ParentLayerPoint& aPoint, mozilla::Modifiers aModifiers) { @@ -2894,6 +2870,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount()); mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount()); mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect()); + mFrameMetrics.SetMaskLayerIndex(aLayerMetrics.GetMaskLayerIndex()); mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot()); mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling()); @@ -3178,19 +3155,19 @@ void AsyncPanZoomController::SendAsyncScrollEvent() { return; } - bool isRoot; + bool isRootContent; CSSRect contentRect; CSSSize scrollableSize; { ReentrantMonitorAutoEnter lock(mMonitor); - isRoot = mFrameMetrics.GetIsRoot(); + isRootContent = mFrameMetrics.IsRootContent(); scrollableSize = mFrameMetrics.GetScrollableRect().Size(); contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels(); contentRect.MoveTo(mCurrentAsyncScrollOffset); } - controller->SendAsyncScrollDOMEvent(isRoot, contentRect, scrollableSize); + controller->SendAsyncScrollDOMEvent(isRootContent, contentRect, scrollableSize); } bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index c0d3472401..0e68e74377 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -428,9 +428,7 @@ protected: */ nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent); - void GetScrollWheelDelta(const ScrollWheelInput& aEvent, - double& aOutDeltaX, - double& aOutDeltaY) const; + LayoutDevicePoint GetScrollWheelDelta(const ScrollWheelInput& aEvent) const; /** * Helper methods for long press gestures. diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp index 763faeb511..b6cff64241 100644 --- a/gfx/layers/apz/util/APZEventState.cpp +++ b/gfx/layers/apz/util/APZEventState.cpp @@ -233,17 +233,6 @@ APZEventState::ProcessLongTap(const nsCOMPtr& aPresShell, mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled); } -void -APZEventState::ProcessLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid& aGuid, - float aPresShellResolution) -{ - APZES_LOG("Handling long tap up at %s\n", Stringify(aPoint).c_str()); - - ProcessSingleTap(aPoint, aModifiers, aGuid, aPresShellResolution); -} - void APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, diff --git a/gfx/layers/apz/util/APZEventState.h b/gfx/layers/apz/util/APZEventState.h index bef7eac029..adbf5feb61 100644 --- a/gfx/layers/apz/util/APZEventState.h +++ b/gfx/layers/apz/util/APZEventState.h @@ -59,10 +59,6 @@ public: const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, float aPresShellResolution); - void ProcessLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid& aGuid, - float aPresShellResolution); void ProcessTouchEvent(const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp index 173f57c87b..333b7e2795 100644 --- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -183,21 +183,6 @@ ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, Modifier aInputBlockId, GetPresShellResolution()); } -void -ChromeProcessController::HandleLongTapUp(const CSSPoint& aPoint, Modifiers aModifiers, - const ScrollableLayerGuid& aGuid) -{ - if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromeProcessController::HandleLongTapUp, - aPoint, aModifiers, aGuid)); - return; - } - - mAPZEventState->ProcessLongTapUp(aPoint, aModifiers, aGuid, GetPresShellResolution()); -} - void ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h index 1366b62a7f..7d07e62a49 100644 --- a/gfx/layers/apz/util/ChromeProcessController.h +++ b/gfx/layers/apz/util/ChromeProcessController.h @@ -49,9 +49,7 @@ public: virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers, const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId) override; - virtual void HandleLongTapUp(const CSSPoint& aPoint, Modifiers aModifiers, - const ScrollableLayerGuid& aGuid) override; - virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, + virtual void SendAsyncScrollDOMEvent(bool aIsRootContent, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize) override {} virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp index 030239cbec..090beb317f 100644 --- a/gfx/layers/client/ClientCanvasLayer.cpp +++ b/gfx/layers/client/ClientCanvasLayer.cpp @@ -133,9 +133,7 @@ ClientCanvasLayer::RenderLayer() PROFILER_LABEL("ClientCanvasLayer", "RenderLayer", js::ProfileEntry::Category::GRAPHICS); - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + RenderMaskLayers(this); if (!IsDirty()) { return; diff --git a/gfx/layers/client/ClientColorLayer.cpp b/gfx/layers/client/ClientColorLayer.cpp index 6ce9b16366..932809ce03 100644 --- a/gfx/layers/client/ClientColorLayer.cpp +++ b/gfx/layers/client/ClientColorLayer.cpp @@ -43,9 +43,7 @@ public: virtual void RenderLayer() { - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + RenderMaskLayers(this); } virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) diff --git a/gfx/layers/client/ClientContainerLayer.h b/gfx/layers/client/ClientContainerLayer.h index b93fdc3038..64bd05483a 100644 --- a/gfx/layers/client/ClientContainerLayer.h +++ b/gfx/layers/client/ClientContainerLayer.h @@ -47,9 +47,7 @@ protected: public: virtual void RenderLayer() override { - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + RenderMaskLayers(this); DefaultComputeSupportsComponentAlphaChildren(); diff --git a/gfx/layers/client/ClientImageLayer.cpp b/gfx/layers/client/ClientImageLayer.cpp index 5d8d085f9f..0df90e8a68 100644 --- a/gfx/layers/client/ClientImageLayer.cpp +++ b/gfx/layers/client/ClientImageLayer.cpp @@ -126,9 +126,7 @@ protected: void ClientImageLayer::RenderLayer() { - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + RenderMaskLayers(this); if (!mContainer) { return; diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h index 311b3bd977..239cc8f5f3 100644 --- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -390,6 +390,16 @@ public: { return static_cast(aLayer->ImplData()); } + + template + static inline void RenderMaskLayers(LayerType* aLayer) { + if (aLayer->GetMaskLayer()) { + ToClientLayer(aLayer->GetMaskLayer())->RenderLayer(); + } + for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { + ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer(); + } + } }; // Create a shadow layer (PLayerChild) for aLayer, if we're forwarding diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp index e564c91d32..62e183149a 100644 --- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -117,9 +117,7 @@ ClientPaintedLayer::PaintThebes() void ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback) { - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + RenderMaskLayers(this); if (!mContentClient) { mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder()); diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp index 49bbb13e3f..8abaa35372 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.cpp +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -447,10 +447,8 @@ ClientTiledPaintedLayer::RenderLayer() } if (!ClientManager()->IsRepeatTransaction()) { - // Only paint the mask layer on the first transaction. - if (GetMaskLayer()) { - ToClientLayer(GetMaskLayer())->RenderLayer(); - } + // Only paint the mask layers on the first transaction. + RenderMaskLayers(this); // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index 399821af14..55ea1684bd 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -234,7 +234,7 @@ LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaque // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (isTranslation && - !aLayer->GetMaskLayer() && + !aLayer->HasMaskLayers() && aLayer->GetLocalOpacity() == 1.0f) { if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion()); diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 7bd063b9a5..fd51ce7384 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -1056,6 +1056,9 @@ CompositorParent::SetShadowProperties(Layer* aLayer) if (Layer* maskLayer = aLayer->GetMaskLayer()) { SetShadowProperties(maskLayer); } + for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { + SetShadowProperties(aLayer->GetAncestorMaskLayerAt(i)); + } // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate. LayerComposite* layerComposite = aLayer->AsLayerComposite(); diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 4c7b9507c8..70b049564d 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -352,6 +352,13 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset, layer->SetInvalidRegion(common.invalidRegion()); layer->SetFrameMetrics(common.metrics()); + nsTArray> maskLayers; + for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) { + Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer(); + maskLayers.AppendElement(maskLayer); + } + layer->SetAncestorMaskLayers(maskLayers); + typedef SpecificLayerAttributes Specific; const SpecificLayerAttributes& specific = attrs.specific(); switch (specific.type()) { diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh index c1a0a12e07..dfaeb978c9 100644 --- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -225,6 +225,7 @@ struct CommonLayerAttributes { int8_t mixBlendMode; bool forceIsolatedGroup; nullable PLayer maskLayer; + PLayer[] ancestorMaskLayers; // Animated colors will only honored for ColorLayers. Animation[] animations; nsIntRegion invalidRegion; diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index ab9f02472b..c635aec5da 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -607,6 +607,10 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies, common.animations() = mutant->GetAnimations(); common.invalidRegion() = mutant->GetInvalidRegion(); common.metrics() = mutant->GetAllFrameMetrics(); + for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) { + auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer()); + common.ancestorMaskLayersChild().AppendElement(layer); + } attrs.specific() = null_t(); mutant->FillSpecificAttributes(attrs.specific()); diff --git a/gfx/tests/gtest/TestAsyncPanZoomController.cpp b/gfx/tests/gtest/TestAsyncPanZoomController.cpp index b51b046f1a..95d7592ba9 100644 --- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -67,7 +67,6 @@ public: MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&)); MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&)); MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t)); - MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&)); MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize)); MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs)); MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg)); @@ -615,7 +614,7 @@ protected: fm.SetScrollOffset(CSSPoint(300, 300)); fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0)); // APZC only allows zooming on the root scrollable frame. - fm.SetIsRoot(true); + fm.SetIsRootContent(true); // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 return fm; } @@ -758,7 +757,7 @@ TEST_F(APZCBasicTester, Overzoom) { fm.SetScrollableRect(CSSRect(0, 0, 125, 150)); fm.SetScrollOffset(CSSPoint(10, 0)); fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0)); - fm.SetIsRoot(true); + fm.SetIsRootContent(true); apzc->SetFrameMetrics(fm); MakeApzcZoomable(); @@ -1342,9 +1341,9 @@ protected: EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1); EXPECT_CALL(check, Call("postHandleLongTap")); - EXPECT_CALL(check, Call("preHandleLongTapUp")); - EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); - EXPECT_CALL(check, Call("postHandleLongTapUp")); + EXPECT_CALL(check, Call("preHandleSingleTap")); + EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); + EXPECT_CALL(check, Call("postHandleSingleTap")); } // There is a longpress event scheduled on a timeout @@ -1371,10 +1370,11 @@ protected: // Finally, simulate lifting the finger. Since the long-press wasn't // prevent-defaulted, we should get a long-tap-up event. - check.Call("preHandleLongTapUp"); + check.Call("preHandleSingleTap"); status = TouchUp(apzc, 10, 10, time); + mcc->RunDelayedTask(); EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status); - check.Call("postHandleLongTapUp"); + check.Call("postHandleSingleTap"); apzc->AssertStateIsReset(); } @@ -1440,7 +1440,7 @@ protected: status = apzc->ReceiveInputEvent(mti, nullptr); EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status); - EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0); + EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0); status = TouchUp(apzc, touchX, touchEndY, time); EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status); diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index 02f233692b..b509dd256f 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -212,18 +212,6 @@ TryEvalJSON(JSContext* cx, JSLinearString* str, MutableHandleValue rval) : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval); } -static bool -HasPollutedScopeChain(JSObject* scopeChain) -{ - while (scopeChain) { - if (scopeChain->is()) - return true; - scopeChain = scopeChain->enclosingScope(); - } - - return false; -} - // Define subset of ExecuteType so that casting performs the injection. enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL }; @@ -250,6 +238,12 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame return false; } + if (evalType == DIRECT_EVAL && caller.script()->isDerivedClassConstructor()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS, + "direct eval"); + return false; + } + // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); @@ -326,13 +320,8 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame if (!staticScope) return false; - bool hasPollutedGlobalScope = - HasPollutedScopeChain(scopeobj) || - (evalType == DIRECT_EVAL && callerScript->hasPollutedGlobalScope()); - CompileOptions options(cx); options.setFileAndLine(filename, 1) - .setHasPollutedScope(hasPollutedGlobalScope) .setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) @@ -404,7 +393,7 @@ js::DirectEvalStringFromIon(JSContext* cx, bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, - &mutedErrors, CALLED_FROM_JSOP_EVAL); + &mutedErrors, CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) @@ -417,8 +406,6 @@ js::DirectEvalStringFromIon(JSContext* cx, CompileOptions options(cx); options.setFileAndLine(filename, 1) - .setHasPollutedScope(HasPollutedScopeChain(scopeobj) || - callerScript->hasPollutedGlobalScope()) .setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) @@ -507,11 +494,14 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri CHECK_REQUEST(cx); assertSameCompartment(cx, global); MOZ_ASSERT(global->is()); - MOZ_RELEASE_ASSERT(scriptArg->hasPollutedGlobalScope()); + MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope()); RootedScript script(cx, scriptArg); if (script->compartment() != cx->compartment()) { - script = CloneScript(cx, nullptr, nullptr, script); + Rooted staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr)); + if (!staticScope) + return false; + script = CloneGlobalScript(cx, staticScope, script); if (!script) return false; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 7bf02b1d1a..0bf89ca9f1 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2360,11 +2360,10 @@ EvalReturningScope(JSContext* cx, unsigned argc, jsval* vp) JS::CompileOptions options(cx); options.setFileAndLine(filename.get(), lineno); options.setNoScriptRval(true); - options.setHasPollutedScope(true); JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership); RootedScript script(cx); - if (!JS::Compile(cx, options, srcBuf, &script)) + if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) return false; if (global) { @@ -2508,18 +2507,42 @@ SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) return true; } +#ifdef DEBUG static bool -SetLazyParsingEnabled(JSContext* cx, unsigned argc, Value* vp) +DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - if (argc < 1) { - JS_ReportError(cx, "setLazyParsingEnabled: need an argument"); + RootedString str(cx, ToString(cx, args.get(0))); + if (!str) return false; - } - bool arg = ToBoolean(args.get(0)); - JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(!arg); + str->dumpRepresentation(stderr, 0); + + args.rval().setUndefined(); + return true; +} +#endif + +static bool +SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + bool disable = !args.hasDefined(0) || ToBoolean(args[0]); + JS::CompartmentOptionsRef(cx->compartment()).setDisableLazyParsing(disable); + + args.rval().setUndefined(); + return true; +} + +static bool +SetDiscardSource(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + bool discard = !args.hasDefined(0) || ToBoolean(args[0]); + JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(discard); args.rval().setUndefined(); return true; @@ -2953,9 +2976,21 @@ gc::ZealModeHelpText), " of internal error, or if the operation doesn't even make sense (for example,\n" " because the object is a revoked proxy)."), - JS_FN_HELP("setLazyParsingEnabled", SetLazyParsingEnabled, 1, 0, -"setLazyParsingEnabled(bool)", -" Enable or disable lazy parsing in the current compartment. The default is enabled."), +#ifdef DEBUG + JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0, +"dumpStringRepresentation(str)", +" Print a human-readable description of how the string |str| is represented.\n"), +#endif + + JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0, +"setLazyParsingDisabled(bool)", +" Explicitly disable lazy parsing in the current compartment. The default is that lazy " +" parsing is not explicitly disabled."), + + JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0, +"setDiscardSource(bool)", +" Explicitly enable source discarding in the current compartment. The default is that " +" source discarding is not explicitly enabled."), JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0, "getConstructorName(object)", diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index c97fe91a4b..a08843860b 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -131,10 +131,12 @@ MaybeCheckEvalFreeVariables(ExclusiveContext* cxArg, HandleScript evalCaller, Ha } static inline bool -CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options) +CanLazilyParse(ExclusiveContext* cx, HandleObject staticScope, + const ReadOnlyCompileOptions& options) { return options.canLazilyParse && - !options.hasPollutedGlobalScope && + !HasNonSyntacticStaticScopeChain(staticScope) && + !cx->compartment()->options().disableLazyParsing() && !cx->compartment()->options().discardSource() && !options.sourceIsLazy; } @@ -246,7 +248,7 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco return nullptr; } - bool canLazilyParse = CanLazilyParse(cx, options); + bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options); Maybe > syntaxParser; if (canLazilyParse) { @@ -547,7 +549,7 @@ CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyComp return false; } - bool canLazilyParse = CanLazilyParse(cx, options); + bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options); Maybe > syntaxParser; if (canLazilyParse) { diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 07b5d6c6e8..36f93a1d7b 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1548,14 +1548,14 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) // Use generic ops if a catch block is encountered. return false; } - if (ssi.hasDynamicScopeObject()) + if (ssi.hasSyntacticDynamicScopeObject()) hops++; continue; } RootedScript script(cx, ssi.funScript()); if (script->functionNonDelazifying()->atom() == pn->pn_atom) return false; - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { uint32_t slot; if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) { JSOp op; @@ -1587,9 +1587,9 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) if (insideNonGlobalEval) return false; - // Skip trying to use GNAME ops if we know our script has a polluted - // global scope, since they'll just get treated as NAME ops anyway. - if (script->hasPollutedGlobalScope()) + // Skip trying to use GNAME ops if we know our script has a non-syntactic + // scope, since they'll just get treated as NAME ops anyway. + if (script->hasNonSyntacticScope()) return false; // Deoptimized names also aren't necessarily globals. @@ -2530,13 +2530,14 @@ BytecodeEmitter::checkRunOnceContext() bool BytecodeEmitter::needsImplicitThis() { - if (sc->isFunctionBox() && sc->asFunctionBox()->inWith) + if (sc->inWith()) return true; for (StmtInfoBCE* stmt = topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_WITH) return true; } + return false; } @@ -3580,6 +3581,19 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body) */ FunctionBox* funbox = sc->asFunctionBox(); + + // Link the function and the script to each other, so that StaticScopeIter + // may walk the scope chain of currently compiling scripts. + RootedFunction fun(cx, funbox->function()); + MOZ_ASSERT(fun->isInterpreted()); + + script->setFunction(fun); + + if (fun->isInterpretedLazy()) + fun->setUnlazifiedScript(script); + else + fun->setScript(script); + if (funbox->argumentsHasLocalBinding()) { MOZ_ASSERT(offset() == 0); /* See JSScript::argumentsBytecode. */ switchToPrologue(); @@ -3683,15 +3697,6 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body) MOZ_ASSERT(!script->hasRunOnce()); } - /* Initialize fun->script() so that the debugger has a valid fun->script(). */ - RootedFunction fun(cx, script->functionNonDelazifying()); - MOZ_ASSERT(fun->isInterpreted()); - - if (fun->isInterpretedLazy()) - fun->setUnlazifiedScript(script); - else - fun->setScript(script); - tellDebuggerAboutCompiledScript(cx); return true; @@ -5938,8 +5943,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) Rooted parent(cx, script); CompileOptions options(cx, parser->options()); options.setMutedErrors(parent->mutedErrors()) - .setHasPollutedScope(parent->hasPollutedGlobalScope()) - .setSelfHostingMode(parent->selfHosted()) .setNoScriptRval(false) .setForEval(false) .setVersion(parent->getVersion()); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 9dcf62fa9d..b4305c90ba 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -633,6 +633,10 @@ class FullParseHandler void addFunctionArgument(ParseNode* pn, ParseNode* argpn) { pn->pn_body->append(argpn); } + void setDerivedClassConstructor(ParseNode* pn) { + MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); + pn->pn_funbox->setDerivedClassConstructor(); + } ParseNode* newLexicalScope(ObjectBox* blockBox) { return new_(blockBox, pos()); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 2b645704bd..38b881fcd7 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1732,6 +1732,7 @@ enum FunctionSyntaxKind Arrow, Method, ClassConstructor, + DerivedClassConstructor, Getter, Setter }; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index fa81d566a3..761aab496d 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -599,7 +599,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct bufEnd(0), length(0), generatorKindBits_(GeneratorKindAsBits(generatorKind)), - inWith(false), // initialized below + inWith_(false), // initialized below inGenexpLambda(false), hasDestructuringArgs(false), useAsm(false), @@ -615,7 +615,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct MOZ_ASSERT(fun->isTenured()); if (!outerpc) { - inWith = false; + inWith_ = false; } else if (outerpc->parsingWith) { // This covers cases that don't involve eval(). For example: @@ -624,7 +624,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct // // In this case, |outerpc| corresponds to global code, and // outerpc->parsingWith is true. - inWith = true; + inWith_ = true; } else if (outerpc->sc->isFunctionBox()) { // This is like the above case, but for more deeply nested functions. @@ -635,8 +635,8 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct // In this case, the inner anonymous function needs to inherit the // setting of |inWith| from the outer one. FunctionBox* parent = outerpc->sc->asFunctionBox(); - if (parent && parent->inWith) - inWith = true; + if (parent && parent->inWith()) + inWith_ = true; } else { // This is like the above case, but when inside eval. // @@ -645,7 +645,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct // with(o) { eval("(function() { g(); })();"); } // // In this case, the static scope chain tells us the presence of with. - inWith = outerpc->sc->asGlobalSharedContext()->inWith(); + inWith_ = outerpc->sc->inWith(); } } @@ -1193,6 +1193,7 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case ClassConstructor: + case DerivedClassConstructor: flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR; allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; @@ -2244,7 +2245,7 @@ Parser::finishFunctionDefinition(Node pn, FunctionBox* funbo // while its ParseContext and associated lexdeps and inner functions are // still available. - if (funbox->inWith) + if (funbox->inWith()) return abortIfSyntaxParser(); size_t numFreeVariables = pc->lexdeps->count(); @@ -2272,6 +2273,8 @@ Parser::finishFunctionDefinition(Node pn, FunctionBox* funbo lazy->setGeneratorKind(funbox->generatorKind()); if (funbox->usesArguments && funbox->usesApply && funbox->usesThis) lazy->setUsesArgumentsApplyAndThis(); + if (funbox->isDerivedClassConstructor()) + lazy->setIsDerivedClassConstructor(); PropagateTransitiveParseFlags(funbox, lazy); fun->initLazyScript(lazy); @@ -2293,6 +2296,9 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* if (!funbox) return false; + if (kind == DerivedClassConstructor) + funbox->setDerivedClassConstructor(); + YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName; // Try a syntax parse for this inner function. @@ -2455,6 +2461,9 @@ Parser::standaloneLazyFunction(HandleFunction fun, unsigned st return null(); funbox->length = fun->nargs() - fun->hasRest(); + if (fun->lazyScript()->isDerivedClassConstructor()) + funbox->setDerivedClassConstructor(); + Directives newDirectives = directives; ParseContext funpc(this, /* parent = */ nullptr, pn, funbox, &newDirectives, staticLevel, /* bodyid = */ 0, @@ -2533,7 +2542,7 @@ Parser::functionArgsAndBodyGeneric(InHandling inHandling, if (!tokenStream.getToken(&tt, TokenStream::Operand)) return false; if (tt != TOK_LC) { - if (funbox->isStarGenerator()) { + if (funbox->isStarGenerator() || kind == Method || kind == ClassConstructor) { report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); return false; } @@ -4399,6 +4408,10 @@ Parser::exportDeclaration() default: tokenStream.ungetToken(); kid = assignExpr(InAllowed, YieldIsKeyword); + if (kid) { + if (!MatchOrInsertSemicolon(tokenStream)) + return null(); + } break; } if (!kid) @@ -5867,7 +5880,8 @@ Parser::classDefinition(YieldHandling yieldHandling, MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS); - ParseNode* classMethods = propertyList(yieldHandling, ClassBody); + ParseNode* classMethods = propertyList(yieldHandling, + hasHeritage ? DerivedClassBody : ClassBody); if (!classMethods) return null(); @@ -6454,6 +6468,11 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.peekToken(&ignored)) return null(); + if (pc->sc->isFunctionBox() && pc->sc->asFunctionBox()->isDerivedClassConstructor()) { + report(ParseError, false, null(), JSMSG_DISABLED_DERIVED_CLASS, "arrow functions"); + return null(); + } + return functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator); } @@ -8225,7 +8244,7 @@ template typename ParseHandler::Node Parser::newPropertyListNode(PropListType type) { - if (type == ClassBody) + if (IsClassBody(type)) return handler.newClassMethodList(pos().begin); MOZ_ASSERT(type == ObjectLiteral); @@ -8253,7 +8272,7 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ break; bool isStatic = false; - if (type == ClassBody) { + if (IsClassBody(type)) { if (ltok == TOK_SEMI) continue; @@ -8393,7 +8412,7 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ } } - if (type == ClassBody) { + if (IsClassBody(type)) { if (!isStatic && atom == context->names().constructor) { if (isGenerator || op != JSOP_INITPROP) { report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF); @@ -8417,7 +8436,7 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ return null(); if (tt == TOK_COLON) { - if (type == ClassBody) { + if (IsClassBody(type)) { report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); return null(); } @@ -8459,7 +8478,7 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ * Support, e.g., |var {x, y} = o| as destructuring shorthand * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. */ - if (type == ClassBody) { + if (IsClassBody(type)) { report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); return null(); } @@ -8481,7 +8500,9 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ } else if (tt == TOK_LP) { tokenStream.ungetToken(); if (!methodDefinition(yieldHandling, type, propList, propname, - isConstructor ? ClassConstructor : Method, + isConstructor ? type == DerivedClassBody ? DerivedClassConstructor + : ClassConstructor + : Method, isGenerator ? StarGenerator : NotGenerator, isStatic, op)) { return null(); @@ -8513,7 +8534,7 @@ Parser::propertyList(YieldHandling yieldHandling, PropListType typ } // Default constructors not yet implemented. See bug 1105463 - if (type == ClassBody && !seenConstructor) { + if (IsClassBody(type) && !seenConstructor) { report(ParseError, false, null(), JSMSG_NO_CLASS_CONSTRUCTOR); return null(); } @@ -8528,19 +8549,23 @@ Parser::methodDefinition(YieldHandling yieldHandling, PropListType Node propList, Node propname, FunctionSyntaxKind kind, GeneratorKind generatorKind, bool isStatic, JSOp op) { - MOZ_ASSERT(kind == Method || kind == ClassConstructor || kind == Getter || kind == Setter); + MOZ_ASSERT(kind == Method || kind == ClassConstructor || kind == DerivedClassConstructor || + kind == Getter || kind == Setter); /* NB: Getter function in { get x(){} } is unnamed. */ RootedPropertyName funName(context); - if ((kind == Method || kind == ClassConstructor) && tokenStream.isCurrentTokenType(TOK_NAME)) + if ((kind == Method || kind == ClassConstructor || kind == DerivedClassConstructor) && + tokenStream.isCurrentTokenType(TOK_NAME)) + { funName = tokenStream.currentName(); - else + } else { funName = nullptr; + } Node fn = functionDef(InAllowed, yieldHandling, funName, kind, generatorKind); if (!fn) return false; - if (listType == ClassBody) + if (IsClassBody(listType)) return handler.addClassMethodDefinition(propList, propname, fn, op, isStatic); MOZ_ASSERT(listType == ObjectLiteral); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 834cd0f55a..9d5c7575a0 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -323,7 +323,13 @@ struct BindData; class CompExprTransplanter; enum VarContext { HoistVars, DontHoistVars }; -enum PropListType { ObjectLiteral, ClassBody }; +enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody }; + +inline bool +IsClassBody(PropListType type) +{ + return type == ClassBody || type == DerivedClassBody; +} // Specify a value for an ES6 grammar parametrization. We have no enum for // [Return] because its behavior is exactly equivalent to checking whether diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 4f06df253a..68d922546d 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -126,6 +126,9 @@ class FunctionContextFlags // bool definitelyNeedsArgsObj:1; + bool needsHomeObject:1; + bool isDerivedClassConstructor:1; + FunctionContextFlags flagsForNestedGeneratorComprehensionLambda() const { FunctionContextFlags flags; flags.mightAliasLocals = mightAliasLocals; @@ -133,11 +136,11 @@ class FunctionContextFlags flags.needsDeclEnvObject = false; flags.argumentsHasLocalBinding = false; flags.definitelyNeedsArgsObj = false; + flags.needsHomeObject = false; + flags.isDerivedClassConstructor = false; return flags; } - bool needsHomeObject:1; - public: FunctionContextFlags() : mightAliasLocals(false), @@ -145,7 +148,8 @@ class FunctionContextFlags needsDeclEnvObject(false), argumentsHasLocalBinding(false), definitelyNeedsArgsObj(false), - needsHomeObject(false) + needsHomeObject(false), + isDerivedClassConstructor(false) { } }; @@ -245,6 +249,7 @@ class SharedContext SuperProperty }; virtual bool allowSyntax(AllowedSyntax allowed) const = 0; + virtual bool inWith() const = 0; protected: static bool FunctionAllowsSyntax(JSFunction* func, AllowedSyntax allowed) @@ -253,8 +258,8 @@ class SharedContext switch (allowed) { case AllowedSyntax::NewTarget: - // For now, disallow new.target inside generators - return !func->isGenerator(); + // Any function supports new.target + return true; case AllowedSyntax::SuperProperty: return func->allowSuperProperty(); default:; @@ -267,19 +272,11 @@ class GlobalSharedContext : public SharedContext { private: Handle topStaticScope_; + bool allowNewTarget_; + bool allowSuperProperty_; + bool inWith_; - public: - GlobalSharedContext(ExclusiveContext* cx, - Directives directives, Handle topStaticScope, - bool extraWarnings) - : SharedContext(cx, directives, extraWarnings), - topStaticScope_(topStaticScope) - {} - - ObjectBox* toObjectBox() { return nullptr; } - HandleObject topStaticScope() const { return topStaticScope_; } - - bool allowSyntax(AllowedSyntax allowed) const { + bool computeAllowSyntax(AllowedSyntax allowed) const { StaticScopeIter it(context, topStaticScope_); for (; !it.done(); it++) { if (it.type() == StaticScopeIter::Function && @@ -291,13 +288,39 @@ class GlobalSharedContext : public SharedContext return false; } - bool inWith() const { + bool computeInWith() const { for (StaticScopeIter it(context, topStaticScope_); !it.done(); it++) { if (it.type() == StaticScopeIter::With) return true; } return false; } + + public: + GlobalSharedContext(ExclusiveContext* cx, + Directives directives, Handle topStaticScope, + bool extraWarnings) + : SharedContext(cx, directives, extraWarnings), + topStaticScope_(topStaticScope), + allowNewTarget_(computeAllowSyntax(AllowedSyntax::NewTarget)), + allowSuperProperty_(computeAllowSyntax(AllowedSyntax::SuperProperty)), + inWith_(computeInWith()) + {} + + ObjectBox* toObjectBox() override { return nullptr; } + HandleObject topStaticScope() const { return topStaticScope_; } + bool allowSyntax(AllowedSyntax allowSyntax) const override { + switch (allowSyntax) { + case AllowedSyntax::NewTarget: + // Any function supports new.target + return allowNewTarget_; + case AllowedSyntax::SuperProperty: + return allowSuperProperty_; + default:; + } + MOZ_CRASH("Unknown AllowedSyntax query"); + } + bool inWith() const override { return inWith_; } }; class FunctionBox : public ObjectBox, public SharedContext @@ -311,7 +334,7 @@ class FunctionBox : public ObjectBox, public SharedContext uint16_t length; uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ - bool inWith:1; /* some enclosing scope is a with-statement */ + bool inWith_:1; /* some enclosing scope is a with-statement */ bool inGenexpLambda:1; /* lambda from generator expression */ bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */ bool useAsm:1; /* see useAsmOrInsideUseAsm */ @@ -329,7 +352,7 @@ class FunctionBox : public ObjectBox, public SharedContext ParseContext* pc, Directives directives, bool extraWarnings, GeneratorKind generatorKind); - ObjectBox* toObjectBox() { return this; } + ObjectBox* toObjectBox() override { return this; } JSFunction* function() const { return &object->as(); } GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } @@ -351,6 +374,7 @@ class FunctionBox : public ObjectBox, public SharedContext bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; } bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; } bool needsHomeObject() const { return funCxFlags.needsHomeObject; } + bool isDerivedClassConstructor() const { return funCxFlags.isDerivedClassConstructor; } void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; } void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; } @@ -360,6 +384,8 @@ class FunctionBox : public ObjectBox, public SharedContext funCxFlags.definitelyNeedsArgsObj = true; } void setNeedsHomeObject() { MOZ_ASSERT(function()->allowSuperProperty()); funCxFlags.needsHomeObject = true; } + void setDerivedClassConstructor() { MOZ_ASSERT(function()->isClassConstructor()); + funCxFlags.isDerivedClassConstructor = true; } FunctionContextFlags flagsForNestedGeneratorComprehensionLambda() const { return funCxFlags.flagsForNestedGeneratorComprehensionLambda(); @@ -394,14 +420,13 @@ class FunctionBox : public ObjectBox, public SharedContext isGenerator(); } - bool allowSyntax(AllowedSyntax allowed) const { - // For now (!) we don't allow new.target in generators, and can't - // check that for functions we haven't finished parsing, as they - // don't have initialized scripts. Check from our stashed bits instead. - if (allowed == AllowedSyntax::NewTarget) - return !isGenerator(); + bool allowSyntax(AllowedSyntax allowed) const override { return FunctionAllowsSyntax(function(), allowed); } + + bool inWith() const override { + return inWith_; + } }; inline FunctionBox* diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 9882eb7ff8..5c46167844 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -328,6 +328,7 @@ class SyntaxParseHandler void setEndPosition(Node pn, Node oth) {} void setEndPosition(Node pn, uint32_t end) {} + void setDerivedClassConstructor(Node pn) {} void setPosition(Node pn, const TokenPos& pos) {} TokenPos getPosition(Node pn) { diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index c21c3a1ff3..6eb6a32238 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -211,18 +211,6 @@ AutoGCRooter::trace(JSTracer* trc) return; } - case OBJOBJHASHMAP: { - AutoObjectObjectHashMap::HashMapImpl& map = static_cast(this)->map; - for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) { - TraceRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value"); - JSObject* key = e.front().key(); - TraceRoot(trc, &key, "AutoObjectObjectHashMap key"); - if (key != e.front().key()) - e.rekeyFront(key); - } - return; - } - case OBJU32HASHMAP: { AutoObjectUnsigned32HashMap* self = static_cast(this); AutoObjectUnsigned32HashMap::HashMapImpl& map = self->map; @@ -235,18 +223,6 @@ AutoGCRooter::trace(JSTracer* trc) return; } - case OBJHASHSET: { - AutoObjectHashSet* self = static_cast(this); - AutoObjectHashSet::HashSetImpl& set = self->set; - for (AutoObjectHashSet::Enum e(set); !e.empty(); e.popFront()) { - JSObject* obj = e.front(); - TraceRoot(trc, &obj, "AutoObjectHashSet value"); - if (obj != e.front()) - e.rekeyFront(obj); - } - return; - } - case HASHABLEVALUE: { AutoHashableValueRooter* rooter = static_cast(this); rooter->trace(trc); diff --git a/js/src/jit-test/tests/asm.js/testBug1147144.js b/js/src/jit-test/tests/asm.js/testBug1147144.js index 894535e420..fbdf3a3961 100644 --- a/js/src/jit-test/tests/asm.js/testBug1147144.js +++ b/js/src/jit-test/tests/asm.js/testBug1147144.js @@ -4,7 +4,7 @@ load(libdir + 'asserts.js'); if (!isAsmJSCompilationAvailable()) quit(); -setLazyParsingEnabled(false) +setDiscardSource(true) assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toString(), "function asmModule() {\n [sourceless code]\n}"); assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toSource(), "(function asmModule() {\n [sourceless code]\n})"); assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toString(), "function asmFun() {\n [sourceless code]\n}"); diff --git a/js/src/jit-test/tests/basic/dumpStringRepresentation.js b/js/src/jit-test/tests/basic/dumpStringRepresentation.js new file mode 100644 index 0000000000..43d3feefd4 --- /dev/null +++ b/js/src/jit-test/tests/basic/dumpStringRepresentation.js @@ -0,0 +1,70 @@ +// Try the dumpStringRepresentation shell function on various types of +// strings, and make sure it doesn't crash. + +if (typeof dumpStringRepresentation !== 'function') + quit(0); + +print("Empty string:"); +dumpStringRepresentation(""); + +print("\nResult of coercion to string:"); +dumpStringRepresentation(); + +print("\ns = Simple short atom:"); +var s = "xxxxxxxx"; +dumpStringRepresentation(s); + +// Simple non-atom flat. +print("\ns + s: Non-atom flat:"); +var s2 = s + s; +dumpStringRepresentation(s2); + +print("\nNon-Latin1 flat:"); +var j = "渋谷区"; +dumpStringRepresentation(j); + +print("\nt = Non-inline atom:"); +var t = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // 31 characters +dumpStringRepresentation(t); + +print("\nr1 = t + s: Simple rope:"); +var r1 = t + s; +dumpStringRepresentation(r1); + +// Flatten that rope, and re-examine the representations of both the result and +// its former leaves. This should be an extensible string. +print("\nr1, former rope after flattening, now extensible:"); +r1.match(/x/); +dumpStringRepresentation(r1); + +print("\nt, s: Original leaves, representation unchanged:"); +dumpStringRepresentation(t); +dumpStringRepresentation(s); + +// Create a new rope with the extensible string as its left child. +print("\nr2 = r1 + s: Rope with extensible leftmost child:"); +var r2 = r1 + s; +dumpStringRepresentation(r2); + +// Flatten that; this should re-use the extensible string's buffer. +print("\nr2: flattened, stole r1's buffer:"); +r2.match(/x/); +dumpStringRepresentation(r2); + +print("\nr1: mutated into a dependent string:"); +dumpStringRepresentation(r1); + +print("\nr3 = r2 + s: a new rope with an extensible leftmost child:"); +r3 = r2 + s; +r3.match(/x/); +dumpStringRepresentation(r3); + +print("\nr2: now mutated into a dependent string"); +dumpStringRepresentation(r2); + +print("\nr1: now a doubly-dependent string, because of r2's mutation:"); +dumpStringRepresentation(r1); + +print("\nt, s: Original leaves, representation unchanged:"); +dumpStringRepresentation(t); +dumpStringRepresentation(s); diff --git a/js/src/jit-test/tests/basic/eval-scopes.js b/js/src/jit-test/tests/basic/eval-scopes.js index 12f8396e91..1a9aa63744 100644 --- a/js/src/jit-test/tests/basic/eval-scopes.js +++ b/js/src/jit-test/tests/basic/eval-scopes.js @@ -22,7 +22,7 @@ function hasGname(f, v, hasIt = true) { var x = "outer"; -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); { let x = "inner"; eval("function g() { assertEq(x, 'inner');} g()"); @@ -43,7 +43,7 @@ eval(` } hasGname(g4(), 'x'); `); -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); { let x = "inner"; @@ -70,13 +70,13 @@ eval(` hasGname(h4(), 'x', false); `); -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); with ({}) { let x = "inner"; eval("function i() { assertEq(x, 'inner');} i()"); eval("function i2() { (function nest() { assertEq(x, 'inner'); })(); } i2()"); } -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); with ({}) { let x = "inner"; @@ -84,13 +84,13 @@ with ({}) { eval("function j2() { (function nest() { assertEq(x, 'inner'); })(); } j2()"); } -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); (function () { var x = "inner"; eval("function k() { assertEq(x, 'inner');} k()"); eval("function k2() { (function nest() { assertEq(x, 'inner'); })(); } k2()"); })(); -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); (function () { let x = "inner"; @@ -114,7 +114,7 @@ eval(` (function() { assertEq(y2, 6); })() `); -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); var y3 = 5; eval(` @@ -132,4 +132,4 @@ eval(` (function() { assertEq(y4, 6); })() `); -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); diff --git a/js/src/jit-test/tests/basic/function-gname.js b/js/src/jit-test/tests/basic/function-gname.js index 9c4e5f80d7..851ca6be26 100644 --- a/js/src/jit-test/tests/basic/function-gname.js +++ b/js/src/jit-test/tests/basic/function-gname.js @@ -26,11 +26,11 @@ var f1 = new Function("assertEq(x, 'outer')"); f1(); hasGname(f1, 'x'); -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); var f2 = new Function("assertEq(x, 'outer')"); f2(); hasGname(f2, 'x'); -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); { let x = "inner"; @@ -39,11 +39,11 @@ setLazyParsingEnabled(true); hasGname(f3, 'x'); } -setLazyParsingEnabled(false); +setLazyParsingDisabled(true); { let x = "inner"; var f4 = new Function("assertEq(x, 'outer')"); f4(); hasGname(f4, 'x'); } -setLazyParsingEnabled(true); +setLazyParsingDisabled(false); diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js index e65ae49f71..3dc2869b2b 100644 --- a/js/src/jit-test/tests/modules/export-declaration.js +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -323,6 +323,10 @@ program([ ) ]).assert(Reflect.parse("export default 1234")); +assertThrowsInstanceOf(function () { + Reflect.parse("export default 1234 5678"); +}, SyntaxError); + var loc = Reflect.parse("export { a as b } from 'c'", { loc: true }).body[0].loc; diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index c337509538..90c52f91ee 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -704,13 +704,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC, scopeChain = fun->environment(); } } else { - // For global scripts without a polluted global scope the scope + // For global scripts without a non-syntactic scope the scope // chain is the script's global (Ion does not compile scripts - // with a polluted global scope). Also note that it's invalid to - // resume into the prologue in this case because the prologue - // expects the scope chain in R1 for eval and global scripts. + // with a non-syntactic global scope). Also note that it's + // invalid to resume into the prologue in this case because + // the prologue expects the scope chain in R1 for eval and + // global scripts. MOZ_ASSERT(!script->isForEval()); - MOZ_ASSERT(!script->hasPollutedGlobalScope()); + MOZ_ASSERT(!script->hasNonSyntacticScope()); scopeChain = &(script->global()); } } diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index f8ab2e8cd2..3c674aafb4 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -20,6 +20,7 @@ #endif #include "jit/SharedICHelpers.h" #include "jit/VMFunctions.h" +#include "vm/ScopeObject.h" #include "vm/TraceLogging.h" #include "jsscriptinlines.h" @@ -126,7 +127,7 @@ BaselineCompiler::compile() return Method_Error; if (fun->isNamedLambda()) { - RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); + RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, TenuredObject)); if (!declEnvObject) return Method_Error; templateScope->as().setEnclosingScope(declEnvObject); @@ -2055,7 +2056,7 @@ BaselineCompiler::emit_JSOP_IN() bool BaselineCompiler::emit_JSOP_GETGNAME() { - if (script->hasPollutedGlobalScope()) + if (script->hasNonSyntacticScope()) return emit_JSOP_GETNAME(); RootedPropertyName name(cx, script->getName(pc)); @@ -2090,7 +2091,7 @@ BaselineCompiler::emit_JSOP_GETGNAME() bool BaselineCompiler::emit_JSOP_BINDGNAME() { - if (!script->hasPollutedGlobalScope()) { + if (!script->hasNonSyntacticScope()) { frame.push(ObjectValue(script->global())); return true; } @@ -2913,7 +2914,7 @@ BaselineCompiler::emit_JSOP_IMPLICITTHIS() bool BaselineCompiler::emit_JSOP_GIMPLICITTHIS() { - if (!script->hasPollutedGlobalScope()) { + if (!script->hasNonSyntacticScope()) { frame.push(UndefinedValue()); return true; } @@ -3684,6 +3685,19 @@ BaselineCompiler::emit_JSOP_RESUME() masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1); masm.branchPtr(Assembler::BelowOrEqual, scratch1, ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret); + Register constructing = regs.takeAny(); + ValueOperand newTarget = regs.takeAnyValue(); + masm.loadValue(Address(genObj, GeneratorObject::offsetOfNewTargetSlot()), newTarget); + masm.move32(Imm32(0), constructing); + { + Label notConstructing; + masm.branchTestObject(Assembler::NotEqual, newTarget, ¬Constructing); + masm.pushValue(newTarget); + masm.move32(Imm32(CalleeToken_FunctionConstructing), constructing); + masm.bind(¬Constructing); + } + regs.add(newTarget); + // Push |undefined| for all formals. Register scratch2 = regs.takeAny(); Label loop, loopDone; @@ -3708,10 +3722,14 @@ BaselineCompiler::emit_JSOP_RESUME() masm.makeFrameDescriptor(scratch2, JitFrame_BaselineJS); masm.Push(Imm32(0)); // actual argc - masm.PushCalleeToken(callee, false); + + // Duplicate PushCalleeToken with a variable instead. + masm.orPtr(constructing, callee); + masm.Push(callee); masm.Push(scratch2); // frame descriptor regs.add(callee); + regs.add(constructing); // Load the return value. ValueOperand retVal = regs.takeAnyValue(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 2215d805e3..b5dfffeded 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -6307,7 +6307,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ attached = true; } - if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) { + if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) { if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub, scopeChain.as(), name, &attached, &isTemporarilyUnoptimizable)) { @@ -6337,7 +6337,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ if (attached) return true; - if (IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) { + if (IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) { Handle global = scopeChain.as(); if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, global, name, &attached)) return false; diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp index 6f198c4574..66e09417c0 100644 --- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -173,7 +173,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn) case JSOP_GETGNAME: case JSOP_SETGNAME: case JSOP_STRICTSETGNAME: - if (script_->hasPollutedGlobalScope()) + if (script_->hasNonSyntacticScope()) usesScopeChain_ = true; break; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 9a8898f5c4..9f10e075c4 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4649,7 +4649,7 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox* lir) bailoutFrom(&bail, lir->snapshot()); } -typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, gc::InitialHeap); +typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, NewObjectKind); static const VMFunction NewDeclEnvObjectInfo = FunctionInfo(DeclEnvObject::createTemplateObject); @@ -4663,7 +4663,7 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject* lir) // If we have a template object, we can inline call object creation. OutOfLineCode* ool = oolCallVM(NewDeclEnvObjectInfo, lir, - ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(gc::DefaultHeap)), + ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(GenericObject)), StoreRegisterTo(objReg)); bool initContents = ShouldInitFixedSlots(lir, templateObj); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 767246c028..bfbf4ad4c5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2127,12 +2127,12 @@ CheckScript(JSContext* cx, JSScript* script, bool osr) return false; } - if (script->hasPollutedGlobalScope() && !script->functionNonDelazifying()) { - // Support functions with a polluted global scope but not other + if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) { + // Support functions with a non-syntactic global scope but not other // scripts. For global scripts, IonBuilder currently uses the global // object as scope chain, this is not valid when the script has a - // polluted global scope. - TrackAndSpewIonAbort(cx, script, "has polluted global scope"); + // non-syntactic global scope. + TrackAndSpewIonAbort(cx, script, "has non-syntactic global scope"); return false; } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 4a5ad01b1f..79efd846fa 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -622,7 +622,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod type = MIRType_Undefined; break; case JSOP_GIMPLICITTHIS: - if (!script()->hasPollutedGlobalScope()) + if (!script()->hasNonSyntacticScope()) type = MIRType_Undefined; break; case JSOP_NULL: @@ -1186,10 +1186,10 @@ IonBuilder::initScopeChain(MDefinition* callee) return false; } } else { - // For global scripts without a polluted global scope, the scope chain - // is the global object. + // For global scripts without a non-syntactic global scope, the scope + // chain is the global object. MOZ_ASSERT(!script()->isForEval()); - MOZ_ASSERT(!script()->hasPollutedGlobalScope()); + MOZ_ASSERT(!script()->hasNonSyntacticScope()); scope = constant(ObjectValue(script()->global())); } @@ -1808,7 +1808,7 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_GETGNAME: { PropertyName* name = info().getAtom(pc)->asPropertyName(); - if (!script()->hasPollutedGlobalScope()) + if (!script()->hasNonSyntacticScope()) return jsop_getgname(name); return jsop_getname(name); } @@ -1817,7 +1817,7 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_STRICTSETGNAME: { PropertyName* name = info().getAtom(pc)->asPropertyName(); - if (script()->hasPollutedGlobalScope()) + if (script()->hasNonSyntacticScope()) return jsop_setprop(name); JSObject* obj = &script()->global(); return setStaticName(obj, name); @@ -1836,7 +1836,7 @@ IonBuilder::inspectOpcode(JSOp op) } case JSOP_BINDGNAME: - if (!script()->hasPollutedGlobalScope()) + if (!script()->hasNonSyntacticScope()) return pushConstant(ObjectValue(script()->global())); // Fall through to JSOP_BINDNAME case JSOP_BINDNAME: @@ -1983,7 +1983,7 @@ IonBuilder::inspectOpcode(JSOp op) return jsop_debugger(); case JSOP_GIMPLICITTHIS: - if (!script()->hasPollutedGlobalScope()) + if (!script()->hasNonSyntacticScope()) return pushConstant(UndefinedValue()); // Just fall through to the unsupported bytecode case. @@ -7629,7 +7629,7 @@ bool IonBuilder::jsop_getname(PropertyName* name) { MDefinition* object; - if (IsGlobalOp(JSOp(*pc)) && !script()->hasPollutedGlobalScope()) { + if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope()) { MInstruction* global = constant(ObjectValue(script()->global())); object = global; } else { diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 71bb6d26bf..74b83c2a17 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -2522,7 +2522,7 @@ InlineFrameIterator::computeScopeChain(Value scopeChainValue, MaybeReadFallback& // Ion does not handle non-function scripts that have anything other than // the global on their scope chain. MOZ_ASSERT(!script()->isForEval()); - MOZ_ASSERT(!script()->hasPollutedGlobalScope()); + MOZ_ASSERT(!script()->hasNonSyntacticScope()); return &script()->global(); } diff --git a/js/src/js.msg b/js/src/js.msg index 2425799243..58f27c763a 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -101,6 +101,7 @@ MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can' MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}") MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null") MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|") +MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS, 1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors") // JSON MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b6ddac4de0..fd57f365a3 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3295,12 +3295,22 @@ JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id } static bool -CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, - MutableHandleObject dynamicScopeObj, - MutableHandleObject staticScopeObj) +CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain, + MutableHandleObject dynamicScopeObj, + MutableHandle staticScopeObj) { - return js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(), - dynamicScopeObj, staticScopeObj); + if (!js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(), dynamicScopeObj)) + return false; + + if (scopeChain.empty()) { + staticScopeObj.set(nullptr); + } else { + staticScopeObj.set(StaticNonSyntacticScopeObjects::create(cx, nullptr)); + if (!staticScopeObj) + return false; + } + + return true; } static bool @@ -3311,11 +3321,23 @@ IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope) // If a function was compiled to be lexically nested inside some other // script, we cannot clone it without breaking the compiler's assumptions. - JSObject* scope = fun->nonLazyScript()->enclosingStaticScope(); - if (scope && (!scope->is() || - scope->as().isDirect() || - scope->as().isStrict())) - { + if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) { + // If the script already deals with a non-syntactic scope, we can clone + // it. + if (scope->is()) + return true; + + // If the script is an indirect eval that is immediately scoped under + // the global, we can clone it. + if (scope->is() && + !scope->as().isDirect() && + !scope->as().isStrict()) + { + return true; + } + + // Any other enclosing static scope (e.g., function, block) cannot be + // cloned. return false; } @@ -3323,7 +3345,8 @@ IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope) } static JSObject* -CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope) +CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope, + Handle staticScope) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); @@ -3360,7 +3383,21 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScop return nullptr; } - return CloneFunctionObject(cx, fun, dynamicScope, fun->getAllocKind()); + if (CanReuseScriptForClone(cx->compartment(), fun, dynamicScope)) { + // If the script is to be reused, either the script can already handle + // non-syntactic scopes, or there is no new static scope. +#ifdef DEBUG + // Fail here if we OOM during debug asserting. + // CloneFunctionReuseScript will delazify the script anyways, so we + // are not creating an extra failure condition for DEBUG builds. + if (!fun->getOrCreateScript(cx)) + return nullptr; + MOZ_ASSERT(!staticScope || fun->nonLazyScript()->hasNonSyntacticScope()); +#endif + return CloneFunctionReuseScript(cx, fun, dynamicScope, fun->getAllocKind()); + } + + return CloneFunctionAndScript(cx, fun, dynamicScope, staticScope, fun->getAllocKind()); } namespace JS { @@ -3368,18 +3405,18 @@ namespace JS { JS_PUBLIC_API(JSObject*) CloneFunctionObject(JSContext* cx, JS::Handle funobj) { - return CloneFunctionObject(cx, funobj, cx->global()); + return CloneFunctionObject(cx, funobj, cx->global(), /* staticScope = */ nullptr); } extern JS_PUBLIC_API(JSObject*) CloneFunctionObject(JSContext* cx, HandleObject funobj, AutoObjectVector& scopeChain) { RootedObject dynamicScope(cx); - RootedObject unusedStaticScope(cx); - if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope)) + Rooted staticScope(cx); + if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope)) return nullptr; - return CloneFunctionObject(cx, funobj, dynamicScope); + return CloneFunctionObject(cx, funobj, dynamicScope, staticScope); } } // namespace JS @@ -3853,31 +3890,40 @@ JS::CompileOptions::CompileOptions(JSContext* cx, JSVersion version) asmJSOption = cx->runtime()->options().asmJS(); } -bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions &options, - SourceBufferHolder& srcBuf, MutableHandleScript script) +enum SyntacticScopeOption { HasSyntacticScope, HasNonSyntacticScope }; + +static bool +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption, + SourceBufferHolder& srcBuf, MutableHandleScript script) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); AutoLastFrameCheck lfc(cx); + Rooted staticScope(cx); + if (scopeOption == HasNonSyntacticScope) { + staticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr); + if (!staticScope) + return false; + } + script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), cx->global(), - nullptr, nullptr, options, srcBuf)); + staticScope, nullptr, options, srcBuf)); return !!script; } -bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions &options, - const char16_t* chars, size_t length, MutableHandleScript script) +static bool +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption, + const char16_t* chars, size_t length, MutableHandleScript script) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); - return Compile(cx, options, srcBuf, script); + return ::Compile(cx, options, scopeOption, srcBuf, script); } -bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions &options, - const char* bytes, size_t length, MutableHandleScript script) +static bool +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption, + const char* bytes, size_t length, MutableHandleScript script) { mozilla::UniquePtr chars; if (options.utf8) @@ -3887,30 +3933,101 @@ JS::Compile(JSContext* cx, const ReadOnlyCompileOptions &options, if (!chars) return false; - return Compile(cx, options, chars.get(), length, script); + return ::Compile(cx, options, scopeOption, chars.get(), length, script); } -bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* fp, - MutableHandleScript script) +static bool +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption, + FILE* fp, MutableHandleScript script) { FileContents buffer(cx); if (!ReadCompleteFile(cx, fp, buffer)) return false; - return Compile(cx, options, buffer.begin(), buffer.length(), script); + return ::Compile(cx, options, scopeOption, buffer.begin(), buffer.length(), script); } -bool -JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* filename, - MutableHandleScript script) +static bool +Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, SyntacticScopeOption scopeOption, + const char* filename, MutableHandleScript script) { AutoFile file; if (!file.open(cx, filename)) return false; CompileOptions options(cx, optionsArg); options.setFileAndLine(filename, 1); - return Compile(cx, options, file.fp(), script); + return ::Compile(cx, options, scopeOption, file.fp(), script); +} + +bool +JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + SourceBufferHolder& srcBuf, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasSyntacticScope, srcBuf, script); +} + +bool +JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasSyntacticScope, bytes, length, script); +} + +bool +JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasSyntacticScope, chars, length, script); +} + +bool +JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + FILE* file, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasSyntacticScope, file, script); +} + +bool +JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* filename, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasSyntacticScope, filename, script); +} + +bool +JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + SourceBufferHolder& srcBuf, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasNonSyntacticScope, srcBuf, script); +} + +bool +JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasNonSyntacticScope, bytes, length, script); +} + +bool +JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, + JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasNonSyntacticScope, chars, length, script); +} + +bool +JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + FILE* file, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasNonSyntacticScope, file, script); +} + +bool +JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* filename, JS::MutableHandleScript script) +{ + return ::Compile(cx, options, HasNonSyntacticScope, filename, script); } JS_PUBLIC_API(bool) @@ -4093,14 +4210,17 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, if (!fun) return false; - // Make sure to handle cases when we have a polluted scopechain. - CompileOptions options(cx, optionsArg); - if (!enclosingDynamicScope->is()) - options.setHasPollutedScope(true); + // Make sure the static scope chain matches up when we have a + // non-syntactic scope. + MOZ_ASSERT_IF(!enclosingDynamicScope->is(), + HasNonSyntacticStaticScopeChain(enclosingStaticScope)); + CompileOptions options(cx, optionsArg); if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingStaticScope)) + { return false; + } return true; } @@ -4112,8 +4232,8 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain, SourceBufferHolder& srcBuf, MutableHandleFunction fun) { RootedObject dynamicScopeObj(cx); - RootedObject staticScopeObj(cx); - if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScopeObj, &staticScopeObj)) + Rooted staticScopeObj(cx); + if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScopeObj, &staticScopeObj)) return false; return CompileFunction(cx, options, name, nargs, argnames, @@ -4187,33 +4307,34 @@ JS_DecompileFunctionBody(JSContext* cx, HandleFunction fun, unsigned indent) } MOZ_NEVER_INLINE static bool -ExecuteScript(JSContext* cx, HandleObject obj, HandleScript scriptArg, jsval* rval) +ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, jsval* rval) { - RootedScript script(cx, scriptArg); - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, scriptArg); - - if (!script->hasPollutedGlobalScope() && !obj->is()) { - script = CloneScript(cx, nullptr, nullptr, script, HasPollutedGlobalScope); - if (!script) - return false; - js::Debugger::onNewScript(cx, script); - } + assertSameCompartment(cx, scope, script); + MOZ_ASSERT_IF(!scope->is(), script->hasNonSyntacticScope()); AutoLastFrameCheck lfc(cx); - return Execute(cx, script, *obj, rval); + return Execute(cx, script, *scope, rval); } static bool ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptArg, jsval* rval) { RootedObject dynamicScope(cx); - RootedObject unusedStaticScope(cx); - if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope)) + Rooted staticScope(cx); + if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope)) return false; - return ExecuteScript(cx, dynamicScope, scriptArg, rval); + + RootedScript script(cx, scriptArg); + if (!script->hasNonSyntacticScope() && !dynamicScope->is()) { + script = CloneGlobalScript(cx, staticScope, script); + if (!script) + return false; + js::Debugger::onNewScript(cx, script); + } + + return ExecuteScript(cx, dynamicScope, script, rval); } MOZ_NEVER_INLINE JS_PUBLIC_API(bool) @@ -4247,7 +4368,7 @@ JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg) CHECK_REQUEST(cx); RootedScript script(cx, scriptArg); if (script->compartment() != cx->compartment()) { - script = CloneScript(cx, nullptr, nullptr, script); + script = CloneGlobalScript(cx, /* enclosingScope = */ nullptr, script); if (!script) return false; @@ -4259,7 +4380,8 @@ JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg) static const unsigned LARGE_SCRIPT_LENGTH = 500*1024; static bool -Evaluate(JSContext* cx, HandleObject scope, const ReadOnlyCompileOptions& optionsArg, +Evaluate(JSContext* cx, HandleObject scope, Handle staticScope, + const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { CompileOptions options(cx, optionsArg); @@ -4270,12 +4392,14 @@ Evaluate(JSContext* cx, HandleObject scope, const ReadOnlyCompileOptions& option AutoLastFrameCheck lfc(cx); - options.setHasPollutedScope(!scope->is()); + MOZ_ASSERT_IF(!scope->is(), HasNonSyntacticStaticScopeChain(staticScope)); + options.setIsRunOnce(true); SourceCompressionTask sct(cx); RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), - scope, nullptr, nullptr, options, - srcBuf, nullptr, 0, &sct)); + scope, staticScope, + /* evalCaller = */ nullptr, options, + srcBuf, /* source = */ nullptr, 0, &sct)); if (!script) return false; @@ -4305,10 +4429,10 @@ Evaluate(JSContext* cx, AutoObjectVector& scopeChain, const ReadOnlyCompileOptio SourceBufferHolder& srcBuf, MutableHandleValue rval) { RootedObject dynamicScope(cx); - RootedObject unusedStaticScope(cx); - if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope)) + Rooted staticScope(cx); + if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope)) return false; - return ::Evaluate(cx, dynamicScope, optionsArg, srcBuf, rval); + return ::Evaluate(cx, dynamicScope, staticScope, optionsArg, srcBuf, rval); } static bool @@ -4316,7 +4440,7 @@ Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char16_t* chars, size_t length, MutableHandleValue rval) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); - return ::Evaluate(cx, cx->global(), optionsArg, srcBuf, rval); + return ::Evaluate(cx, cx->global(), nullptr, optionsArg, srcBuf, rval); } extern JS_PUBLIC_API(bool) @@ -4332,7 +4456,7 @@ JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options, return false; SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership); - bool ok = ::Evaluate(cx, cx->global(), options, srcBuf, rval); + bool ok = ::Evaluate(cx, cx->global(), nullptr, options, srcBuf, rval); return ok; } @@ -4356,7 +4480,7 @@ JS_PUBLIC_API(bool) JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { - return ::Evaluate(cx, cx->global(), optionsArg, srcBuf, rval); + return ::Evaluate(cx, cx->global(), nullptr, optionsArg, srcBuf, rval); } JS_PUBLIC_API(bool) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 61c98ffcae..f8a63d4932 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2269,6 +2269,7 @@ class JS_PUBLIC_API(CompartmentOptions) , invisibleToDebugger_(false) , mergeable_(false) , discardSource_(false) + , disableLazyParsing_(false) , cloneSingletons_(false) , traceGlobal_(nullptr) , singletonsAsTemplates_(true) @@ -2313,6 +2314,12 @@ class JS_PUBLIC_API(CompartmentOptions) return *this; } + bool disableLazyParsing() const { return disableLazyParsing_; } + CompartmentOptions& setDisableLazyParsing(bool flag) { + disableLazyParsing_ = flag; + return *this; + } + bool cloneSingletons() const { return cloneSingletons_; } CompartmentOptions& setCloneSingletons(bool flag) { cloneSingletons_ = flag; @@ -2365,6 +2372,7 @@ class JS_PUBLIC_API(CompartmentOptions) bool invisibleToDebugger_; bool mergeable_; bool discardSource_; + bool disableLazyParsing_; bool cloneSingletons_; Override extraWarningsOverride_; union { @@ -3430,7 +3438,6 @@ class JS_FRIEND_API(ReadOnlyCompileOptions) utf8(false), lineno(1), column(0), - hasPollutedGlobalScope(false), isRunOnce(false), forEval(false), noScriptRval(false), @@ -3470,7 +3477,6 @@ class JS_FRIEND_API(ReadOnlyCompileOptions) bool utf8; unsigned lineno; unsigned column; - bool hasPollutedGlobalScope; // isRunOnce only applies to non-function scripts. bool isRunOnce; bool forEval; @@ -3563,7 +3569,6 @@ class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions } OwningCompileOptions& setUTF8(bool u) { utf8 = u; return *this; } OwningCompileOptions& setColumn(unsigned c) { column = c; return *this; } - OwningCompileOptions& setHasPollutedScope(bool p) { hasPollutedGlobalScope = p; return *this; } OwningCompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; } OwningCompileOptions& setForEval(bool eval) { forEval = eval; return *this; } OwningCompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; } @@ -3647,7 +3652,6 @@ class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOpti } CompileOptions& setUTF8(bool u) { utf8 = u; return *this; } CompileOptions& setColumn(unsigned c) { column = c; return *this; } - CompileOptions& setHasPollutedScope(bool p) { hasPollutedGlobalScope = p; return *this; } CompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; } CompileOptions& setForEval(bool eval) { forEval = eval; return *this; } CompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; } @@ -3679,24 +3683,44 @@ class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOpti * |script| will always be set. On failure, it will be set to nullptr. */ extern JS_PUBLIC_API(bool) -Compile(JSContext *cx, const ReadOnlyCompileOptions &options, +Compile(JSContext* cx, const ReadOnlyCompileOptions &options, SourceBufferHolder& srcBuf, JS::MutableHandleScript script); extern JS_PUBLIC_API(bool) -Compile(JSContext *cx, const ReadOnlyCompileOptions &options, +Compile(JSContext* cx, const ReadOnlyCompileOptions &options, const char* bytes, size_t length, JS::MutableHandleScript script); extern JS_PUBLIC_API(bool) -Compile(JSContext *cx, const ReadOnlyCompileOptions &options, +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, JS::MutableHandleScript script); extern JS_PUBLIC_API(bool) -Compile(JSContext *cx, const ReadOnlyCompileOptions &options, FILE *file, - JS::MutableHandleScript script); +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + FILE* file, JS::MutableHandleScript script); extern JS_PUBLIC_API(bool) -Compile(JSContext *cx, const ReadOnlyCompileOptions &options, const char *filename, - JS::MutableHandleScript script); +Compile(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* filename, JS::MutableHandleScript script); + +extern JS_PUBLIC_API(bool) +CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + SourceBufferHolder& srcBuf, JS::MutableHandleScript script); + +extern JS_PUBLIC_API(bool) +CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* bytes, size_t length, JS::MutableHandleScript script); + +extern JS_PUBLIC_API(bool) +CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, JS::MutableHandleScript script); + +extern JS_PUBLIC_API(bool) +CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + FILE* file, JS::MutableHandleScript script); + +extern JS_PUBLIC_API(bool) +CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options, + const char* filename, JS::MutableHandleScript script); extern JS_PUBLIC_API(bool) CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4976098272..1e96b78159 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -666,19 +666,6 @@ typedef JS::AutoVectorRooter AutoStringVector; typedef JS::AutoVectorRooter AutoPropertyNameVector; typedef JS::AutoVectorRooter AutoShapeVector; -class AutoObjectObjectHashMap : public AutoHashMapRooter -{ - public: - explicit AutoObjectObjectHashMap(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoHashMapRooter(cx, OBJOBJHASHMAP) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - class AutoObjectUnsigned32HashMap : public AutoHashMapRooter { public: @@ -692,19 +679,6 @@ class AutoObjectUnsigned32HashMap : public AutoHashMapRooter -{ - public: - explicit AutoObjectHashSet(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoHashSetRooter(cx, OBJHASHSET) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - /* AutoArrayRooter roots an external array of Values. */ class AutoArrayRooter : private JS::AutoGCRooter { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index ec372443ad..397d9e74dc 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -35,6 +35,7 @@ #include "jit/JitFrameIterator.h" #include "js/CallNonGenericMethod.h" #include "js/Proxy.h" +#include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/Shape.h" @@ -516,7 +517,7 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, Han HasSingletonType = 0x8 }; - /* NB: Keep this in sync with CloneFunctionAndScript. */ + /* NB: Keep this in sync with CloneInnerInterpretedFunction. */ RootedAtom atom(xdr->cx()); uint32_t firstword = 0; /* bitmask of FirstWordFlag */ uint32_t flagsword = 0; /* word for argument count and fun->flags */ @@ -606,10 +607,9 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, Han fun->setFlags(uint16_t(flagsword)); fun->initAtom(atom); if (firstword & IsLazy) { - fun->initLazyScript(lazy); + MOZ_ASSERT(fun->lazyScript() == lazy); } else { - fun->initScript(script); - script->setFunction(fun); + MOZ_ASSERT(fun->nonLazyScript() == script); MOZ_ASSERT(fun->nargs() == script->bindings.numArgs()); } @@ -623,48 +623,10 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, Han } template bool -js::XDRInterpretedFunction(XDRState *, HandleObject, HandleScript, MutableHandleFunction); +js::XDRInterpretedFunction(XDRState*, HandleObject, HandleScript, MutableHandleFunction); template bool -js::XDRInterpretedFunction(XDRState *, HandleObject, HandleScript, MutableHandleFunction); - -JSObject * -js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun, - PollutedGlobalScopeOption polluted) -{ - /* NB: Keep this in sync with XDRInterpretedFunction. */ - RootedObject cloneProto(cx); - if (srcFun->isStarGenerator()) { - cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); - if (!cloneProto) - return nullptr; - } - - gc::AllocKind allocKind = srcFun->getAllocKind(); - RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0, - JSFunction::INTERPRETED, nullptr, nullptr, - cloneProto, allocKind, TenuredObject)); - if (!clone) - return nullptr; - - JSScript::AutoDelazify srcScript(cx, srcFun); - if (!srcScript) - return nullptr; - RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript, polluted)); - if (!clonedScript) - return nullptr; - - clone->setArgCount(srcFun->nargs()); - clone->setFlags(srcFun->flags()); - clone->initAtom(srcFun->displayAtom()); - clone->initScript(clonedScript); - clonedScript->setFunction(clone); - if (!JSFunction::setTypeForScriptedFunction(cx, clone)) - return nullptr; - - RootedScript cloneScript(cx, clone->nonLazyScript()); - return clone; -} +js::XDRInterpretedFunction(XDRState*, HandleObject, HandleScript, MutableHandleFunction); /* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */ bool @@ -758,7 +720,7 @@ js::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, MutableHandleValue v * if v is an object) returning true if .prototype is found. */ static bool -fun_hasInstance(JSContext *cx, HandleObject objArg, MutableHandleValue v, bool *bp) +fun_hasInstance(JSContext* cx, HandleObject objArg, MutableHandleValue v, bool* bp) { RootedObject obj(cx, objArg); @@ -825,8 +787,8 @@ ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) return false; } -static JSObject * -CreateFunctionConstructor(JSContext *cx, JSProtoKey key) +static JSObject* +CreateFunctionConstructor(JSContext* cx, JSProtoKey key) { Rooted global(cx, cx->global()); RootedObject functionProto(cx, &global->getPrototype(JSProto_Function).toObject()); @@ -843,7 +805,7 @@ CreateFunctionConstructor(JSContext *cx, JSProtoKey key) } static JSObject* -CreateFunctionPrototype(JSContext *cx, JSProtoKey key) +CreateFunctionPrototype(JSContext* cx, JSProtoKey key) { Rooted self(cx, cx->global()); @@ -906,7 +868,7 @@ CreateFunctionPrototype(JSContext *cx, JSProtoKey key) /* * The default 'new' group of Function.prototype is required by type * inference to have unknown properties, to simplify handling of e.g. - * CloneFunctionObject. + * NewFunctionClone. */ if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto)) return nullptr; @@ -1515,16 +1477,13 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti if (script) { RootedObject enclosingScope(cx, lazy->enclosingScope()); - RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script)); + RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script)); if (!clonedScript) return false; clonedScript->setSourceObject(lazy->sourceObject()); fun->initAtom(script->functionNonDelazifying()->displayAtom()); - clonedScript->setFunction(fun); - - fun->setUnlazifiedScript(clonedScript); if (!lazy->maybeScript()) lazy->initScript(clonedScript); @@ -1542,8 +1501,15 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti const char16_t* lazyStart = chars + lazy->begin(); size_t lazyLength = lazy->end() - lazy->begin(); - if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) + if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) { + // The frontend may have linked the function and the non-lazy + // script together during bytecode compilation. Reset it now on + // error. + fun->initLazyScript(lazy); + if (lazy->maybeScriptUnbarriered()) + lazy->resetScript(); return false; + } script = fun->nonLazyScript(); @@ -2164,8 +2130,8 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native, } bool -js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun, - HandleObject newParent) +js::CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun, + HandleObject newParent) { if (compartment != fun->compartment() || fun->isSingleton() || @@ -2186,48 +2152,40 @@ js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction return true; // We need to clone the script if we're interpreted and not already marked - // as having a polluted scope. If we're lazy, go ahead and clone the - // script; see the big comment at the end of CloneScript for the explanation - // of what's going on there. + // as having a non-syntactic scope. If we're lazy, go ahead and clone the + // script; see the big comment at the end of CopyScriptInternal for the + // explanation of what's going on there. return !fun->isInterpreted() || - (fun->hasScript() && fun->nonLazyScript()->hasPollutedGlobalScope()); + (fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope()); } -JSFunction * -js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, - gc::AllocKind allocKind, - NewObjectKind newKindArg /* = GenericObject */, - HandleObject proto) +static bool +FunctionCloneScopeIsWellFormed(JSContext* cx, HandleObject parent) { MOZ_ASSERT(parent); - MOZ_ASSERT(!fun->isBoundFunction()); + RootedObject realParent(cx, SkipScopeParent(parent)); + // We'd like to assert that realParent is null-or-global, but + // js::ExecuteInGlobalAndReturnScope and debugger eval bits mess that up. + // Assert that it's one of those or a debug scope proxy or the unqualified + // var obj, since it should still be ok to parent to the global in that + // case. + return !realParent || realParent == cx->global() || + realParent->is() || + realParent->isUnqualifiedVarObj(); +} - bool useSameScript = CloneFunctionObjectUseSameScript(cx->compartment(), fun, parent); - - JSScript::AutoDelazify funScript(cx); - if (!useSameScript && fun->isInterpretedLazy()) { - funScript = fun; - if (!funScript) - return nullptr; - } - - NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject; +static inline JSFunction* +NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind, + gc::AllocKind allocKind, HandleObject proto) +{ RootedObject cloneProto(cx, proto); - if (!cloneProto && fun->isStarGenerator()) { + if (!proto && fun->isStarGenerator()) { cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!cloneProto) return nullptr; } -#ifdef DEBUG - RootedObject realParent(cx, SkipScopeParent(parent)); - // We'd like to assert that realParent is null-or-global, but - // js::ExecuteInGlobalAndReturnScope messes that up. Assert that it's one - // of those or the unqualified var obj, since it should still be ok to - // parent to the global in that case. - MOZ_ASSERT(!realParent || realParent == cx->global() || - realParent->isUnqualifiedVarObj()); -#endif - JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto, + + JSObject* cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto, allocKind, newKind); if (!cloneobj) return nullptr; @@ -2241,18 +2199,6 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, clone->setArgCount(fun->nargs()); clone->setFlags(flags); - if (fun->hasScript()) { - clone->initScript(fun->nonLazyScript()); - clone->initEnvironment(parent); - } else if (fun->isInterpretedLazy()) { - MOZ_ASSERT(fun->compartment() == clone->compartment()); - MOZ_ASSERT(useSameScript); - LazyScript* lazy = fun->lazyScriptOrNull(); - clone->initLazyScript(lazy); - clone->initEnvironment(parent); - } else { - clone->initNative(fun->native(), fun->jitInfo()); - } clone->initAtom(fun->displayAtom()); if (allocKind == AllocKind::FUNCTION_EXTENDED) { @@ -2264,34 +2210,103 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, } } - if (useSameScript) { - /* - * Clone the function, reusing its script. We can use the same group as - * the original function provided that its prototype is correct. - */ - if (fun->getProto() == clone->getProto()) - clone->setGroup(fun->group()); - return clone; - } + return clone; +} - RootedFunction cloneRoot(cx, clone); +JSFunction* +js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent, + gc::AllocKind allocKind /* = FUNCTION */ , + NewObjectKind newKind /* = GenericObject */, + HandleObject proto /* = nullptr */) +{ + MOZ_ASSERT(FunctionCloneScopeIsWellFormed(cx, parent)); + MOZ_ASSERT(!fun->isBoundFunction()); + MOZ_ASSERT(CanReuseScriptForClone(cx->compartment(), fun, parent)); + + RootedFunction clone(cx, NewFunctionClone(cx, fun, newKind, allocKind, proto)); + if (!clone) + return nullptr; + + if (fun->hasScript()) { + clone->initScript(fun->nonLazyScript()); + clone->initEnvironment(parent); + } else if (fun->isInterpretedLazy()) { + MOZ_ASSERT(fun->compartment() == clone->compartment()); + LazyScript* lazy = fun->lazyScriptOrNull(); + clone->initLazyScript(lazy); + clone->initEnvironment(parent); + } else { + clone->initNative(fun->native(), fun->jitInfo()); + } /* - * Across compartments or if we have to introduce a polluted scope we have - * to clone the script for interpreted functions. Cross-compartment cloning - * only happens via JSAPI (JS::CloneFunctionObject) which dynamically - * ensures that 'script' has no enclosing lexical scope (only the global - * scope or other non-lexical scope). + * Clone the function, reusing its script. We can use the same group as + * the original function provided that its prototype is correct. */ - PollutedGlobalScopeOption globalScopeOption = parent->is() ? - HasCleanGlobalScope : HasPollutedGlobalScope; - if (cloneRoot->isInterpreted() && - !CloneFunctionScript(cx, fun, cloneRoot, globalScopeOption, newKindArg)) - { + if (fun->getProto() == clone->getProto()) + clone->setGroup(fun->group()); + return clone; +} + +JSFunction* +js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, + HandleObject newStaticScope, + gc::AllocKind allocKind /* = FUNCTION */, + HandleObject proto /* = nullptr */) +{ + MOZ_ASSERT(FunctionCloneScopeIsWellFormed(cx, parent)); + MOZ_ASSERT(!fun->isBoundFunction()); + + RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto)); + if (!clone) return nullptr; + + if (fun->hasScript()) { + clone->initScript(nullptr); + clone->initEnvironment(parent); + } else { + clone->initNative(fun->native(), fun->jitInfo()); } - return cloneRoot; + /* + * Across compartments or if we have to introduce a non-syntactic scope we + * have to clone the script for interpreted functions. Cross-compartment + * cloning only happens via JSAPI (JS::CloneFunctionObject) which + * dynamically ensures that 'script' has no enclosing lexical scope (only + * the global scope or other non-lexical scope). + */ +#ifdef DEBUG + RootedObject terminatingScope(cx, parent); + while (IsSyntacticScope(terminatingScope)) + terminatingScope = terminatingScope->enclosingScope(); + MOZ_ASSERT_IF(!terminatingScope->is(), + HasNonSyntacticStaticScopeChain(newStaticScope)); +#endif + + if (clone->isInterpreted()) { + // The self-hosting compartment is shared across processes, and + // AutoDelazify enters fun->compartment(). We would get races if the + // self-hosting compartment has lazy interpreted functions. + MOZ_ASSERT_IF(fun->compartment()->isSelfHosting, !fun->isInterpretedLazy()); + JSScript::AutoDelazify funScript(cx); + if (fun->isInterpretedLazy()) { + funScript = fun; + if (!funScript) + return nullptr; + } + + RootedScript script(cx, fun->nonLazyScript()); + MOZ_ASSERT(script->compartment() == fun->compartment()); + MOZ_ASSERT(cx->compartment() == clone->compartment(), + "Otherwise we could relazify clone below!"); + + RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newStaticScope, clone, script)); + if (!clonedScript) + return nullptr; + Debugger::onNewScript(cx, clonedScript); + } + + return clone; } /* diff --git a/js/src/jsfun.h b/js/src/jsfun.h index a026e0c781..02b09e245e 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -658,14 +658,20 @@ class FunctionExtended : public JSFunction }; extern bool -CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction fun, - HandleObject newParent); +CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun, HandleObject newParent); extern JSFunction* -CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent, - gc::AllocKind kind = gc::AllocKind::FUNCTION, - NewObjectKind newKindArg = GenericObject, - HandleObject proto = nullptr); +CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent, + gc::AllocKind kind = gc::AllocKind::FUNCTION, + NewObjectKind newKindArg = GenericObject, + HandleObject proto = nullptr); + +// Functions whose scripts are cloned are always given singleton types. +extern JSFunction* +CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, + HandleObject newStaticScope, + gc::AllocKind kind = gc::AllocKind::FUNCTION, + HandleObject proto = nullptr); extern bool FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart, @@ -727,10 +733,6 @@ bool XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, MutableHandleFunction objp); -extern JSObject * -CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, - PollutedGlobalScopeOption polluted); - /* * Report an error that call.thisv is not compatible with the specified class, * assuming that the method (clasp->name).prototype. diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 0b686b6aad..eacc34368e 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -85,7 +85,12 @@ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObjec gc::AllocKind kind = fun->isExtended() ? extendedFinalizeKind : finalizeKind; - return CloneFunctionObject(cx, fun, parent, kind, newKind, proto); + + if (CanReuseScriptForClone(cx->compartment(), fun, parent)) + return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto); + + RootedObject staticScope(cx, fun->getOrCreateScript(cx)->enclosingStaticScope()); + return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto); } } /* namespace js */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ab0859887a..d0e4405d21 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -670,7 +670,7 @@ NewObjectGCKind(const js::Class* clasp) static inline JSObject* NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind, - NewObjectKind newKind) + NewObjectKind newKind, uint32_t initialShapeFlags = 0) { const Class* clasp = group->clasp(); @@ -685,7 +685,8 @@ NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind, ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp) : GetGCKindSlots(kind, clasp); - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed)); + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed, + initialShapeFlags)); if (!shape) return nullptr; @@ -733,7 +734,8 @@ NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle JSObject* js::NewObjectWithGivenTaggedProto(ExclusiveContext* cxArg, const Class* clasp, Handle proto, - gc::AllocKind allocKind, NewObjectKind newKind) + gc::AllocKind allocKind, NewObjectKind newKind, + uint32_t initialShapeFlags) { if (CanBeFinalizedInBackground(allocKind, clasp)) allocKind = GetBackgroundAllocKind(allocKind); @@ -755,7 +757,7 @@ js::NewObjectWithGivenTaggedProto(ExclusiveContext* cxArg, const Class* clasp, if (!group) return nullptr; - RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind)); + RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind, initialShapeFlags)); if (!obj) return nullptr; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index cb52ff3ddd..a213816bd5 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -420,10 +420,10 @@ class JSObject : public js::gc::Cell * slot of the object. For other scope objects, the chain goes directly to * the global. * - * In code which is not marked hasPollutedGlobalScope, scope chains can + * In code which is not marked hasNonSyntacticScope, scope chains can * contain only syntactic scope objects (see IsSyntacticScope) with a global * object at the root as the scope of the outermost non-function script. In - * hasPollutedGlobalScope code, the scope of the outermost non-function + * hasNonSyntacticScope code, the scope of the outermost non-function * script might not be a global object, and can have a mix of other objects * above it before the global object is reached. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 9201b8ccfa..b91dc47ed4 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -624,25 +624,38 @@ typedef AutoVectorRooter AutoPropertyDescriptorVector; */ JSObject* NewObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp, Handle proto, - gc::AllocKind allocKind, NewObjectKind newKind); + gc::AllocKind allocKind, NewObjectKind newKind, + uint32_t initialShapeFlags = 0); inline JSObject* NewObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp, Handle proto, - NewObjectKind newKind = GenericObject) + NewObjectKind newKind = GenericObject, + uint32_t initialShapeFlags = 0) { gc::AllocKind allocKind = gc::GetGCObjectKind(clasp); - return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind); + return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind, initialShapeFlags); } template inline T* NewObjectWithGivenTaggedProto(ExclusiveContext* cx, Handle proto, - NewObjectKind newKind = GenericObject) + NewObjectKind newKind = GenericObject, + uint32_t initialShapeFlags = 0) { - JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto, newKind); + JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto, newKind, + initialShapeFlags); return obj ? &obj->as() : nullptr; } +template +inline T* +NewObjectWithNullTaggedProto(ExclusiveContext* cx, NewObjectKind newKind = GenericObject, + uint32_t initialShapeFlags = 0) +{ + Rooted nullProto(cx, TaggedProto(nullptr)); + return NewObjectWithGivenTaggedProto(cx, nullProto, newKind, initialShapeFlags); +} + inline JSObject* NewObjectWithGivenProto(ExclusiveContext* cx, const Class* clasp, HandleObject proto, gc::AllocKind allocKind, NewObjectKind newKind) diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 0e313dfc2f..b260853930 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -272,9 +272,7 @@ class JS_PUBLIC_API(AutoGCRooter) IONMASM = -19, /* js::jit::MacroAssembler */ WRAPVECTOR = -20, /* js::AutoWrapperVector */ WRAPPER = -21, /* js::AutoWrapperRooter */ - OBJOBJHASHMAP=-22, /* js::AutoObjectObjectHashMap */ OBJU32HASHMAP=-23, /* js::AutoObjectUnsigned32HashMap */ - OBJHASHSET = -24, /* js::AutoObjectHashSet */ JSONPARSER = -25, /* js::JSONParser */ CUSTOM = -26 /* js::CustomAutoRooter */ }; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 3d77dcb060..fcbb2a539e 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -563,10 +563,10 @@ enum XDRClassKind { template bool -js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, +js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp) { - /* NB: Keep this in sync with CloneScript. */ + /* NB: Keep this in sync with CopyScript. */ enum ScriptBits { NoScriptRval, @@ -587,7 +587,7 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc HasSingleton, TreatAsRunOnce, HasLazyScript, - HasPollutedGlobalScope, + HasNonSyntacticScope, }; uint32_t length, lineno, column, nslots, staticLevel; @@ -600,6 +600,7 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc JSContext* cx = xdr->cx(); RootedScript script(cx); + RootedObject enclosingScope(cx, enclosingScopeArg); natoms = nsrcnotes = 0; nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0; @@ -719,8 +720,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc scriptBits |= (1 << TreatAsRunOnce); if (script->isRelazifiable()) scriptBits |= (1 << HasLazyScript); - if (script->hasPollutedGlobalScope()) - scriptBits |= (1 << HasPollutedGlobalScope); + if (script->hasNonSyntacticScope()) + scriptBits |= (1 << HasNonSyntacticScope); } if (!xdr->codeUint32(&prologueLength)) @@ -788,10 +789,26 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc MOZ_ASSERT(enclosingScript->sourceObject()->is()); sourceObject = &enclosingScript->sourceObject()->as(); } + + // If the outermost script has a non-syntactic scope, reflect that on + // the static scope chain. + if (scriptBits & (1 << HasNonSyntacticScope) && !enclosingScope) { + enclosingScope = StaticNonSyntacticScopeObjects::create(cx, nullptr); + if (!enclosingScope) + return false; + } + script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)), options, /* staticLevel = */ 0, sourceObject, 0, 0); if (!script) return false; + + // Set the script in its function now so that inner scripts to be + // decoded may iterate the static scope chain. + if (fun) { + fun->initScript(script); + script->setFunction(fun); + } } /* JSScript::partiallyInit assumes script->bindings is fully initialized. */ @@ -836,8 +853,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc script->hasSingletons_ = true; if (scriptBits & (1 << TreatAsRunOnce)) script->treatAsRunOnce_ = true; - if (scriptBits & (1 << HasPollutedGlobalScope)) - script->hasPollutedGlobalScope_ = true; + if (scriptBits & (1 << HasNonSyntacticScope)) + script->hasNonSyntacticScope_ = true; if (scriptBits & (1 << IsLegacyGenerator)) { MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator))); @@ -969,7 +986,12 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc MOZ_ASSERT(enclosingStaticScopeIndex < i); enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex]; } else { - enclosingStaticScope = fun; + // This is not ternary because MSVC can't typecheck the + // ternary. + if (fun) + enclosingStaticScope = fun; + else + enclosingStaticScope = enclosingScope; } } @@ -1006,8 +1028,15 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc StaticScopeIter ssi(funEnclosingScope); - if (ssi.done() || ssi.type() == StaticScopeIter::Function) { - MOZ_ASSERT(ssi.done() == !fun); + // Starting from a nested function, hitting a non-syntactic + // scope on the static scope chain means that its enclosing + // function has a non-syntactic scope. Nested functions + // themselves never have non-syntactic scope chains. + if (ssi.done() || + ssi.type() == StaticScopeIter::NonSyntactic || + ssi.type() == StaticScopeIter::Function) + { + MOZ_ASSERT_IF(ssi.done() || ssi.type() != StaticScopeIter::Function, !fun); funEnclosingScopeIndex = UINT32_MAX; } else if (ssi.type() == StaticScopeIter::Block) { funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block()); @@ -1023,7 +1052,12 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enc if (mode == XDR_DECODE) { if (funEnclosingScopeIndex == UINT32_MAX) { - funEnclosingScope = fun; + // This is not ternary because MSVC can't typecheck the + // ternary. + if (fun) + funEnclosingScope = fun; + else + funEnclosingScope = enclosingScope; } else { MOZ_ASSERT(funEnclosingScopeIndex < i); funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex]; @@ -1164,9 +1198,13 @@ js::XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript return false; } - if (mode == XDR_DECODE) + if (mode == XDR_DECODE) { lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript, packedFields, begin, end, lineno, column)); + if (!lazy) + return false; + fun->initLazyScript(lazy); + } } // Code free variables. @@ -2422,11 +2460,16 @@ JSScript::Create(ExclusiveContext *cx, HandleObject enclosingScope, bool savedCa script->savedCallerFun_ = savedCallerFun; script->initCompartment(cx); - script->hasPollutedGlobalScope_ = options.hasPollutedGlobalScope; script->selfHosted_ = options.selfHostingMode; script->noScriptRval_ = options.noScriptRval; script->treatAsRunOnce_ = options.isRunOnce; + // Compute whether this script is under a non-syntactic scope. We don't + // need to walk the entire static scope chain if the script is nested in a + // function. In that case, we can propagate the cached value from the + // outer script. + script->hasNonSyntacticScope_ = HasNonSyntacticStaticScopeChain(enclosingScope); + script->version = options.version; MOZ_ASSERT(script->getVersion() == options.version); // assert that no overflow occurred @@ -2643,6 +2686,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false; script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false; script->needsHomeObject_ = funbox ? funbox->needsHomeObject() : false; + script->isDerivedClassConstructor_ = funbox ? funbox->isDerivedClassConstructor() : false; script->hasSingletons_ = bce->hasSingletons; if (funbox) { @@ -2660,10 +2704,13 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco RootedFunction fun(cx, nullptr); if (funbox) { + // The function should have already been earlier to enable + // StaticScopeIter to walk the static scope chain of + // currently compiling scripts. + MOZ_ASSERT(script->functionNonDelazifying() == funbox->function()); MOZ_ASSERT(!bce->script->noScriptRval()); script->isGeneratorExp_ = funbox->inGenexpLambda; script->setGeneratorKind(funbox->generatorKind()); - script->setFunction(funbox->function()); if (bce->yieldOffsetList.length() != 0) bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength); } @@ -2996,15 +3043,48 @@ Rebase(JSScript* dst, JSScript* src, T* srcp) return reinterpret_cast(dst->data + off); } -JSScript * -js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src, - PollutedGlobalScopeOption polluted /* = HasCleanGlobalScope */, - NewObjectKind newKind /* = GenericObject */) +static JSObject* +CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun) +{ + /* NB: Keep this in sync with XDRInterpretedFunction. */ + RootedObject cloneProto(cx); + if (srcFun->isStarGenerator()) { + cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); + if (!cloneProto) + return nullptr; + } + + gc::AllocKind allocKind = srcFun->getAllocKind(); + RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0, + JSFunction::INTERPRETED, nullptr, nullptr, + cloneProto, allocKind, TenuredObject)); + if (!clone) + return nullptr; + + JSScript::AutoDelazify srcScript(cx, srcFun); + if (!srcScript) + return nullptr; + JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript); + if (!cloneScript) + return nullptr; + + clone->setArgCount(srcFun->nargs()); + clone->setFlags(srcFun->flags()); + clone->initAtom(srcFun->displayAtom()); + if (!JSFunction::setTypeForScriptedFunction(cx, clone)) + return nullptr; + + return clone; +} + +bool +js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, + HandleScript dst) { if (src->treatAsRunOnce() && !src->functionNonDelazifying()) { // Toplevel run-once scripts may not be cloned. JS_ReportError(cx, "No cloning toplevel run-once scripts"); - return nullptr; + return false; } /* NB: Keep this in sync with XDRScript. */ @@ -3023,7 +3103,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, size_t size = src->dataSize(); uint8_t* data = AllocScriptData(cx->zone(), size); if (size && !data) - return nullptr; + return false; /* Bindings */ @@ -3031,7 +3111,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, InternalHandle bindingsHandle = InternalHandle::fromMarkedLocation(bindings.address()); if (!Bindings::clone(cx, bindingsHandle, data, src)) - return nullptr; + return false; /* Objects */ @@ -3048,7 +3128,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, if (NestedScopeObject* enclosingBlock = innerBlock->enclosingNestedScope()) enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)]; else - enclosingScope = fun; + enclosingScope = scriptStaticScope; clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock); } else if (obj->is()) { @@ -3057,32 +3137,36 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, if (cx->compartment() != innerFun->compartment()) { MOZ_ASSERT(innerFun->isAsmJSNative()); JS_ReportError(cx, "AsmJS modules do not yet support cloning."); - return nullptr; + return false; } clone = innerFun; } else { if (innerFun->isInterpretedLazy()) { AutoCompartment ac(cx, innerFun); if (!innerFun->getOrCreateScript(cx)) - return nullptr; + return false; } RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope()); StaticScopeIter ssi(cx, staticScope); RootedObject enclosingScope(cx); - if (ssi.done() || ssi.type() == StaticScopeIter::Function) - enclosingScope = fun; - else if (ssi.type() == StaticScopeIter::Block) + if (ssi.done() || ssi.type() == StaticScopeIter::NonSyntactic) { + enclosingScope = scriptStaticScope; + } else if (ssi.type() == StaticScopeIter::Function) { + MOZ_ASSERT(scriptStaticScope->is()); + enclosingScope = scriptStaticScope; + } else if (ssi.type() == StaticScopeIter::Block) { enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())]; - else + } else { enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())]; + } - clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted); + clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun); } } else { clone = DeepCloneObjectLiteral(cx, obj, TenuredObject); } if (!clone || !objects.append(clone)) - return nullptr; + return false; } } @@ -3090,54 +3174,15 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, AutoObjectVector regexps(cx); for (unsigned i = 0; i < nregexps; i++) { - HeapPtrObject *vector = src->regexps()->vector; + HeapPtrObject* vector = src->regexps()->vector; for (unsigned i = 0; i < nregexps; i++) { - JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as()); + JSObject* clone = CloneScriptRegExpObject(cx, vector[i]->as()); if (!clone || !regexps.append(clone)) - return nullptr; + return false; } } - /* - * Wrap the script source object as needed. Self-hosted scripts may be - * in another runtime, so lazily create a new script source object to - * use for them. - */ - RootedObject sourceObject(cx); - if (cx->runtime()->isSelfHostingCompartment(src->compartment())) { - if (!cx->compartment()->selfHostingScriptSource) { - CompileOptions options(cx); - FillSelfHostingCompileOptions(options); - - ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options); - if (!obj) - return nullptr; - cx->compartment()->selfHostingScriptSource.set(obj); - } - sourceObject = cx->compartment()->selfHostingScriptSource; - } else { - sourceObject = src->sourceObject(); - if (!cx->compartment()->wrap(cx, &sourceObject)) - return nullptr; - } - - /* Now that all fallible allocation is complete, create the GC thing. */ - - CompileOptions options(cx); - options.setMutedErrors(src->mutedErrors()) - .setHasPollutedScope(src->hasPollutedGlobalScope() || - polluted == HasPollutedGlobalScope) - .setSelfHostingMode(src->selfHosted()) - .setNoScriptRval(src->noScriptRval()) - .setVersion(src->getVersion()); - - RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(), - options, src->staticLevel(), - sourceObject, src->sourceStart(), src->sourceEnd())); - if (!dst) { - js_free(data); - return nullptr; - } + /* Now that all fallible allocation is complete, do the copying. */ dst->bindings = bindings; @@ -3199,59 +3244,105 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, /* * Function delazification assumes that their script does not have a - * polluted global scope. We ensure that as follows: + * non-syntactic global scope. We ensure that as follows: * * 1) Initial parsing only creates lazy functions if - * !hasPollutedGlobalScope. + * !hasNonSyntacticScope. * 2) Cloning a lazy function into a non-global scope will always require * that its script be cloned. See comments in * CloneFunctionObjectUseSameScript. * 3) Cloning a script never sets a lazyScript on the clone, so the function * cannot be relazified. * - * If you decide that lazy functions should be supported with a polluted - * global scope, make sure delazification can deal. + * If you decide that lazy functions should be supported with a + * non-syntactic global scope, make sure delazification can deal. */ - MOZ_ASSERT_IF(dst->hasPollutedGlobalScope(), !dst->maybeLazyScript()); - MOZ_ASSERT_IF(dst->hasPollutedGlobalScope(), !dst->isRelazifiable()); + MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->maybeLazyScript()); + MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->isRelazifiable()); + return true; +} + +static JSScript* +CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src) +{ + /* + * Wrap the script source object as needed. Self-hosted scripts may be + * in another runtime, so lazily create a new script source object to + * use for them. + */ + RootedObject sourceObject(cx); + if (cx->runtime()->isSelfHostingCompartment(src->compartment())) { + if (!cx->compartment()->selfHostingScriptSource) { + CompileOptions options(cx); + FillSelfHostingCompileOptions(options); + + ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options); + if (!obj) + return nullptr; + cx->compartment()->selfHostingScriptSource.set(obj); + } + sourceObject = cx->compartment()->selfHostingScriptSource; + } else { + sourceObject = src->sourceObject(); + if (!cx->compartment()->wrap(cx, &sourceObject)) + return nullptr; + } + + CompileOptions options(cx); + options.setMutedErrors(src->mutedErrors()) + .setSelfHostingMode(src->selfHosted()) + .setNoScriptRval(src->noScriptRval()) + .setVersion(src->getVersion()); + + return JSScript::Create(cx, enclosingScope, src->savedCallerFun(), + options, src->staticLevel(), + sourceObject, src->sourceStart(), src->sourceEnd()); +} + +JSScript* +js::CloneGlobalScript(JSContext* cx, Handle enclosingScope, HandleScript src) +{ + // No enclosingScope means clean global. + MOZ_ASSERT(!enclosingScope || enclosingScope->is()); + + RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src)); + if (!dst) + return nullptr; + + if (!detail::CopyScript(cx, enclosingScope, src, dst)) + return nullptr; + return dst; } -bool -js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone, - PollutedGlobalScopeOption polluted, NewObjectKind newKind) +JSScript* +js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, + HandleScript src) { - RootedScript script(cx, clone->nonLazyScript()); - MOZ_ASSERT(script); - MOZ_ASSERT(script->compartment() == original->compartment()); - MOZ_ASSERT(cx->compartment() == clone->compartment(), - "Otherwise we could relazify clone below!"); + MOZ_ASSERT(fun->isInterpreted()); - // The only scripts with enclosing static scopes that may be cloned across - // compartments are non-strict, indirect eval scripts, as their dynamic - // scope chains terminate in the global scope immediately. - RootedObject scope(cx, script->enclosingStaticScope()); - if (script->compartment() != cx->compartment() && scope) { - MOZ_ASSERT(!scope->as().isDirect() && - !scope->as().isStrict()); - scope = StaticEvalObject::create(cx, nullptr); - if (!scope) - return false; + // Allocate the destination script up front and set it as the script of + // |fun|, which is to be its container. + // + // This is so that when cloning nested functions, they can walk the static + // scope chain via fun and correctly compute the presence of a + // non-syntactic global. + RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src)); + if (!dst) + return nullptr; + + dst->setFunction(fun); + if (fun->isInterpretedLazy()) + fun->setUnlazifiedScript(dst); + else + fun->initScript(dst); + + if (!detail::CopyScript(cx, fun, src, dst)) { + fun->setScript(nullptr); + return nullptr; } - clone->initScript(nullptr); - - JSScript *cscript = CloneScript(cx, scope, clone, script, polluted, newKind); - if (!cscript) - return false; - - clone->setScript(cscript); - cscript->setFunction(clone); - - script = clone->nonLazyScript(); - Debugger::onNewScript(cx, script); - - return true; + return dst; } DebugScript* @@ -3831,6 +3922,7 @@ LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, p.hasDirectEval = false; p.directlyInsideEval = false; p.usesArgumentsApplyAndThis = false; + p.isDerivedClassConstructor = false; LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); MOZ_ASSERT_IF(res, res->version() == version); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 9a96c89b5a..31ba23f7de 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -751,23 +751,13 @@ GeneratorKindFromBits(unsigned val) { */ template bool -XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, +XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp); -enum PollutedGlobalScopeOption { - HasPollutedGlobalScope, - HasCleanGlobalScope -}; - -JSScript * -CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script, - PollutedGlobalScopeOption polluted = HasCleanGlobalScope, - NewObjectKind newKind = GenericObject); - template bool -XDRLazyScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, - HandleFunction fun, MutableHandle lazy); +XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, + HandleFunction fun, MutableHandle lazy); /* * Code any constant value. @@ -776,6 +766,16 @@ template bool XDRScriptConst(XDRState* xdr, MutableHandleValue vp); + +namespace detail { + +// Do not call this directly! It is exposed for the friend declaration in +// JSScript. +bool +CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst); + +} // namespace detail + } /* namespace js */ class JSScript : public js::gc::TenuredCell @@ -783,13 +783,13 @@ class JSScript : public js::gc::TenuredCell template friend bool - js::XDRScript(js::XDRState* xdr, js::HandleObject enclosingScope, js::HandleScript enclosingScript, + js::XDRScript(js::XDRState* xdr, js::HandleObject enclosingScope, + js::HandleScript enclosingScript, js::HandleFunction fun, js::MutableHandleScript scriptp); - friend JSScript * - js::CloneScript(JSContext *cx, js::HandleObject enclosingScope, js::HandleFunction fun, - js::HandleScript src, js::PollutedGlobalScopeOption polluted, - js::NewObjectKind newKind); + friend bool + js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src, + js::HandleScript dst); public: // @@ -935,7 +935,7 @@ class JSScript : public js::gc::TenuredCell // True if the script has a non-syntactic scope on its dynamic scope chain. // That is, there are objects about which we know nothing between the // outermost syntactic scope and the global. - bool hasPollutedGlobalScope_:1; + bool hasNonSyntacticScope_:1; // see Parser::selfHostingMode. bool selfHosted_:1; @@ -1024,6 +1024,8 @@ class JSScript : public js::gc::TenuredCell bool needsHomeObject_:1; + bool isDerivedClassConstructor_:1; + // Add padding so JSScript is gc::Cell aligned. Make padding protected // instead of private to suppress -Wunused-private-field compiler warnings. protected: @@ -1175,8 +1177,8 @@ class JSScript : public js::gc::TenuredCell bool explicitUseStrict() const { return explicitUseStrict_; } - bool hasPollutedGlobalScope() const { - return hasPollutedGlobalScope_; + bool hasNonSyntacticScope() const { + return hasNonSyntacticScope_; } bool selfHosted() const { return selfHosted_; } @@ -1296,6 +1298,9 @@ class JSScript : public js::gc::TenuredCell return needsHomeObject_; } + bool isDerivedClassConstructor() const { + return isDerivedClassConstructor_; + } /* * As an optimization, even when argsHasLocalBinding, the function prologue @@ -1943,11 +1948,13 @@ class LazyScript : public gc::TenuredCell uint32_t version : 8; uint32_t numFreeVariables : 24; - uint32_t numInnerFunctions : 22; + uint32_t numInnerFunctions : 21; uint32_t generatorKindBits : 2; // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC. + // If you add another boolean here, make sure to initialze it in + // LazyScript::CreateRaw(). uint32_t strict : 1; uint32_t bindingsAccessedDynamically : 1; uint32_t hasDebuggerStatement : 1; @@ -1956,6 +1963,7 @@ class LazyScript : public gc::TenuredCell uint32_t usesArgumentsApplyAndThis : 1; uint32_t hasBeenCloned : 1; uint32_t treatAsRunOnce : 1; + uint32_t isDerivedClassConstructor : 1; }; union { @@ -2127,6 +2135,13 @@ class LazyScript : public gc::TenuredCell p_.treatAsRunOnce = true; } + bool isDerivedClassConstructor() const { + return p_.isDerivedClassConstructor; + } + void setIsDerivedClassConstructor() { + p_.isDerivedClassConstructor = true; + } + const char* filename() const { return scriptSource()->filename(); } @@ -2276,14 +2291,17 @@ enum LineOption { }; extern void -DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript, - const char **file, unsigned *linenop, - uint32_t *pcOffset, bool *mutedErrors, +DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript, + const char** file, unsigned* linenop, + uint32_t* pcOffset, bool* mutedErrors, LineOption opt = NOT_CALLED_FROM_JSOP_EVAL); -bool -CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone, - PollutedGlobalScopeOption polluted, NewObjectKind newKind); +JSScript* +CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, + HandleScript src); + +JSScript* +CloneGlobalScript(JSContext* cx, Handle enclosingScope, HandleScript src); } /* namespace js */ diff --git a/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js b/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js new file mode 100644 index 0000000000..6f4898b31f --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!xulRuntime.shell) + +var test = ` + +class base { + constructor() { + eval(''); + (()=>0)(); + } +} + +class derived extends base { + constructor() { + eval(''); + } +} + +// Make sure eval and arrows are still valid in non-derived constructors. +new base(); + +// Eval throws in derived class constructors, in both class expressions and +// statements. +assertThrowsInstanceOf((() => new derived()), InternalError); +assertThrowsInstanceOf((() => new class extends base { constructor() { eval('') } }()), InternalError); + +var g = newGlobal(); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function(frame) { assertThrowsInstanceOf(()=>frame.eval(''), InternalError); }; +g.eval("new class foo extends null { constructor() { debugger; } }()"); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/methDefn.js b/js/src/tests/ecma_6/Class/methDefn.js index 3da1dbbb8e..bbce2f2789 100644 --- a/js/src/tests/ecma_6/Class/methDefn.js +++ b/js/src/tests/ecma_6/Class/methDefn.js @@ -32,6 +32,9 @@ syntaxError("b = {a{}}"); syntaxError("b = {a({}}"); syntaxError("b = {a@(){}}"); syntaxError("b = {a() => 0}"); +syntaxError("b = {a() void 0}"); +syntaxError("b = {a() 1}"); +syntaxError("b = {a() false}"); b = {a(){return 5;}}; assertEq(b.a(), 5); diff --git a/js/src/tests/ecma_6/Class/newTargetGenerators.js b/js/src/tests/ecma_6/Class/newTargetGenerators.js new file mode 100644 index 0000000000..da611c09ec --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetGenerators.js @@ -0,0 +1,24 @@ +function *generatorNewTarget(expected) { + assertEq(new.target, expected); + assertEq(eval('new.target'), expected); + assertEq((() => new.target)(), expected); + yield (() => new.target); +} + +const ITERATIONS = 25; + +for (let i = 0; i < ITERATIONS; i++) + assertEq(generatorNewTarget(undefined).next().value(), undefined); + +for (let i = 0; i < ITERATIONS; i++) + assertEq(new generatorNewTarget(generatorNewTarget).next().value(), + generatorNewTarget); + +// also check to make sure it's useful in yield inside generators. +// Plus, this code is so ugly, how could it not be a test? ;) +// Thanks to anba for supplying this ludicrous expression. +assertDeepEq([...new function*(i) { yield i; if(i > 0) yield* new new.target(i-1) }(10)], + [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/for-loop-with-bindings-added-at-runtime.js b/js/src/tests/ecma_6/LexicalEnvironment/for-loop-with-bindings-added-at-runtime.js new file mode 100644 index 0000000000..f6abf3d0cd --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/for-loop-with-bindings-added-at-runtime.js @@ -0,0 +1,125 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = "for-loop-with-bindings-added-at-runtime.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1149797; +var summary = + "Don't assert when freshening the scope chain for a for-loop whose head " + + "contains a lexical declaration, where the loop body might add more " + + "bindings at runtime"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +for (let x = 0; x < 9; ++x) + eval("var y"); + +{ + for (let x = 0; x < 9; ++x) + eval("var y"); +} + +function f1() +{ + for (let x = 0; x < 9; ++x) + eval("var y"); +} +f1(); + +function f2() +{ + { + for (let x = 0; x < 9; ++x) + eval("var y"); + } +} +f2(); + +for (let x = 0; x < 9; ++x) +{ + // deliberately inside a block statement + eval("var y"); +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } +} + +function g1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } +} +g1(); + +function g2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } + } +} +g2(); + +for (let x = 0; x < 9; ++x) { + (function() { + eval("var y"); + })(); +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("var y"); + })(); + } +} + +function h1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("var y"); + })(); + } +} +h1(); + +function h2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { eval("var y"); })(); + } + } +} +h2(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js new file mode 100644 index 0000000000..ea2494bdab --- /dev/null +++ b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js @@ -0,0 +1,126 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = + "for-loop-with-lexical-declaration-and-nested-function-statement.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1149797; +var summary = + "Don't assert when freshening the scope chain for a for-loop whose head " + + "contains a lexical declaration, where the loop body might add more " + + "bindings at runtime"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +for (let x = 0; x < 9; ++x) + function q1() {} + +{ + for (let x = 0; x < 9; ++x) + function q2() {} +} + +function f1() +{ + for (let x = 0; x < 9; ++x) + function q3() {} +} +f1(); + +function f2() +{ + { + for (let x = 0; x < 9; ++x) + function q4() {} + } +} +f2(); + +for (let x = 0; x < 9; ++x) +{ + // deliberately inside a block statement + function q5() {} +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + function q6() {} + } +} + +function g1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + function q7() {} + } +} +g1(); + +function g2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + function q8() {} + } + } +} +g2(); + +for (let x = 0; x < 9; ++x) { + (function() { + eval("function q9() {}"); + })(); +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("function q10() {}"); + })(); + } +} + +function h1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("function q11() {}"); + })(); + } +} +h1(); + +function h2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { eval("function q12() {}"); })(); + } + } +} +h2(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/js1_8_5/reflect-parse/classes.js b/js/src/tests/js1_8_5/reflect-parse/classes.js index db7a498728..c6f85ba021 100644 --- a/js/src/tests/js1_8_5/reflect-parse/classes.js +++ b/js/src/tests/js1_8_5/reflect-parse/classes.js @@ -118,6 +118,9 @@ function testClasses() { // Currently, we do not allow default constructors assertClassError("class NAME { }", TypeError); + // For now, disallow arrow functions in derived class constructors + assertClassError("class NAME extends null { constructor() { (() => 0); }", InternalError); + // It is an error to have two methods named constructor, but not other // names, regardless if one is an accessor or a generator or static. assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError); @@ -408,10 +411,10 @@ function testClasses() { assertError("(()=>super['prop'])", SyntaxError); assertError("function *foo() { super['prop']; }", SyntaxError); assertError("super['prop']", SyntaxError); - + // Or inside functions inside method definitions... assertClassError("class NAME { constructor() { function nested() { super.prop; }}}", SyntaxError); - + // Bare super is forbidden assertError("super", SyntaxError); @@ -442,6 +445,17 @@ function testClasses() { assertClassError("class NAME { constructor() { super.m(", SyntaxError); assertClassError("class NAME { constructor() { super[", SyntaxError); assertClassError("class NAME { constructor() { super(", SyntaxError); + + // Can not omit curly brackets + assertClassError("class NAME { constructor() ({}) }", SyntaxError); + assertClassError("class NAME { constructor() void 0 }", SyntaxError); + assertClassError("class NAME { constructor() 1 }", SyntaxError); + assertClassError("class NAME { constructor() false }", SyntaxError); + assertClassError("class NAME { constructor() {} a() ({}) }", SyntaxError); + assertClassError("class NAME { constructor() {} a() void 0 }", SyntaxError); + assertClassError("class NAME { constructor() {} a() 1 }", SyntaxError); + assertClassError("class NAME { constructor() {} a() false }", SyntaxError); + } if (classesEnabled()) diff --git a/js/src/tests/js1_8_5/reflect-parse/methodDefn.js b/js/src/tests/js1_8_5/reflect-parse/methodDefn.js index 727b111fcc..090a0844fb 100644 --- a/js/src/tests/js1_8_5/reflect-parse/methodDefn.js +++ b/js/src/tests/js1_8_5/reflect-parse/methodDefn.js @@ -10,6 +10,11 @@ assertExpr("b = { *a() { } }", aExpr("=", ident("b"), objExpr([{ key: ident("a"), value: genFunExpr(ident("a"), [], blockStmt([])), method: true}]))); +// Method definitions without braces +assertError("({ a() void 0 })", SyntaxError); +assertError("({ a() 1 })", SyntaxError); +assertError("({ a() false })", SyntaxError); + // getters and setters assertExpr("({ get x() { return 42 } })", diff --git a/js/src/tests/js1_8_5/reflect-parse/newTarget.js b/js/src/tests/js1_8_5/reflect-parse/newTarget.js index 4b703fbf53..45cf527ec6 100644 --- a/js/src/tests/js1_8_5/reflect-parse/newTarget.js +++ b/js/src/tests/js1_8_5/reflect-parse/newTarget.js @@ -15,8 +15,9 @@ function testNewTarget() { assertInFunctionExpr("()=>new.target", arrowExpr([], newTarget())); assertError("(() => new.target))", SyntaxError); - // invalid (for now!) in generators - assertError("function *foo() { new.target; }", SyntaxError); + // valid in generators, too! + assertStmt("function *foo() { new.target; }", genFunDecl(ident("foo"), [], + blockStmt([exprStmt(newTarget())]))); // new.target is a member expression. You should be able to call, invoke, or // access properties of it. diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 7a603d9cc8..d8755045f5 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6264,12 +6264,14 @@ EvaluateInEnv(JSContext* cx, Handle env, HandleValue thisv, AbstractFrameP * boundaries, and we are putting a DebugScopeProxy or non-syntactic With on * the scope chain. */ - Rooted staticScope(cx, StaticEvalObject::create(cx, nullptr)); + Rooted enclosingStaticScope(cx); + if (!env->is()) + enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr); + Rooted staticScope(cx, StaticEvalObject::create(cx, enclosingStaticScope)); if (!staticScope) return false; CompileOptions options(cx); - options.setHasPollutedScope(true) - .setIsRunOnce(true) + options.setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) .setFileAndLine(filename, lineno) @@ -6305,6 +6307,13 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code MOZ_ASSERT_IF(iter, !scope); MOZ_ASSERT_IF(!iter, scope && scope->is()); + if (iter && iter->script()->isDerivedClassConstructor()) { + MOZ_ASSERT(iter->isFunctionFrame() && iter->calleeTemplate()->isClassConstructor()); + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS, + "debugger eval"); + return false; + } + /* Check the first argument, the eval code string. */ if (!code.isString()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, @@ -6420,14 +6429,8 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code return false; RootedObject dynamicScope(cx); - // We ignore the static scope here. See comments about static - // scopes in EvaluateInEnv. - RootedObject unusedStaticScope(cx); - if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope, - &unusedStaticScope)) - { + if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope)) return false; - } env = dynamicScope; } diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 153cfde511..0c59ac2a3d 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -49,6 +49,7 @@ GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) GeneratorObject* genObj = &obj->as(); genObj->setCallee(*frame.callee()); genObj->setThisValue(frame.thisValue()); + genObj->setNewTarget(frame.newTarget()); genObj->setScopeChain(*frame.scopeChain()); if (frame.script()->needsArgsObj()) genObj->setArgsObj(frame.argsObj()); @@ -162,9 +163,11 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation, RootedFunction callee(cx, &genObj->callee()); RootedValue thisv(cx, genObj->thisValue()); + RootedValue newTarget(cx, genObj->newTarget()); RootedObject scopeChain(cx, &genObj->scopeChain()); - if (!activation.resumeGeneratorFrame(callee, thisv, scopeChain)) + if (!activation.resumeGeneratorFrame(callee, thisv, newTarget, scopeChain)) return false; + activation.regs().fp()->setResumedGenerator(); if (genObj->hasArgsObj()) activation.regs().fp()->initArgsObj(genObj->argsObj()); diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index abfa408eaf..4d8e4ddbda 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -31,6 +31,7 @@ class GeneratorObject : public NativeObject ARGS_OBJ_SLOT, EXPRESSION_STACK_SLOT, YIELD_INDEX_SLOT, + NEWTARGET_SLOT, RESERVED_SLOTS }; @@ -117,6 +118,17 @@ class GeneratorObject : public NativeObject setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); } + bool isConstructing() const { + return getFixedSlot(NEWTARGET_SLOT).isObject(); + } + const Value& newTarget() const { + return getFixedSlot(NEWTARGET_SLOT); + } + void setNewTarget(Value newTarget) { + setFixedSlot(NEWTARGET_SLOT, newTarget); + } + + // The yield index slot is abused for a few purposes. It's undefined if // it hasn't been set yet (before the initial yield), and null if the // generator is closed. If the generator is running, the yield index is @@ -172,6 +184,7 @@ class GeneratorObject : public NativeObject setFixedSlot(ARGS_OBJ_SLOT, NullValue()); setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); setFixedSlot(YIELD_INDEX_SLOT, NullValue()); + setFixedSlot(NEWTARGET_SLOT, NullValue()); } static size_t offsetOfCalleeSlot() { @@ -192,6 +205,9 @@ class GeneratorObject : public NativeObject static size_t offsetOfExpressionStackSlot() { return getFixedSlotOffset(EXPRESSION_STACK_SLOT); } + static size_t offsetOfNewTargetSlot() { + return getFixedSlotOffset(NEWTARGET_SLOT); + } }; class LegacyGeneratorObject : public GeneratorObject diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 589db4bd09..898f8180f8 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -273,9 +273,9 @@ SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject s *pc == JSOP_STRICTSETNAME || *pc == JSOP_SETGNAME || *pc == JSOP_STRICTSETGNAME); - MOZ_ASSERT_IF(*pc == JSOP_SETGNAME && !script->hasPollutedGlobalScope(), + MOZ_ASSERT_IF(*pc == JSOP_SETGNAME && !script->hasNonSyntacticScope(), scope == cx->global()); - MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME && !script->hasPollutedGlobalScope(), + MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME && !script->hasNonSyntacticScope(), scope == cx->global()); bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME; diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index c34798b1bd..d533c766c4 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -272,7 +272,7 @@ GetNameOperation(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc, MutableHan * the actual behavior even if the id could be found on the scope chain * before the global object. */ - if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasPollutedGlobalScope()) + if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasNonSyntacticScope()) obj = &obj->global(); Shape* shape = nullptr; @@ -866,7 +866,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c while (IsSyntacticScope(terminatingScope)) terminatingScope = terminatingScope->enclosingScope(); MOZ_ASSERT(terminatingScope->is() || - script->hasPollutedGlobalScope()); + script->hasNonSyntacticScope()); #endif if (script->treatAsRunOnce()) { @@ -902,8 +902,8 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value* RootedObject scopeChain(cx, &scopeChainArg); MOZ_ASSERT(scopeChain == GetInnerObject(scopeChain)); - MOZ_RELEASE_ASSERT(scopeChain->is() || script->hasPollutedGlobalScope(), - "Only scripts with polluted scopes can be executed with " + MOZ_RELEASE_ASSERT(scopeChain->is() || script->hasNonSyntacticScope(), + "Only scripts with non-syntactic scopes can be executed with " "interesting scopechains"); /* Ensure the scope chain is all same-compartment and terminates in a global. */ @@ -1219,6 +1219,7 @@ PopScope(JSContext* cx, ScopeIter& si) break; case ScopeIter::Call: case ScopeIter::Eval: + case ScopeIter::NonSyntactic: break; } } @@ -2362,7 +2363,7 @@ CASE(JSOP_BINDGNAME) CASE(JSOP_BINDNAME) { JSOp op = JSOp(*REGS.pc); - if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) { + if (op == JSOP_BINDNAME || script->hasNonSyntacticScope()) { ReservedRooted scopeChain(&rootObject0, REGS.fp()->scopeChain()); ReservedRooted name(&rootName0, script->getName(REGS.pc)); @@ -3116,7 +3117,7 @@ CASE(JSOP_IMPLICITTHIS) CASE(JSOP_GIMPLICITTHIS) { JSOp op = JSOp(*REGS.pc); - if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) { + if (op == JSOP_IMPLICITTHIS || script->hasNonSyntacticScope()) { ReservedRooted name(&rootName0, script->getName(REGS.pc)); ReservedRooted scopeObj(&rootObject0, REGS.fp()->scopeChain()); ReservedRooted scope(&rootObject1); @@ -3994,7 +3995,7 @@ CASE(JSOP_SUPERBASE) { ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc)); for (; !si.done(); ++si) { - if (si.hasScopeObject() && si.type() == ScopeIter::Call) { + if (si.hasSyntacticScopeObject() && si.type() == ScopeIter::Call) { JSFunction& callee = si.scope().as().callee(); // Arrow functions don't have the information we're looking for, diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index f32cf82689..92c305b9c3 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1578,9 +1578,9 @@ macro(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, JOF_BYTE) \ \ /* - * Looks up name on global scope and pushes its value onto the stack, unless - * the script has a polluted global, in which case it acts just like - * JSOP_NAME. + * Looks up name on global scope and pushes its value onto the stack, + * unless the script has a non-syntactic global scope, in which case it + * acts just like JSOP_NAME. * * Free variable references that must either be found on the global or a * ReferenceError. @@ -1594,7 +1594,7 @@ * Pops the top two values on the stack as 'val' and 'scope', sets property * of 'scope' as 'val' and pushes 'val' back on the stack. * - * 'scope' should be the global scope unless the script has a polluted + * 'scope' should be the global scope unless the script has a non-syntactic * global scope, in which case acts like JSOP_SETNAME. * Category: Variables and Scopes * Type: Free Variables @@ -1608,7 +1608,7 @@ * of 'scope' as 'val' and pushes 'val' back on the stack. Throws a * TypeError if the set fails, per strict mode semantics. * - * 'scope' should be the global scope unless the script has a polluted + * 'scope' should be the global scope unless the script has a non-syntactic * global scope, in which case acts like JSOP_STRICTSETNAME. * Category: Variables and Scopes * Type: Free Variables @@ -1879,7 +1879,7 @@ macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \ /* * Pushes the global scope onto the stack if the script doesn't have a - * polluted global scope. Otherwise will act like JSOP_BINDNAME. + * non-syntactic global scope. Otherwise will act like JSOP_BINDNAME. * * 'nameIndex' is only used when acting like JSOP_BINDNAME. * Category: Variables and Scopes diff --git a/js/src/vm/ScopeObject-inl.h b/js/src/vm/ScopeObject-inl.h index 435d49e31f..1432bbf329 100644 --- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -84,6 +84,8 @@ StaticScopeIter::operator++(int) obj = obj->template as().enclosingScopeForStaticScopeIter(); } else if (obj->template is()) { obj = obj->template as().enclosingScopeForStaticScopeIter(); + } else if (obj->template is()) { + obj = obj->template as().enclosingScopeForStaticScopeIter(); } else if (onNamedLambda || !obj->template as().isNamedLambda()) { onNamedLambda = false; obj = obj->template as().nonLazyScript()->enclosingStaticScope(); @@ -92,27 +94,32 @@ StaticScopeIter::operator++(int) } MOZ_ASSERT_IF(obj, obj->template is() || obj->template is() || + obj->template is() || obj->template is()); MOZ_ASSERT_IF(onNamedLambda, obj->template is()); } template inline bool -StaticScopeIter::hasDynamicScopeObject() const +StaticScopeIter::hasSyntacticDynamicScopeObject() const { - return obj->template is() - ? obj->template as().needsClone() - : (obj->template is() - ? obj->template as().isStrict() - : (obj->template is() || - obj->template as().isHeavyweight())); + if (obj->template is()) + return obj->template as().isHeavyweight(); + if (obj->template is()) + return obj->template as().needsClone(); + if (obj->template is()) + return true; + if (obj->template is()) + return obj->template as().isStrict(); + MOZ_ASSERT(obj->template is()); + return false; } template inline Shape* StaticScopeIter::scopeShape() const { - MOZ_ASSERT(hasDynamicScopeObject()); + MOZ_ASSERT(hasSyntacticDynamicScopeObject()); MOZ_ASSERT(type() != NamedLambda && type() != Eval); if (type() == Block) return block().lastProperty(); @@ -131,6 +138,8 @@ StaticScopeIter::type() const ? With : (obj->template is() ? Eval + : (obj->template is()) + ? NonSyntactic : Function)); } @@ -158,6 +167,14 @@ StaticScopeIter::eval() const return obj->template as(); } +template +inline StaticNonSyntacticScopeObjects& +StaticScopeIter::nonSyntactic() const +{ + MOZ_ASSERT(type() == NonSyntactic); + return obj->template as(); +} + template inline JSScript* StaticScopeIter::funScript() const diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 93b1d03fc3..89aac675b5 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -43,7 +43,7 @@ js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc) uint32_t hops = ScopeCoordinate(pc).hops(); while (true) { MOZ_ASSERT(!ssi.done()); - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { if (!hops) break; hops--; @@ -107,7 +107,7 @@ js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc) StaticScopeIter ssi(script->innermostStaticScopeInScript(pc)); uint32_t hops = ScopeCoordinate(pc).hops(); while (true) { - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { if (!hops) break; hops--; @@ -212,7 +212,7 @@ CallObject::create(JSContext* cx, HandleScript script, HandleObject enclosing, H if (!callobj) return nullptr; - callobj->as().setEnclosingScope(enclosing); + callobj->setEnclosingScope(enclosing); callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); if (script->treatAsRunOnce()) { @@ -323,22 +323,10 @@ const Class DeclEnvObject::class_ = { * scope and callee) or used as a template for jit compilation. */ DeclEnvObject* -DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, gc::InitialHeap heap) +DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind) { - MOZ_ASSERT(IsNurseryAllocable(FINALIZE_KIND)); - - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); - if (!group) - return nullptr; - - RootedShape emptyDeclEnvShape(cx); - emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE); - if (!emptyDeclEnvShape) - return nullptr; - - RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, heap, - emptyDeclEnvShape, group))); + Rooted obj(cx); + obj = NewObjectWithNullTaggedProto(cx, newKind, BaseShape::DELEGATE); if (!obj) return nullptr; @@ -356,13 +344,13 @@ DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, gc::Initi return nullptr; MOZ_ASSERT(!obj->hasDynamicSlots()); - return &obj->as(); + return obj; } DeclEnvObject* DeclEnvObject::create(JSContext* cx, HandleObject enclosing, HandleFunction callee) { - Rooted obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap)); + Rooted obj(cx, createTemplateObject(cx, callee, GenericObject)); if (!obj) return nullptr; @@ -400,20 +388,7 @@ js::XDRStaticWithObject(XDRState*, HandleObject, MutableHandleas(); + return NewObjectWithNullTaggedProto(cx, TenuredObject, BaseShape::DELEGATE); } static JSObject* @@ -433,18 +408,11 @@ DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclo HandleObject staticWith, WithKind kind) { MOZ_ASSERT(staticWith->is()); - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, - TaggedProto(staticWith.get()))); - if (!group) - return nullptr; - RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith), - FINALIZE_KIND)); - if (!shape) - return nullptr; - - RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, - gc::DefaultHeap, shape, group))); + Rooted proto(cx, TaggedProto(staticWith)); + Rooted obj(cx); + obj = NewObjectWithGivenTaggedProto(cx, proto, GenericObject, + BaseShape::DELEGATE); if (!obj) return nullptr; @@ -452,12 +420,12 @@ DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclo if (!thisp) return nullptr; - obj->as().setEnclosingScope(enclosing); + obj->setEnclosingScope(enclosing); obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object)); obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp)); obj->setFixedSlot(KIND_SLOT, Int32Value(kind)); - return &obj->as(); + return obj; } /* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */ @@ -607,23 +575,14 @@ const Class DynamicWithObject::class_ = { /* static */ StaticEvalObject* StaticEvalObject::create(JSContext* cx, HandleObject enclosing) { - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); - if (!group) - return nullptr; - - RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE)); - if (!shape) - return nullptr; - - RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, - gc::TenuredHeap, shape, group))); + StaticEvalObject* obj = + NewObjectWithNullTaggedProto(cx, TenuredObject, BaseShape::DELEGATE); if (!obj) return nullptr; obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing)); obj->setReservedSlot(STRICT_SLOT, BooleanValue(false)); - return &obj->as(); + return obj; } const Class StaticEvalObject::class_ = { @@ -632,6 +591,25 @@ const Class StaticEvalObject::class_ = { JSCLASS_IS_ANONYMOUS }; +/* static */ StaticNonSyntacticScopeObjects* +StaticNonSyntacticScopeObjects::create(JSContext*cx, HandleObject enclosing) +{ + StaticNonSyntacticScopeObjects* obj = + NewObjectWithNullTaggedProto(cx, TenuredObject, + BaseShape::DELEGATE); + if (!obj) + return nullptr; + + obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing)); + return obj; +} + +const Class StaticNonSyntacticScopeObjects::class_ = { + "StaticNonSyntacticScopeObjects", + JSCLASS_HAS_RESERVED_SLOTS(StaticNonSyntacticScopeObjects::RESERVED_SLOTS) | + JSCLASS_IS_ANONYMOUS +}; + /*****************************************************************************/ /* static */ ClonedBlockObject* @@ -646,7 +624,10 @@ ClonedBlockObject::create(JSContext* cx, Handle block, Handl RootedShape shape(cx, block->lastProperty()); - RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, + gc::AllocKind allocKind = gc::GetGCObjectKind(&BlockObject::class_); + if (CanBeFinalizedInBackground(allocKind, &BlockObject::class_)) + allocKind = GetBackgroundAllocKind(allocKind); + RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, allocKind, gc::TenuredHeap, shape, group))); if (!obj) return nullptr; @@ -701,54 +682,26 @@ ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame) } } -/* static */ ClonedBlockObject * -ClonedBlockObject::clone(ExclusiveContext *cx, Handle block) +/* static */ ClonedBlockObject* +ClonedBlockObject::clone(JSContext* cx, Handle clonedBlock) { - RootedObject enclosing(cx, &block->enclosingScope()); + Rooted staticBlock(cx, &clonedBlock->staticBlock()); + RootedObject enclosing(cx, &clonedBlock->enclosingScope()); - MOZ_ASSERT(block->getClass() == &BlockObject::class_); - - RootedObjectGroup cloneGroup(cx, block->group()); - RootedShape cloneShape(cx, block->lastProperty()); - - JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, cloneShape, cloneGroup); - if (!obj) + Rooted copy(cx, create(cx, staticBlock, enclosing)); + if (!copy) return nullptr; - ClonedBlockObject © = obj->as(); + for (uint32_t i = 0, count = staticBlock->numVariables(); i < count; i++) + copy->setVar(i, clonedBlock->var(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING); - MOZ_ASSERT(!copy.inDictionaryMode()); - MOZ_ASSERT(copy.isDelegate()); - MOZ_ASSERT(block->slotSpan() == copy.slotSpan()); - MOZ_ASSERT(copy.slotSpan() >= copy.numVariables() + RESERVED_SLOTS); - - copy.setReservedSlot(SCOPE_CHAIN_SLOT, block->getReservedSlot(SCOPE_CHAIN_SLOT)); - - for (uint32_t i = 0, count = copy.numVariables(); i < count; i++) - copy.setVar(i, block->var(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING); - - return © + return copy; } StaticBlockObject* StaticBlockObject::create(ExclusiveContext* cx) { - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_, - TaggedProto(nullptr))); - if (!group) - return nullptr; - - RootedShape emptyBlockShape(cx); - emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE); - if (!emptyBlockShape) - return nullptr; - - JSObject* obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, group); - if (!obj) - return nullptr; - - return &obj->as(); + return NewObjectWithNullTaggedProto(cx, TenuredObject, BaseShape::DELEGATE); } /* static */ Shape* @@ -946,22 +899,13 @@ js::CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope, Handle(cx, GenericObject, + BaseShape::DELEGATE); if (!obj) return nullptr; - - obj->as().setEnclosingScope(enclosing); - - return &obj->as(); + obj->setEnclosingScope(enclosing); + return obj; } static void @@ -999,8 +943,8 @@ uninitialized_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver } static bool -uninitialized_SetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult &result) +uninitialized_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) { ReportUninitializedLexicalId(cx, id); return false; @@ -1015,7 +959,7 @@ uninitialized_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId } static bool -uninitialized_DeleteProperty(JSContext *cx, HandleObject obj, HandleId id, ObjectOpResult &result) +uninitialized_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result) { ReportUninitializedLexicalId(cx, id); return false; @@ -1098,7 +1042,14 @@ ScopeIter::ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc void ScopeIter::incrementStaticScopeIter() { - ssi_++; + // If settled on a non-syntactic static scope, only increment ssi_ once + // we've iterated through all the non-syntactic dynamic ScopeObjects. + if (ssi_.type() == StaticScopeIter::NonSyntactic) { + if (!hasNonSyntacticScopeObject()) + ssi_++; + } else { + ssi_++; + } // For named lambdas, DeclEnvObject scopes are always attached to their // CallObjects. Skip it here, as they are special cased in users of @@ -1125,7 +1076,7 @@ ScopeIter::settle() frame_ = NullFramePtr(); #ifdef DEBUG - if (!ssi_.done() && hasScopeObject()) { + if (!ssi_.done() && hasAnyScopeObject()) { switch (ssi_.type()) { case StaticScopeIter::Function: MOZ_ASSERT(scope_->as().callee().nonLazyScript() == ssi_.funScript()); @@ -1139,6 +1090,9 @@ ScopeIter::settle() case StaticScopeIter::Eval: MOZ_ASSERT(scope_->as().isForEval()); break; + case StaticScopeIter::NonSyntactic: + MOZ_ASSERT(!IsSyntacticScope(scope_)); + break; case StaticScopeIter::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); } @@ -1149,7 +1103,7 @@ ScopeIter::settle() ScopeIter& ScopeIter::operator++() { - if (hasScopeObject()) { + if (hasAnyScopeObject()) { scope_ = &scope_->as().enclosingScope(); if (scope_->is()) scope_ = &scope_->as().enclosingScope(); @@ -1175,6 +1129,8 @@ ScopeIter::type() const return With; case StaticScopeIter::Eval: return Eval; + case StaticScopeIter::NonSyntactic: + return NonSyntactic; case StaticScopeIter::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); default: @@ -1185,7 +1141,7 @@ ScopeIter::type() const ScopeObject& ScopeIter::scope() const { - MOZ_ASSERT(hasScopeObject()); + MOZ_ASSERT(hasAnyScopeObject()); return scope_->as(); } @@ -1204,6 +1160,8 @@ ScopeIter::maybeStaticScope() const return &staticWith(); case StaticScopeIter::Eval: return &staticEval(); + case StaticScopeIter::NonSyntactic: + return &staticNonSyntactic(); case StaticScopeIter::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); default: @@ -1807,7 +1765,7 @@ const DebugScopeProxy DebugScopeProxy::singleton; DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing) { MOZ_ASSERT(scope.compartment() == cx->compartment()); - MOZ_ASSERT(!IsSyntacticScope(enclosing)); + MOZ_ASSERT(!enclosing->is()); RootedValue priv(cx, ObjectValue(scope)); JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv, @@ -2069,7 +2027,7 @@ DebugScopes::addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject& DebugScopeObject* DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si) { - MOZ_ASSERT(!si.hasScopeObject()); + MOZ_ASSERT(!si.hasSyntacticScopeObject()); DebugScopes* scopes = cx->compartment()->debugScopes; if (!scopes) @@ -2085,7 +2043,7 @@ DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si) bool DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope) { - MOZ_ASSERT(!si.hasScopeObject()); + MOZ_ASSERT(!si.hasSyntacticScopeObject()); MOZ_ASSERT(cx->compartment() == debugScope.compartment()); MOZ_ASSERT_IF(si.withinInitialFrame() && si.initialFrame().isFunctionFrame(), !si.initialFrame().callee()->isGenerator()); @@ -2303,7 +2261,7 @@ DebugScopes::updateLiveScopes(JSContext* cx) continue; for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) { - if (si.hasScopeObject()) { + if (si.hasSyntacticScopeObject()) { MOZ_ASSERT(si.scope().compartment() == cx->compartment()); DebugScopes* scopes = ensureCompartmentData(cx); if (!scopes) @@ -2421,7 +2379,7 @@ GetDebugScopeForScope(JSContext* cx, const ScopeIter& si) static DebugScopeObject* GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) { - MOZ_ASSERT(!si.hasScopeObject() && si.canHaveScopeObject()); + MOZ_ASSERT(!si.hasSyntacticScopeObject() && si.canHaveSyntacticScopeObject()); if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, si)) return debugScope; @@ -2488,6 +2446,8 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) case ScopeIter::With: case ScopeIter::Eval: MOZ_CRASH("should already have a scope"); + case ScopeIter::NonSyntactic: + MOZ_CRASH("non-syntactic scopes cannot be synthesized"); } if (!debugScope) return nullptr; @@ -2498,15 +2458,15 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) return debugScope; } -static JSObject * -GetDebugScopeForNonScopeObject(const ScopeIter &si) +static JSObject* +GetDebugScopeForNonScopeObject(const ScopeIter& si) { - JSObject &enclosing = si.enclosingScope(); - MOZ_ASSERT(!IsSyntacticScope(&enclosing)); + JSObject& enclosing = si.enclosingScope(); + MOZ_ASSERT(!enclosing.is()); #ifdef DEBUG - JSObject *o = &enclosing; + JSObject* o = &enclosing; while ((o = o->enclosingScope())) - MOZ_ASSERT(!IsSyntacticScope(o)); + MOZ_ASSERT(!o->is()); #endif return &enclosing; } @@ -2519,10 +2479,10 @@ GetDebugScope(JSContext* cx, const ScopeIter& si) if (si.done()) return GetDebugScopeForNonScopeObject(si); - if (si.hasScopeObject()) + if (si.hasAnyScopeObject()) return GetDebugScopeForScope(cx, si); - if (si.canHaveScopeObject()) + if (si.canHaveSyntacticScopeObject()) return GetDebugScopeForMissing(cx, si); ScopeIter copy(cx, si); @@ -2566,10 +2526,9 @@ js::GetObjectEnvironmentObjectForFunction(JSFunction *fun) } bool -js::CreateScopeObjectsForScopeChain(JSContext *cx, AutoObjectVector &scopeChain, +js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, HandleObject dynamicTerminatingScope, - MutableHandleObject dynamicScopeObj, - MutableHandleObject staticScopeObj) + MutableHandleObject dynamicScopeObj) { #ifdef DEBUG for (size_t i = 0; i < scopeChain.length(); ++i) { @@ -2599,10 +2558,23 @@ js::CreateScopeObjectsForScopeChain(JSContext *cx, AutoObjectVector &scopeChain, } dynamicScopeObj.set(dynamicEnclosingScope); - staticScopeObj.set(staticEnclosingScope); return true; } +bool +js::HasNonSyntacticStaticScopeChain(JSObject* staticScope) +{ + for (StaticScopeIter ssi(staticScope); !ssi.done(); ssi++) { + // If we hit a function scope, we can short circuit the logic, as + // scripts cache whether they are under a non-syntactic scope. + if (ssi.type() == StaticScopeIter::Function) + return ssi.funScript()->hasNonSyntacticScope(); + if (ssi.type() == StaticScopeIter::NonSyntactic) + return true; + } + return false; +} + #ifdef DEBUG typedef HashSet PropertyNameSet; @@ -2637,7 +2609,7 @@ RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remai case JSOP_GETGNAME: case JSOP_SETGNAME: case JSOP_STRICTSETGNAME: - if (script->hasPollutedGlobalScope()) + if (script->hasNonSyntacticScope()) name = script->getName(pc); else name = nullptr; diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 21fb20ff84..41b0dffc09 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -22,6 +22,7 @@ namespace frontend { struct Definition; } class StaticWithObject; class StaticEvalObject; +class StaticNonSyntacticScopeObjects; /*****************************************************************************/ @@ -62,6 +63,7 @@ class StaticScopeIter obj->is() || obj->is() || obj->is() || + obj->is() || obj->is()); } @@ -81,6 +83,7 @@ class StaticScopeIter obj->is() || obj->is() || obj->is() || + obj->is() || obj->is()); } @@ -95,16 +98,19 @@ class StaticScopeIter bool done() const; void operator++(int); - /* Return whether this static scope will be on the dynamic scope chain. */ - bool hasDynamicScopeObject() const; + // Return whether this static scope will have a syntactic scope (i.e. a + // ScopeObject that isn't a non-syntactic With or + // NonSyntacticVariablesObject) on the dynamic scope chain. + bool hasSyntacticDynamicScopeObject() const; Shape* scopeShape() const; - enum Type { Function, Block, With, NamedLambda, Eval }; + enum Type { Function, Block, With, NamedLambda, Eval, NonSyntactic }; Type type() const; StaticBlockObject& block() const; StaticWithObject& staticWith() const; StaticEvalObject& eval() const; + StaticNonSyntacticScopeObjects& nonSyntactic() const; JSScript* funScript() const; JSFunction& fun() const; }; @@ -175,26 +181,28 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc); * scope objects is: * * JSObject Generic object - * \ - * ScopeObject Engine-internal scope - * \ \ \ \ - * \ \ \ StaticEvalObject Placeholder so eval scopes may be iterated through - * \ \ \ - * \ \ DeclEnvObject Holds name of recursive/heavyweight named lambda - * \ \ - * \ CallObject Scope of entire function or strict eval - * \ - * NestedScopeObject Scope created for a statement - * \ \ \ - * \ \ StaticWithObject Template for "with" object in static scope chain - * \ \ - * \ DynamicWithObject Run-time "with" object on scope chain - * \ - * BlockObject Shared interface of cloned/static block objects - * \ \ - * \ ClonedBlockObject let, switch, catch, for - * \ - * StaticBlockObject See NB + * | + * ScopeObject---+---+ Engine-internal scope + * | | | | | + * | | | | StaticNonSyntacticScopeObjects See NB2 + * | | | | + * | | | StaticEvalObject Placeholder so eval scopes may be iterated through + * | | | + * | | DeclEnvObject Holds name of recursive/heavyweight named lambda + * | | + * | CallObject Scope of entire function or strict eval + * | + * NestedScopeObject Scope created for a statement + * | | | + * | | StaticWithObject Template for "with" object in static scope chain + * | | + * | DynamicWithObject Run-time "with" object on scope chain + * | + * BlockObject Shared interface of cloned/static block objects + * | | + * | ClonedBlockObject let, switch, catch, for + * | + * StaticBlockObject See NB * * This hierarchy represents more than just the interface hierarchy: reserved * slots in base classes are fixed for all derived classes. Thus, for example, @@ -206,6 +214,9 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc); * are cloned at runtime. These objects should never escape into the wild and * support a restricted set of ScopeObject operations. * + * NB2: StaticNonSyntacticScopeObjects notify either of 0+ non-syntactic + * DynamicWithObjects on the dynamic scope chain or a NonSyntacticScopeObject. + * * See also "Debug scope objects" below. */ @@ -340,12 +351,10 @@ class DeclEnvObject : public ScopeObject public: static const uint32_t RESERVED_SLOTS = 2; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND; - static const Class class_; static DeclEnvObject* - createTemplateObject(JSContext* cx, HandleFunction fun, gc::InitialHeap heap); + createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind); static DeclEnvObject* create(JSContext* cx, HandleObject enclosing, HandleFunction callee); @@ -354,16 +363,15 @@ class DeclEnvObject : public ScopeObject } }; -// Static eval scope template objects on the static scope. Created at the -// time of compiling the eval script, and set as its static enclosing scope. +// Static eval scope placeholder objects on the static scope chain. Created at +// the time of compiling the eval script, and set as its static enclosing +// scope. class StaticEvalObject : public ScopeObject { static const uint32_t STRICT_SLOT = 1; public: static const unsigned RESERVED_SLOTS = 2; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND; - static const Class class_; static StaticEvalObject* create(JSContext* cx, HandleObject enclosing); @@ -387,6 +395,34 @@ class StaticEvalObject : public ScopeObject } }; +// Static scope objects that stand in for one or more "polluting global" +// scopes on the dynamic scope chain. +// +// There are two flavors of polluting global scopes on the dynamic scope +// chain: +// +// 1. 0+ non-syntactic DynamicWithObjects. This static scope helps ScopeIter +// iterate these DynamicWithObjects. +// +// 2. 1 PlainObject that is a both a QualifiedVarObj and an UnqualifiedVarObj, +// created exclusively in js::ExecuteInGlobalAndReturnScope. +// +// Since this PlainObject is not a ScopeObject, ScopeIter cannot iterate +// through it. Instead, this PlainObject always comes after the syntactic +// portion of the dynamic scope chain in front of a GlobalObject. +class StaticNonSyntacticScopeObjects : public ScopeObject +{ + public: + static const unsigned RESERVED_SLOTS = 1; + static const Class class_; + + static StaticNonSyntacticScopeObjects* create(JSContext* cx, HandleObject enclosing); + + JSObject* enclosingScopeForStaticScopeIter() { + return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); + } +}; + class NestedScopeObject : public ScopeObject { public: @@ -436,8 +472,6 @@ class StaticWithObject : public NestedScopeObject { public: static const unsigned RESERVED_SLOTS = 1; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND; - static const Class class_; static StaticWithObject* create(ExclusiveContext* cx); @@ -452,8 +486,6 @@ class DynamicWithObject : public NestedScopeObject public: static const unsigned RESERVED_SLOTS = 4; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND; - static const Class class_; enum WithKind { @@ -505,8 +537,6 @@ class BlockObject : public NestedScopeObject public: static const unsigned RESERVED_SLOTS = 2; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND; - static const Class class_; /* Return the abstract stack depth right before entering this nested scope. */ @@ -593,7 +623,7 @@ class StaticBlockObject : public BlockObject * variable of the block isAliased. */ bool needsClone() { - return !getFixedSlot(RESERVED_SLOTS).isFalse(); + return numVariables() > 0 && !getSlot(RESERVED_SLOTS).isFalse(); } /* Frontend-only functions ***********************************************/ @@ -674,7 +704,7 @@ class ClonedBlockObject : public BlockObject * Create a new ClonedBlockObject with the same enclosing scope and * variable values as this. */ - static ClonedBlockObject *clone(ExclusiveContext *cx, Handle block); + static ClonedBlockObject* clone(JSContext* cx, Handle block); }; // Internal scope object used by JSOP_BINDNAME upon encountering an @@ -697,8 +727,6 @@ class UninitializedLexicalObject : public ScopeObject { public: static const unsigned RESERVED_SLOTS = 1; - static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND; - static const Class class_; static UninitializedLexicalObject* create(JSContext* cx, HandleObject enclosing); @@ -759,17 +787,20 @@ class ScopeIter inline JSObject& enclosingScope() const; // If !done(): - enum Type { Call, Block, With, Eval }; + enum Type { Call, Block, With, Eval, NonSyntactic }; Type type() const; - inline bool hasScopeObject() const; - inline bool canHaveScopeObject() const; + inline bool hasNonSyntacticScopeObject() const; + inline bool hasSyntacticScopeObject() const; + inline bool hasAnyScopeObject() const; + inline bool canHaveSyntacticScopeObject() const; ScopeObject& scope() const; JSObject* maybeStaticScope() const; StaticBlockObject& staticBlock() const { return ssi_.block(); } StaticWithObject& staticWith() const { return ssi_.staticWith(); } StaticEvalObject& staticEval() const { return ssi_.eval(); } + StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); } JSFunction& fun() const { return ssi_.fun(); } bool withinInitialFrame() const { return !!frame_; } @@ -1066,16 +1097,50 @@ ScopeIter::done() const } inline bool -ScopeIter::hasScopeObject() const +ScopeIter::hasSyntacticScopeObject() const { - return ssi_.hasDynamicScopeObject(); + return ssi_.hasSyntacticDynamicScopeObject(); } inline bool -ScopeIter::canHaveScopeObject() const +ScopeIter::hasNonSyntacticScopeObject() const { - // Non-strict eval scopes cannot have dynamic scope objects. - return !ssi_.done() && (type() != Eval || staticEval().isStrict()); + // The case we're worrying about here is a NonSyntactic static scope which + // has 0+ corresponding non-syntactic DynamicWithObject scopes or a + // NonSyntacticVariablesObject. + if (ssi_.type() == StaticScopeIter::NonSyntactic) { + MOZ_ASSERT_IF(scope_->is(), + !scope_->as().isSyntactic()); + return scope_->is(); + } + return false; +} + +inline bool +ScopeIter::hasAnyScopeObject() const +{ + return hasSyntacticScopeObject() || hasNonSyntacticScopeObject(); +} + +inline bool +ScopeIter::canHaveSyntacticScopeObject() const +{ + if (ssi_.done()) + return false; + + switch (type()) { + case Call: + return true; + case Block: + return true; + case With: + return true; + case Eval: + // Only strict eval scopes can have dynamic scope objects. + return staticEval().isStrict(); + case NonSyntactic: + return false; + } } inline JSObject& @@ -1091,10 +1156,11 @@ ScopeIter::enclosingScope() const } extern bool -CreateScopeObjectsForScopeChain(JSContext *cx, AutoObjectVector &scopeChain, +CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, HandleObject dynamicTerminatingScope, - MutableHandleObject dynamicScopeObj, - MutableHandleObject staticScopeObj); + MutableHandleObject dynamicScopeObj); + +bool HasNonSyntacticStaticScopeChain(JSObject* staticScope); #ifdef DEBUG bool diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 1cb933e459..85e718be0e 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1782,7 +1782,9 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject) js::gc::AllocKind kind = hasName ? gc::AllocKind::FUNCTION_EXTENDED : selfHostedFunction->getAllocKind(); - clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject); + MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global())); + clone = CloneFunctionAndScript(cx, selfHostedFunction, cx->global(), + /* newStaticScope = */ nullptr, kind); // To be able to re-lazify the cloned function, its name in the // self-hosting compartment has to be stored on the clone. if (clone && hasName) @@ -1866,22 +1868,17 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there // aren't any. MOZ_ASSERT(!sourceFun->isGenerator()); - RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); - if (!sourceScript) - return false; - MOZ_ASSERT(!sourceScript->enclosingStaticScope()); - JSScript* cscript = CloneScript(cx, nullptr, targetFun, sourceScript); - if (!cscript) - return false; - cscript->setFunction(targetFun); - MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs()); // The target function might have been relazified after it's flags changed. targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) | sourceFun->flags() | JSFunction::EXTENDED); - targetFun->setScript(cscript); MOZ_ASSERT(targetFun->isExtended()); - return true; + + RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); + if (!sourceScript) + return false; + MOZ_ASSERT(!sourceScript->enclosingStaticScope()); + return !!CloneScriptIntoFunction(cx, /* enclosingScope = */ nullptr, targetFun, sourceScript); } bool diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index f28d4784e2..69149c5080 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -348,7 +348,7 @@ InterpreterStack::pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const Ca MOZ_ALWAYS_INLINE bool InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, HandleFunction callee, HandleValue thisv, - HandleObject scopeChain) + HandleValue newTarget, HandleObject scopeChain) { MOZ_ASSERT(callee->isGenerator()); RootedScript script(cx, callee->getOrCreateScript(cx)); @@ -361,9 +361,11 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, LifoAlloc::Mark mark = allocator_.mark(); - // Include callee, |this|. + bool constructing = newTarget.isObject(); + + // Include callee, |this|, and maybe |new.target| unsigned nformal = callee->nargs(); - unsigned nvals = 2 + nformal + script->nslots(); + unsigned nvals = 2 + constructing + nformal + script->nslots(); uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvals * sizeof(Value)); if (!buffer) @@ -373,9 +375,12 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, argv[-2] = ObjectValue(*callee); argv[-1] = thisv; SetValueRangeToUndefined(argv, nformal); + if (constructing) + argv[nformal] = newTarget; - InterpreterFrame* fp = reinterpret_cast(argv + nformal); - InterpreterFrame::Flags flags = ToFrameFlags(INITIAL_NONE); + InterpreterFrame* fp = reinterpret_cast(argv + nformal + constructing); + InterpreterFrame::Flags flags = constructing ? ToFrameFlags(INITIAL_CONSTRUCT) + : ToFrameFlags(INITIAL_NONE); fp->mark_ = mark; fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, 0, flags); fp->resumeGeneratorFrame(scopeChain); @@ -965,10 +970,10 @@ InterpreterActivation::popInlineFrame(InterpreterFrame* frame) inline bool InterpreterActivation::resumeGeneratorFrame(HandleFunction callee, HandleValue thisv, - HandleObject scopeChain) + HandleValue newTarget, HandleObject scopeChain) { InterpreterStack& stack = cx_->asJSContext()->runtime()->interpreterStack(); - if (!stack.resumeGeneratorCallFrame(cx_->asJSContext(), regs_, callee, thisv, scopeChain)) + if (!stack.resumeGeneratorCallFrame(cx_->asJSContext(), regs_, callee, thisv, newTarget, scopeChain)) return false; MOZ_ASSERT(regs_.fp()->script()->compartment() == compartment_); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index cfa92a1ed8..a9d142a784 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -134,7 +134,12 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* #ifdef DEBUG RootedObject enclosingScope(cx, script->enclosingStaticScope()); for (StaticScopeIter i(enclosingScope); !i.done(); i++) { - if (i.hasDynamicScopeObject()) { + if (i.type() == StaticScopeIter::NonSyntactic) { + while (scope->is()) { + MOZ_ASSERT(!scope->as().isSyntactic()); + scope = &scope->as().enclosingScope(); + } + } else if (i.hasSyntacticDynamicScopeObject()) { switch (i.type()) { case StaticScopeIter::Function: MOZ_ASSERT(scope->as().callee().nonLazyScript() == i.funScript()); @@ -154,12 +159,15 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* case StaticScopeIter::Eval: scope = &scope->as().enclosingScope(); break; + case StaticScopeIter::NonSyntactic: + MOZ_CRASH("NonSyntactic should not have a syntactic scope"); + break; } } } // The scope chain is always ended by one or more non-syntactic - // ScopeObjects (viz. GlobalObject or a non-syntactic WithObject). + // ScopeObjects (viz. GlobalObject or an unqualified varobj). MOZ_ASSERT(!IsSyntacticScope(scope)); #endif } @@ -226,8 +234,7 @@ InterpreterFrame::epilogue(JSContext* cx) if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) DebugScopes::onPopStrictEvalScope(this); } else if (isDirectEvalFrame()) { - if (isDebuggerEvalFrame()) - MOZ_ASSERT(!IsSyntacticScope(scopeChain())); + MOZ_ASSERT_IF(isDebuggerEvalFrame(), !IsSyntacticScope(scopeChain())); } else { /* * Debugger.Object.prototype.evalInGlobal creates indirect eval @@ -263,7 +270,7 @@ InterpreterFrame::epilogue(JSContext* cx) if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) DebugScopes::onPopCall(this, cx); - if (isConstructing() && thisValue().isObject() && returnValue().isPrimitive()) + if (!fun()->isGenerator() && isConstructing() && thisValue().isObject() && returnValue().isPrimitive()) setReturnValue(ObjectValue(constructorThis())); } @@ -1119,7 +1126,7 @@ FrameIter::matchCallee(JSContext* cx, HandleFunction fun) const // expect both functions to have the same JSScript. If so, and if they are // different, then they cannot be equal. RootedObject global(cx, &fun->global()); - bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee, global); + bool useSameScript = CanReuseScriptForClone(fun->compartment(), currentCallee, global); if (useSameScript && (currentCallee->hasScript() != fun->hasScript() || currentCallee->nonLazyScript() != fun->nonLazyScript())) diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index ea0e61107a..3762a06b80 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -299,7 +299,9 @@ class InterpreterFrame CONSTRUCTING = 0x10, /* frame is for a constructor invocation */ - /* (0x20, 0x40 and 0x80 are unused) */ + RESUMED_GENERATOR = 0x20, /* frame is for a resumed generator invocation */ + + /* (0x40 and 0x80 are unused) */ /* Function prologue state */ HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */ @@ -830,6 +832,13 @@ class InterpreterFrame return !!(flags_ & CONSTRUCTING); } + void setResumedGenerator() { + flags_ |= RESUMED_GENERATOR; + } + bool isResumedGenerator() const { + return !!(flags_ & RESUMED_GENERATOR); + } + /* * These two queries should not be used in general: the presence/absence of * the call/args object is determined by the static(ish) properties of the @@ -953,7 +962,8 @@ class InterpreterRegs void popInlineFrame() { pc = fp_->prevpc(); - sp = fp_->prevsp() - fp_->numActualArgs() - 1 - fp_->isConstructing(); + unsigned spForNewTarget = fp_->isResumedGenerator() ? 0 : fp_->isConstructing(); + sp = fp_->prevsp() - fp_->numActualArgs() - 1 - spForNewTarget; fp_ = fp_->prev(); MOZ_ASSERT(fp_); } @@ -1027,7 +1037,7 @@ class InterpreterStack bool resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, HandleFunction callee, HandleValue thisv, - HandleObject scopeChain); + HandleValue newTarget, HandleObject scopeChain); inline void purge(JSRuntime* rt); @@ -1236,7 +1246,7 @@ class InterpreterActivation : public Activation inline void popInlineFrame(InterpreterFrame* frame); inline bool resumeGeneratorFrame(HandleFunction callee, HandleValue thisv, - HandleObject scopeChain); + HandleValue newTarget, HandleObject scopeChain); InterpreterFrame* current() const { return regs_.fp(); diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 0383935487..04f89ce2d7 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -155,6 +155,48 @@ JSString::dump() fputc('\n', stderr); } +void +JSString::dumpRepresentation(FILE* fp, int indent) const +{ + if (isRope()) asRope() .dumpRepresentation(fp, indent); + else if (isDependent()) asDependent() .dumpRepresentation(fp, indent); + else if (isExternal()) asExternal() .dumpRepresentation(fp, indent); + else if (isExtensible()) asExtensible() .dumpRepresentation(fp, indent); + else if (isInline()) asInline() .dumpRepresentation(fp, indent); + else if (isFlat()) asFlat() .dumpRepresentation(fp, indent); + else + MOZ_CRASH("Unexpected JSString representation"); +} + +void +JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const +{ + uint32_t flags = d.u1.flags; + // Print the string's address as an actual C++ expression, to facilitate + // copy-and-paste into a debugger. + fprintf(fp, "((%s*) %p) length: %zu flags: 0x%x", subclass, this, length(), flags); + if (flags & FLAT_BIT) fputs(" FLAT", fp); + if (flags & HAS_BASE_BIT) fputs(" HAS_BASE", fp); + if (flags & INLINE_CHARS_BIT) fputs(" INLINE_CHARS", fp); + if (flags & ATOM_BIT) fputs(" ATOM", fp); + if (isPermanentAtom()) fputs(" PERMANENT", fp); + if (flags & LATIN1_CHARS_BIT) fputs(" LATIN1", fp); + fputc('\n', fp); +} + +void +JSLinearString::dumpRepresentationChars(FILE* fp, int indent) const +{ + if (hasLatin1Chars()) { + fprintf(fp, "%*schars: ((Latin1Char*) %p) ", indent, "", rawLatin1Chars()); + dumpChars(rawLatin1Chars(), length()); + } else { + fprintf(fp, "%*schars: ((char16_t*) %p) ", indent, "", rawTwoByteChars()); + dumpChars(rawTwoByteChars(), length()); + } + fputc('\n', fp); +} + bool JSString::equals(const char* s) { @@ -263,6 +305,21 @@ JSRope::copyCharsInternal(ExclusiveContext* cx, ScopedJSFreePtr& out, return true; } +#ifdef DEBUG +void +JSRope::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, "JSRope"); + indent += 2; + + fprintf(fp, "%*sleft: ", indent, ""); + leftChild()->dumpRepresentation(fp, indent); + + fprintf(fp, "%*sright: ", indent, ""); + rightChild()->dumpRepresentation(fp, indent); +} +#endif + namespace js { template <> @@ -589,6 +646,19 @@ JSDependentString::undepend(ExclusiveContext* cx) : undependInternal(cx); } +#ifdef DEBUG +void +JSDependentString::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, "JSDependentString"); + indent += 2; + + fprintf(fp, "%*soffset: %zu\n", indent, "", baseOffset()); + fprintf(fp, "%*sbase: ", indent, ""); + base()->dumpRepresentation(fp, indent); +} +#endif + template /* static */ bool JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp) @@ -851,6 +921,17 @@ JSAtom::dump() fprintf(stderr, "JSAtom* (%p) = ", (void*) this); this->JSString::dump(); } + +void +JSExternalString::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, "JSExternalString"); + indent += 2; + + fprintf(fp, "%*sfinalizer: ((JSStringFinalizer*) %p)\n", indent, "", externalFinalizer()); + fprintf(fp, "%*sbase: ", indent, ""); + base()->dumpRepresentation(fp, indent); +} #endif /* DEBUG */ JSLinearString* @@ -1083,3 +1164,34 @@ template JSFlatString* NewStringCopyN(ExclusiveContext* cx, const Latin1Char* s, size_t n); } /* namespace js */ + +#ifdef DEBUG +void +JSExtensibleString::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, "JSExtensibleString"); + indent += 2; + + fprintf(fp, "%*scapacity: %zu\n", indent, "", capacity()); + dumpRepresentationChars(fp, indent); +} + +void +JSInlineString::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, + isFatInline() ? "JSFatInlineString" : "JSThinInlineString"); + indent += 2; + + dumpRepresentationChars(fp, indent); +} + +void +JSFlatString::dumpRepresentation(FILE* fp, int indent) const +{ + dumpRepresentationHeader(fp, indent, "JSFlatString"); + indent += 2; + + dumpRepresentationChars(fp, indent); +} +#endif diff --git a/js/src/vm/String.h b/js/src/vm/String.h index aefd13c6c5..41ae546357 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -490,6 +490,8 @@ class JSString : public js::gc::TenuredCell #ifdef DEBUG void dump(); void dumpCharsNoNewline(FILE* fp=stderr); + void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const; template static void dumpChars(const CharT* s, size_t len, FILE* fp=stderr); @@ -574,6 +576,10 @@ class JSRope : public JSString static size_t offsetOfRight() { return offsetof(JSRope, d.s.u3.right); } + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSRope) == sizeof(JSString), @@ -653,6 +659,10 @@ class JSLinearString : public JSString JS::AutoCheckCannotGC nogc; return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index]; } + +#ifdef DEBUG + void dumpRepresentationChars(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSLinearString) == sizeof(JSString), @@ -693,6 +703,10 @@ class JSDependentString : public JSLinearString inline static size_t offsetOfBase() { return offsetof(JSDependentString, d.s.u3.base); } + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSDependentString) == sizeof(JSString), @@ -754,6 +768,10 @@ class JSFlatString : public JSLinearString } inline void finalize(js::FreeOp* fop); + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSFlatString) == sizeof(JSString), @@ -771,6 +789,10 @@ class JSExtensibleString : public JSFlatString MOZ_ASSERT(JSString::isExtensible()); return d.s.u3.capacity; } + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSExtensibleString) == sizeof(JSString), @@ -799,6 +821,10 @@ class JSInlineString : public JSFlatString static size_t offsetOfInlineStorage() { return offsetof(JSInlineString, d.inlineStorageTwoByte); } + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSInlineString) == sizeof(JSString), @@ -907,6 +933,10 @@ class JSExternalString : public JSFlatString /* Only called by the GC for strings with the AllocKind::EXTERNAL_STRING kind. */ inline void finalize(js::FreeOp* fop); + +#ifdef DEBUG + void dumpRepresentation(FILE* fp, int indent) const; +#endif }; static_assert(sizeof(JSExternalString) == sizeof(JSString), diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 9f41732cbd..7d361f255d 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 287; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 288; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 397, +static_assert(JSErr_Limit == 398, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index 5074f6e1d7..62cbbb9208 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -25,16 +25,21 @@ #include "xpcprivate.h" // For xpc::OptionsBase #include "jswrapper.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/HoldDropJSObjects.h" #include "mozilla/scache/StartupCache.h" #include "mozilla/scache/StartupCacheUtils.h" #include "mozilla/unused.h" #include "nsContentUtils.h" #include "nsStringGlue.h" +#include "nsCycleCollectionParticipant.h" using namespace mozilla::scache; using namespace JS; using namespace xpc; using namespace mozilla; +using namespace mozilla::dom; class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase { public: @@ -44,17 +49,20 @@ public: , target(cx) , charset(NullString()) , ignoreCache(false) + , async(false) { } virtual bool Parse() { return ParseObject("target", &target) && ParseString("charset", charset) && - ParseBoolean("ignoreCache", &ignoreCache); + ParseBoolean("ignoreCache", &ignoreCache) && + ParseBoolean("async", &async); } RootedObject target; nsString charset; bool ignoreCache; + bool async; }; @@ -108,6 +116,328 @@ ReportError(JSContext* cx, const char* origMsg, nsIURI* uri) return ReportError(cx, msg.get()); } +nsresult +PrepareScript(nsIURI* uri, + JSContext* cx, + RootedObject& targetObj, + const char* uriStr, + const nsAString& charset, + const char* buf, + int64_t len, + bool reuseGlobal, + MutableHandleScript script, + MutableHandleFunction function) +{ + JS::CompileOptions options(cx); + options.setFileAndLine(uriStr, 1); + if (!charset.IsVoid()) { + char16_t* scriptBuf = nullptr; + size_t scriptLength = 0; + + nsresult rv = + nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast(buf), len, + charset, nullptr, scriptBuf, scriptLength); + + JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, + JS::SourceBufferHolder::GiveOwnership); + + if (NS_FAILED(rv)) { + return ReportError(cx, LOAD_ERROR_BADCHARSET, uri); + } + + if (!reuseGlobal) { + if (JS_IsGlobalObject(targetObj)) + JS::Compile(cx, options, srcBuf, script); + else + JS::CompileForNonSyntacticScope(cx, options, srcBuf, script); + } else { + AutoObjectVector scopeChain(cx); + if (!JS_IsGlobalObject(targetObj) && + !scopeChain.append(targetObj)) { + return NS_ERROR_OUT_OF_MEMORY; + } + // XXXbz do we really not care if the compile fails??? + JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr, + srcBuf, function); + } + } else { + // We only use lazy source when no special encoding is specified because + // the lazy source loader doesn't know the encoding. + if (!reuseGlobal) { + options.setSourceIsLazy(true); + if (JS_IsGlobalObject(targetObj)) + JS::Compile(cx, options, buf, len, script); + else + JS::CompileForNonSyntacticScope(cx, options, buf, len, script); + } else { + AutoObjectVector scopeChain(cx); + if (!JS_IsGlobalObject(targetObj) && + !scopeChain.append(targetObj)) { + return NS_ERROR_OUT_OF_MEMORY; + } + // XXXbz do we really not care if the compile fails??? + JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr, + buf, len, function); + } + } + return NS_OK; +} + +nsresult +EvalScript(JSContext* cx, + RootedObject& target_obj, + MutableHandleValue retval, + nsIURI* uri, + bool cache, + RootedScript& script, + RootedFunction& function) +{ + if (function) { + script = JS_GetFunctionScript(cx, function); + } + + bool ok = false; + if (function) { + ok = JS_CallFunction(cx, target_obj, function, JS::HandleValueArray::empty(), + retval); + } else { + if (JS_IsGlobalObject(target_obj)) { + ok = JS_ExecuteScript(cx, script, retval); + } else { + JS::AutoObjectVector scopeChain(cx); + ok = scopeChain.append(target_obj) && + JS_ExecuteScript(cx, scopeChain, script, retval); + } + } + + if (ok) { + JSAutoCompartment rac(cx, target_obj); + if (!JS_WrapValue(cx, retval)) + return NS_ERROR_UNEXPECTED; + } + + nsAutoCString cachePath; + JSVersion version = JS_GetVersion(cx); + cachePath.AppendPrintf("jssubloader/%d", version); + PathifyURI(uri, cachePath); + + nsCOMPtr secman = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + if (!secman) + return NS_OK; + + nsCOMPtr principal; + nsresult rv = secman->GetSystemPrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv) || !principal) + return rv; + + if (cache && ok && !!script) { + WriteCachedScript(StartupCache::GetSingleton(), + cachePath, cx, principal, script); + } + return NS_OK; +} + +class AsyncScriptLoader : public nsIStreamLoaderObserver +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSISTREAMLOADEROBSERVER + + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader) + + AsyncScriptLoader(nsIChannel* aChannel, bool aReuseGlobal, + JSObject* aTargetObj, const nsAString& aCharset, + bool aCache, Promise* aPromise) + : mChannel(aChannel) + , mTargetObj(aTargetObj) + , mPromise(aPromise) + , mCharset(aCharset) + , mReuseGlobal(aReuseGlobal) + , mCache(aCache) + { + // Needed for the cycle collector to manage mTargetObj. + mozilla::HoldJSObjects(this); + } + +private: + virtual ~AsyncScriptLoader() { + mozilla::DropJSObjects(this); + } + + nsRefPtr mChannel; + Heap mTargetObj; + nsRefPtr mPromise; + nsString mCharset; + bool mReuseGlobal; + bool mCache; +}; + +NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader) + NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) + tmp->mTargetObj = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader) + +class MOZ_STACK_CLASS AutoRejectPromise +{ + public: + AutoRejectPromise(JSContext* cx, + Promise* aPromise, + nsIGlobalObject* aGlobalObject) + : mCx(cx) + , mPromise(aPromise) + , mGlobalObject(aGlobalObject) {} + + ~AutoRejectPromise() { + if (mPromise) { + JS::Rooted undefined(mCx, JS::UndefinedValue()); + mPromise->MaybeReject(mCx, undefined); + } + } + + void ResolvePromise(HandleValue aResolveValue) { + mPromise->MaybeResolve(aResolveValue); + mPromise = nullptr; + } + + private: + JSContext* mCx; + nsRefPtr mPromise; + nsCOMPtr mGlobalObject; +}; + +NS_IMETHODIMP +AsyncScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + uint32_t aLength, + const uint8_t* aBuf) +{ + nsCOMPtr uri; + mChannel->GetURI(getter_AddRefs(uri)); + + nsCOMPtr globalObject = xpc::NativeGlobal(mTargetObj); + AutoEntryScript aes(globalObject, "async loadSubScript"); + JSContext* cx = aes.cx(); + AutoRejectPromise autoPromise(cx, mPromise, globalObject); + + if (NS_FAILED(aStatus)) { + ReportError(cx, "Unable to load script.", uri); + } + // Just notify that we are done with this load. + NS_ENSURE_SUCCESS(aStatus, NS_OK); + + if (aLength == 0) { + return ReportError(cx, LOAD_ERROR_NOCONTENT, uri); + } + + if (aLength > INT32_MAX) { + return ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri); + } + + RootedFunction function(cx); + RootedScript script(cx); + nsAutoCString spec; + uri->GetSpec(spec); + + RootedObject target_obj(cx, mTargetObj); + + nsresult rv = PrepareScript(uri, cx, target_obj, spec.get(), + mCharset, + reinterpret_cast(aBuf), aLength, + mReuseGlobal, &script, &function); + if (NS_FAILED(rv)) { + return rv; + } + + JS::Rooted retval(cx); + rv = EvalScript(cx, target_obj, &retval, uri, mCache, script, function); + + if (NS_SUCCEEDED(rv)) { + autoPromise.ResolvePromise(retval); + } + + return rv; +} + +nsresult +mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri, JSObject* targetObjArg, + const nsAString& charset, + nsIIOService* serv, bool reuseGlobal, + bool cache, MutableHandleValue retval) +{ + RootedObject target_obj(nsContentUtils::RootingCx(), targetObjArg); + + nsCOMPtr globalObject = xpc::NativeGlobal(target_obj); + ErrorResult result; + + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(globalObject))) { + return NS_ERROR_UNEXPECTED; + } + + nsRefPtr promise = Promise::Create(globalObject, result); + if (result.Failed()) { + promise = nullptr; + } + + DebugOnly asJS = ToJSValue(jsapi.cx(), promise, retval); + MOZ_ASSERT(asJS, "Should not fail to convert the promise to a JS value"); + + // We create a channel and call SetContentType, to avoid expensive MIME type + // lookups (bug 632490). + nsCOMPtr channel; + nsresult rv; + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, + nullptr, // aLoadGroup + nullptr, // aCallbacks + nsIRequest::LOAD_NORMAL, + serv); + + if (!NS_SUCCEEDED(rv)) { + return rv; + } + + channel->SetContentType(NS_LITERAL_CSTRING("application/javascript")); + + nsRefPtr loadObserver = + new AsyncScriptLoader(channel, + reuseGlobal, + target_obj, + charset, + cache, + promise); + + nsCOMPtr loader; + rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr listener = loader.get(); + return channel->AsyncOpen(listener, nullptr); +} + nsresult mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObjArg, const nsAString& charset, const char* uriStr, @@ -115,11 +445,11 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObj bool reuseGlobal, JS::MutableHandleScript script, JS::MutableHandleFunction function) { - RootedObject target_obj(cx, targetObjArg); - script.set(nullptr); function.set(nullptr); + RootedObject target_obj(cx, targetObjArg); + // We create a channel and call SetContentType, to avoid expensive MIME type // lookups (bug 632490). nsCOMPtr chan; @@ -157,56 +487,13 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObj nsCString buf; rv = NS_ReadInputStreamToString(instream, buf, len); - if (NS_FAILED(rv)) - return rv; + NS_ENSURE_SUCCESS(rv, rv); - JS::CompileOptions options(cx); - options.setFileAndLine(uriStr, 1); - if (!charset.IsVoid()) { - char16_t* scriptBuf = nullptr; - size_t scriptLength = 0; - - rv = nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast(buf.get()), len, - charset, nullptr, scriptBuf, scriptLength); - - JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, - JS::SourceBufferHolder::GiveOwnership); - - if (NS_FAILED(rv)) { - return ReportError(cx, LOAD_ERROR_BADCHARSET, uri); - } - - if (!reuseGlobal) { - options.setHasPollutedScope(!JS_IsGlobalObject(target_obj)); - JS::Compile(cx, options, srcBuf, script); - } else { - AutoObjectVector scopeChain(cx); - if (!JS_IsGlobalObject(target_obj) && - !scopeChain.append(target_obj)) { - return NS_ERROR_OUT_OF_MEMORY; - } - // XXXbz do we really not care if the compile fails??? - JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr, - srcBuf, function); - } - } else { - // We only use lazy source when no special encoding is specified because - // the lazy source loader doesn't know the encoding. - if (!reuseGlobal) { - options.setSourceIsLazy(true) - .setHasPollutedScope(!JS_IsGlobalObject(target_obj)); - JS::Compile(cx, options, buf.get(), len, script); - } else { - AutoObjectVector scopeChain(cx); - if (!JS_IsGlobalObject(target_obj) && - !scopeChain.append(target_obj)) { - return NS_ERROR_OUT_OF_MEMORY; - } - // XXXbz do we really not care if the compile fails??? - JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr, - buf.get(), len, function); - } - } + rv = PrepareScript(uri, cx, target_obj, uriStr, charset, + buf.get(), len, + reuseGlobal, + script, function); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -351,7 +638,6 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url, uriStr = tmp; } - bool writeScript = false; JSVersion version = JS_GetVersion(cx); nsAutoCString cachePath; cachePath.AppendPrintf("jssubloader/%d", version); @@ -361,45 +647,25 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url, RootedScript script(cx); if (cache && !options.ignoreCache) rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script); + + // If we are doing an async load, trigger it and bail out. + if (!script && options.async) { + return ReadScriptAsync(uri, targetObj, options.charset, serv, + reusingGlobal, !!cache, retval); + } + if (!script) { rv = ReadScript(uri, cx, targetObj, options.charset, static_cast(uriStr.get()), serv, principal, reusingGlobal, &script, &function); - writeScript = !!script; + } else { + cache = nullptr; } if (NS_FAILED(rv) || (!script && !function)) return rv; - if (function) { - script = JS_GetFunctionScript(cx, function); - } - - bool ok = false; - if (function) { - ok = JS_CallFunction(cx, targetObj, function, JS::HandleValueArray::empty(), - retval); - } else { - if (JS_IsGlobalObject(targetObj)) { - ok = JS_ExecuteScript(cx, script, retval); - } else { - JS::AutoObjectVector scopeChain(cx); - ok = scopeChain.append(targetObj) && - JS_ExecuteScript(cx, scopeChain, script, retval); - } - } - - if (ok) { - JSAutoCompartment rac(cx, result_obj); - if (!JS_WrapValue(cx, retval)) - return NS_ERROR_UNEXPECTED; - } - - if (cache && ok && writeScript) { - WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script); - } - - return NS_OK; + return EvalScript(cx, targetObj, retval, uri, !!cache, script, function); } /** diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.h b/js/xpconnect/loader/mozJSSubScriptLoader.h index 011357529d..91a4439443 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.h +++ b/js/xpconnect/loader/mozJSSubScriptLoader.h @@ -39,6 +39,11 @@ private: bool reuseGlobal, JS::MutableHandleScript script, JS::MutableHandleFunction function); + nsresult ReadScriptAsync(nsIURI* uri, JSObject* target_obj, + const nsAString& charset, + nsIIOService* serv, bool reuseGlobal, + bool cache, JS::MutableHandleValue retval); + nsresult DoLoadSubScriptWithOptions(const nsAString& url, LoadSubScriptOptions& options, JSContext* cx, diff --git a/js/xpconnect/tests/unit/bug451678_subscript.js b/js/xpconnect/tests/unit/bug451678_subscript.js index f67b8a2a9f..72ff49a2d6 100644 --- a/js/xpconnect/tests/unit/bug451678_subscript.js +++ b/js/xpconnect/tests/unit/bug451678_subscript.js @@ -1,2 +1,5 @@ var tags = []; function makeTags() {} + +// This will be the return value of the script. +42 diff --git a/js/xpconnect/tests/unit/test_bug1150106.js b/js/xpconnect/tests/unit/test_bug1150106.js new file mode 100644 index 0000000000..0a93fb588d --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1150106.js @@ -0,0 +1,35 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +var srvScope = {}; + +function success(result) { + equal(result, 42, "Result of script is correct"); + ok('makeTags' in srvScope && srvScope.makeTags instanceof Function, + "makeTags is a function."); + do_test_finished(); +} + +function error() { + ok(false, "error loading the script asynchronously."); + do_test_finished(); +} + +function run_test() { + do_test_pending(); + + var file = do_get_file("bug451678_subscript.js"); + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var uri = ios.newFileURI(file); + var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + var p = scriptLoader.loadSubScriptWithOptions(uri.spec, + { target: srvScope, + async: true }); + p.then(success, error); +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 2f78cc4420..dcae88fec5 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -54,6 +54,7 @@ support-files = [test_bug1082450.js] [test_bug1081990.js] [test_bug1110546.js] +[test_bug1150106.js] [test_bug_442086.js] [test_callFunctionWithAsyncStack.js] [test_file.js] @@ -120,4 +121,4 @@ head = head_watchdog.js [test_writeToGlobalPrototype.js] [test_xpcwn_tamperproof.js] [test_xrayed_iterator.js] -[test_xray_SavedFrame.js] \ No newline at end of file +[test_xray_SavedFrame.js] diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index c4cf7830ee..e50cb263ba 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1047,7 +1047,7 @@ protected: * mask layer which has been used for aLayer before), or create one if such * a layer doesn't exist. */ - already_AddRefed CreateOrRecycleMaskImageLayerFor(Layer* aLayer); + already_AddRefed CreateOrRecycleMaskImageLayerFor(Layer* aLayer, const Maybe& aForAncestorMaskLayer); /** * Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes them * available for recycling. @@ -1149,6 +1149,12 @@ protected: const nsIntRegion& aLayerVisibleRegion, uint32_t aRoundedRectClipCount = UINT32_MAX); + already_AddRefed CreateMaskLayer( + Layer *aLayer, const DisplayItemClip& aClip, + const nsIntRegion& aLayerVisibleRegion, + const Maybe& aForAncestorMaskLayer, + uint32_t aRoundedRectClipCount = UINT32_MAX); + bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList, const nsIFrame **aAnimatedGeometryRoot); @@ -1877,10 +1883,10 @@ ContainerState::CreateOrRecycleImageLayer(PaintedLayer *aPainted) } already_AddRefed -ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer) +ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer, const Maybe& aForAncestorMaskLayer) { nsRefPtr result = mRecycledMaskImageLayers.Get(aLayer); - if (result) { + if (result && !aForAncestorMaskLayer) { mRecycledMaskImageLayers.Remove(aLayer); aLayer->ClearExtraDumpInfo(); // XXX if we use clip on mask layers, null it out here @@ -4259,9 +4265,16 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) nsAutoTArray metricsArray; if (aEntry->mBaseFrameMetrics) { metricsArray.AppendElement(*aEntry->mBaseFrameMetrics); + + // The base FrameMetrics was not computed by the nsIScrollableframe, so it + // should not have a mask layer. + MOZ_ASSERT(!aEntry->mBaseFrameMetrics->GetMaskLayerIndex()); } uint32_t baseLength = metricsArray.Length(); + // Any extra mask layers we need to attach to FrameMetrics. + nsTArray> maskLayers; + nsIFrame* fParent; for (const nsIFrame* f = aEntry->mAnimatedGeometryRoot; f != mContainerAnimatedGeometryRoot; @@ -4286,11 +4299,38 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) continue; } - scrollFrame->ComputeFrameMetrics(aEntry->mLayer, mContainerReferenceFrame, - mParameters, &metricsArray); + Maybe info = + scrollFrame->ComputeFrameMetrics(aEntry->mLayer, mContainerReferenceFrame, mParameters); + if (!info) { + continue; + } + + FrameMetrics& metrics = info->metrics; + const DisplayItemClip* clip = info->clip; + + if (clip && + clip->HasClip() && + clip->GetRoundedRectCount() > 0) + { + // The clip in between this scrollframe and its ancestor scrollframe + // requires a mask layer. Since this mask layer should not move with + // the APZC associated with this FrameMetrics, we attach the mask + // layer as an additional, separate clip. + Maybe nextIndex = Some(maskLayers.Length()); + nsRefPtr maskLayer = + CreateMaskLayer(aEntry->mLayer, *clip, aEntry->mVisibleRegion, nextIndex, clip->GetRoundedRectCount()); + if (maskLayer) { + metrics.SetMaskLayerIndex(nextIndex); + maskLayers.AppendElement(maskLayer); + } + } + + metricsArray.AppendElement(metrics); } + // Watch out for FrameMetrics copies in profiles aEntry->mLayer->SetFrameMetrics(metricsArray); + aEntry->mLayer->SetAncestorMaskLayers(maskLayers); } static void @@ -5513,8 +5553,27 @@ ContainerState::SetupMaskLayer(Layer *aLayer, return; } + nsRefPtr maskLayer = + CreateMaskLayer(aLayer, aClip, aLayerVisibleRegion, Nothing(), aRoundedRectClipCount); + + if (!maskLayer) { + SetClipCount(paintedData, 0); + return; + } + + aLayer->SetMaskLayer(maskLayer); + SetClipCount(paintedData, aRoundedRectClipCount); +} + +already_AddRefed +ContainerState::CreateMaskLayer(Layer *aLayer, + const DisplayItemClip& aClip, + const nsIntRegion& aLayerVisibleRegion, + const Maybe& aForAncestorMaskLayer, + uint32_t aRoundedRectClipCount) +{ // check if we can re-use the mask layer - nsRefPtr maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer); + nsRefPtr maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer, aForAncestorMaskLayer); MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer); MaskLayerUserData newData; @@ -5525,9 +5584,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel(); if (*userData == newData) { - aLayer->SetMaskLayer(maskLayer); - SetClipCount(paintedData, aRoundedRectClipCount); - return; + return maskLayer.forget(); } // calculate a more precise bounding rect @@ -5580,8 +5637,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, // fail if we can't get the right surface if (!dt) { NS_WARNING("Could not create DrawTarget for mask layer."); - SetClipCount(paintedData, 0); - return; + return nullptr; } nsRefPtr context = new gfxContext(dt); @@ -5626,9 +5682,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects); userData->mImageKey = lookupKey; - aLayer->SetMaskLayer(maskLayer); - SetClipCount(paintedData, aRoundedRectClipCount); - return; + return maskLayer.forget(); } } // namespace mozilla diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 13feb2fa97..0a1e5cc3dc 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -437,11 +437,10 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { nsIFrame* cb = cont->GetContainingBlock(); nsMargin newOffsets; - const nsSize size = cb->GetContentRectRelativeToSelf().Size(); + WritingMode wm = cb->GetWritingMode(); + const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size()); - nsHTMLReflowState::ComputeRelativeOffsets( - cb->StyleVisibility()->mDirection, - cont, size.width, size.height, newOffsets); + nsHTMLReflowState::ComputeRelativeOffsets(wm, cont, size, newOffsets); NS_ASSERTION(newOffsets.left == -newOffsets.right && newOffsets.top == -newOffsets.bottom, "ComputeRelativeOffsets should return valid results"); @@ -497,9 +496,9 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) const nsMargin& parentBorder = parentReflowState.mStyleBorder->GetComputedBorder(); cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom()); + LogicalSize lcbSize(frameWM, cbSize); nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState, - aFrame, availSize, - cbSize.width, cbSize.height); + aFrame, availSize, &lcbSize); nsSize computedSize(reflowState.ComputedWidth(), reflowState.ComputedHeight()); computedSize.width += reflowState.ComputedPhysicalBorderPadding().LeftRight(); if (computedSize.height != NS_INTRINSICSIZE) { diff --git a/layout/base/moz.build b/layout/base/moz.build index 46d2952fcd..98670bfc3e 100644 --- a/layout/base/moz.build +++ b/layout/base/moz.build @@ -76,7 +76,7 @@ EXPORTS += [ 'nsIFrameTraversal.h', 'nsILayoutDebugger.h', 'nsILayoutHistoryState.h', - 'nsIPercentHeightObserver.h', + 'nsIPercentBSizeObserver.h', 'nsIPresShell.h', 'nsIReflowCallback.h', 'nsLayoutUtils.h', diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index a35ce62b86..4970e86805 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1397,18 +1397,6 @@ nsFrameConstructorSaveState::~nsFrameConstructorSaveState() } } -static -bool IsBorderCollapse(nsIFrame* aFrame) -{ - for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { - if (nsGkAtoms::tableFrame == frame->GetType()) { - return ((nsTableFrame*)frame)->IsBorderCollapse(); - } - } - NS_ASSERTION(false, "program error"); - return false; -} - /** * Moves aFrameList from aOldParent to aNewParent. This updates the parent * pointer of the frames in the list, and reparents their views as needed. @@ -2190,7 +2178,8 @@ nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState, nsStyleContext* const styleContext = aItem.mStyleContext; const uint32_t nameSpaceID = aItem.mNameSpaceID; - bool borderCollapse = IsBorderCollapse(aParentFrame); + nsTableFrame* tableFrame = + static_cast(aParentFrame)->GetTableFrame(); nsContainerFrame* newFrame; // is border separate in mathml.css and the MathML code doesn't implement // border collapse. For those users who style with border collapse, @@ -2199,13 +2188,14 @@ nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState, // table code, and so we can freely mix with or , or . // What will happen is just that non-MathML frames won't understand MathML attributes // and will therefore miss the special handling that the MathML code does. - if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse) - newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext); - else + if (kNameSpaceID_MathML == nameSpaceID && !tableFrame->IsBorderCollapse()) { + newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext, tableFrame); + } else { // Warning: If you change this and add a wrapper frame around table cell // frames, make sure Bug 368554 doesn't regress! // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. - newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse); + newFrame = NS_NewTableCellFrame(mPresShell, styleContext, tableFrame); + } // Initialize the table cell frame InitAndRestoreFrame(aState, content, aParentFrame, newFrame); @@ -8852,8 +8842,10 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext, // Warning: If you change this and add a wrapper frame around table cell // frames, make sure Bug 368554 doesn't regress! // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. + nsTableFrame* tableFrame = + static_cast(aParentFrame)->GetTableFrame(); nsTableCellFrame* cellFrame = - NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame)); + NS_NewTableCellFrame(shell, styleContext, tableFrame); cellFrame->Init(content, aParentFrame, aFrame); if (cellFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index c27dfc4b8a..b22f9811d2 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1567,7 +1567,7 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB } if (addMetrics || ensureMetricsForRootId) { - bool isRoot = presContext->IsRootContentDocument(); + bool isRootContent = presContext->IsRootContentDocument(); nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize()); @@ -1576,7 +1576,10 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB rootScrollFrame, content, aBuilder->FindReferenceFrameFor(frame), root, FrameMetrics::NULL_SCROLL_ID, viewport, Nothing(), - isRoot, containerParameters)); + isRootContent, containerParameters)); + } else { + // Set empty metrics to clear any metrics that might be on a recycled layer. + root->SetFrameMetrics(nsTArray()); } // NS_WARNING is debug-only, so don't even bother checking the conditions in diff --git a/layout/base/nsIPercentHeightObserver.h b/layout/base/nsIPercentBSizeObserver.h similarity index 58% rename from layout/base/nsIPercentHeightObserver.h rename to layout/base/nsIPercentBSizeObserver.h index 29d0d7f04c..a933fe581f 100644 --- a/layout/base/nsIPercentHeightObserver.h +++ b/layout/base/nsIPercentBSizeObserver.h @@ -3,28 +3,29 @@ * 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 nsIPercentHeightObserver_h___ -#define nsIPercentHeightObserver_h___ +#ifndef nsIPercentBSizeObserver_h___ +#define nsIPercentBSizeObserver_h___ #include "nsQueryFrame.h" struct nsHTMLReflowState; /** - * This interface is supported by frames that need to provide computed height - * values to children during reflow which would otherwise not happen. Currently only - * table cells support this. + * This interface is supported by frames that need to provide computed bsize + * values to children during reflow which would otherwise not happen. Currently + * only table cells support this. */ -class nsIPercentHeightObserver +class nsIPercentBSizeObserver { public: - NS_DECL_QUERYFRAME_TARGET(nsIPercentHeightObserver) + NS_DECL_QUERYFRAME_TARGET(nsIPercentBSizeObserver) - // Notify the observer that aReflowState has no computed height, but it has a percent height - virtual void NotifyPercentHeight(const nsHTMLReflowState& aReflowState) = 0; + // Notify the observer that aReflowState has no computed bsize, + // but it has a percent bsize + virtual void NotifyPercentBSize(const nsHTMLReflowState& aReflowState) = 0; // Ask the observer if it should observe aReflowState.frame virtual bool NeedsToObserve(const nsHTMLReflowState& aReflowState) = 0; }; -#endif // nsIPercentHeightObserver_h___ +#endif // nsIPercentBSizeObserver_h___ diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index fdb305be67..984d776559 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8254,7 +8254,7 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, ViewID aScrollParentId, const nsRect& aViewport, const Maybe& aClipRect, - bool aIsRoot, + bool aIsRootContent, const ContainerLayerParameters& aContainerParameters) { nsPresContext* presContext = aForFrame->PresContext(); @@ -8333,7 +8333,7 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, // overscroll handoff chain. MOZ_ASSERT(aScrollParentId == FrameMetrics::NULL_SCROLL_ID || scrollId != aScrollParentId); metrics.SetScrollId(scrollId); - metrics.SetIsRoot(aIsRoot); + metrics.SetIsRootContent(aIsRootContent); metrics.SetScrollParentId(aScrollParentId); if (scrollId != FrameMetrics::NULL_SCROLL_ID && !presContext->GetParentPresContext()) { diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index e391c02bff..6f04c0703d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9259,7 +9259,7 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible) // was reflowed by its parent. nsMargin currentBorder = target->GetUsedBorder(); nsMargin currentPadding = target->GetUsedPadding(); - reflowState.Init(mPresContext, -1, -1, ¤tBorder, ¤tPadding); + reflowState.Init(mPresContext, nullptr, ¤tBorder, ¤tPadding); } // fix the computed height diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 297dcc9bfb..6d43f9a4ec 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -495,10 +495,10 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, - innerAvailSize, -1, -1, + innerAvailSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding - kidReflowState.Init(aPresContext, -1, -1, nullptr, + kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 5b966d046a..f09ea109b6 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -559,10 +559,10 @@ nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid, availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, - aKid, availSize, -1, -1, + aKid, availSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override padding with our computed padding in case we got it from theming or percentage - kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding()); + kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Set computed width and computed height for the child kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h index d88abb522f..b00200b528 100644 --- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -1262,6 +1262,13 @@ public: IStart() + aMargin.IStart()); } + LogicalMargin operator+=(const LogicalMargin& aMargin) + { + CHECK_WRITING_MODE(aMargin.GetWritingMode()); + mMargin += aMargin.mMargin; + return *this; + } + LogicalMargin operator-(const LogicalMargin& aMargin) const { CHECK_WRITING_MODE(aMargin.GetWritingMode()); return LogicalMargin(GetWritingMode(), diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 8be0a09f0f..59aed179d2 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -373,7 +373,8 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat #endif // DEBUG WritingMode wm = aKidFrame->GetWritingMode(); - nscoord availISize = LogicalSize(wm, aContainingBlock.Size()).ISize(wm); + LogicalSize logicalCBSize(wm, aContainingBlock.Size()); + nscoord availISize = logicalCBSize.ISize(wm); if (availISize == -1) { NS_ASSERTION(aReflowState.ComputedSize(wm).ISize(wm) != NS_UNCONSTRAINEDSIZE, @@ -386,8 +387,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame, LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE), - aContainingBlock.width, - aContainingBlock.height); + &logicalCBSize); // Get the border values const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder(); @@ -415,28 +415,25 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat // width/height if ((NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().left) || (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().top)) { - nscoord aContainingBlockWidth = aContainingBlock.width; - nscoord aContainingBlockHeight = aContainingBlock.height; - if (-1 == aContainingBlockWidth) { + if (logicalCBSize.Width(wm) == -1) { // Get the containing block width/height - kidReflowState.ComputeContainingBlockRectangle(aPresContext, - &aReflowState, - aContainingBlockWidth, - aContainingBlockHeight); + logicalCBSize = + kidReflowState.ComputeContainingBlockRectangle(aPresContext, + &aReflowState); } if (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().left) { NS_ASSERTION(NS_AUTOOFFSET != kidReflowState.ComputedPhysicalOffsets().right, "Can't solve for both left and right"); - kidReflowState.ComputedPhysicalOffsets().left = aContainingBlockWidth - + kidReflowState.ComputedPhysicalOffsets().left = logicalCBSize.Width(wm) - kidReflowState.ComputedPhysicalOffsets().right - kidReflowState.ComputedPhysicalMargin().right - kidDesiredSize.Width() - kidReflowState.ComputedPhysicalMargin().left; } if (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().top) { - kidReflowState.ComputedPhysicalOffsets().top = aContainingBlockHeight - + kidReflowState.ComputedPhysicalOffsets().top = logicalCBSize.Height(wm) - kidReflowState.ComputedPhysicalOffsets().bottom - kidReflowState.ComputedPhysicalMargin().bottom - kidDesiredSize.Height() - diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 048a782cca..dd5fefd3bf 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -2080,7 +2080,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) || (aState.mReflowState.IsBResize() && - (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)); + (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)); // Reflow our last line if our availableBSize has increased // so that we (and our last child) pull up content as necessary diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 344e050f6d..3fea5f5ecd 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -209,7 +209,7 @@ GetBEndMarginClone(nsIFrame* aFrame, { if (aFrame->StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE) { - nsCSSOffsetState os(aFrame, aRenderingContext, aContentArea.Width(aWritingMode)); + nsCSSOffsetState os(aFrame, aRenderingContext, aContentArea.ISize(aWritingMode)); return os.ComputedLogicalMargin(). ConvertTo(aWritingMode, aFrame->GetWritingMode()).BEnd(aWritingMode); @@ -735,7 +735,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) "Float frame has wrong parent"); nsCSSOffsetState offsets(aFloat, mReflowState.rendContext, - mReflowState.ComputedWidth()); + mReflowState.ComputedISize()); nscoord floatMarginISize = FloatMarginISize(mReflowState, adjustedAvailableSpace.ISize(wm), diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 3fdde78372..eb6ef63434 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -652,11 +652,11 @@ nsCanvasFrame::Reflow(nsPresContext* aPresContext, kidReflowState(aPresContext, aReflowState, kidFrame, aReflowState.AvailableSize(kidFrame->GetWritingMode())); - if (aReflowState.IsVResize() && - (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { - // Tell our kid it's being vertically resized too. Bit of a + if (aReflowState.IsBResize() && + (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { + // Tell our kid it's being block-dir resized too. Bit of a // hack for framesets. - kidReflowState.SetVResize(true); + kidReflowState.SetBResize(true); } WritingMode wm = aReflowState.GetWritingMode(); diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 7dac50108f..fbebf744fd 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -596,8 +596,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, - availSize, kidCBSize.Width(wm), - kidCBSize.Height(wm)); + availSize, &kidCBSize); kidReflowState.mFlags.mIsTopOfPage = true; kidReflowState.mFlags.mTableIsSplittable = false; kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; @@ -1024,14 +1023,11 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext, // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; - // Our children depend on our height if we have a fixed height. - if (aReflowState.ComputedHeight() != NS_AUTOHEIGHT) { - NS_ASSERTION(aReflowState.ComputedHeight() != NS_INTRINSICSIZE, - "Unexpected computed height"); - AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); - } - else { - RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + // Our children depend on our block-size if we have a fixed block-size. + if (aReflowState.ComputedBSize() != NS_AUTOHEIGHT) { + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); + } else { + RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } #ifdef DEBUG diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 3241fc5be4..8ec958eec3 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -21,6 +21,7 @@ #include #include "mozilla/LinkedList.h" #include "mozilla/FloatingPoint.h" +#include "WritingModes.h" using namespace mozilla; using namespace mozilla::layout; @@ -42,8 +43,8 @@ GetFlexContainerLog() } // XXXdholbert Some of this helper-stuff should be separated out into a general -// "LogicalAxisUtils.h" helper. Should that be a class, or a namespace (under -// what super-namespace?), or what? +// "main/cross-axis utils" header, shared by grid & flexbox? +// (Particularly when grid gets support for align-*/justify-* properties.) // Helper enums // ============ @@ -93,13 +94,6 @@ AxisGrowsInPositiveDirection(AxisOrientationType aAxis) return eAxis_LR == aAxis || eAxis_TB == aAxis; } -// Indicates whether the given axis is horizontal. -static inline bool -IsAxisHorizontal(AxisOrientationType aAxis) -{ - return eAxis_LR == aAxis || eAxis_RL == aAxis; -} - // Given an AxisOrientationType, returns the "reverse" AxisOrientationType // (in the same dimension, but the opposite direction) static inline AxisOrientationType @@ -122,35 +116,25 @@ GetReverseAxis(AxisOrientationType aAxis) return reversedAxis; } -// Returns aFrame's computed value for 'height' or 'width' -- whichever is in -// the same dimension as aAxis. -static inline const nsStyleCoord& -GetSizePropertyForAxis(const nsIFrame* aFrame, AxisOrientationType aAxis) -{ - const nsStylePosition* stylePos = aFrame->StylePosition(); - - return IsAxisHorizontal(aAxis) ? - stylePos->mWidth : - stylePos->mHeight; -} - /** - * Converts a logical position in a given axis into a position in the - * corresponding physical (x or y) axis. If the logical axis already maps - * directly onto one of our physical axes (i.e. LTR or TTB), then the logical - * and physical positions are equal; otherwise, we subtract the logical - * position from the container-size in that axis, to flip the polarity. - * (so e.g. a logical position of 2px in a RTL 20px-wide container - * would correspond to a physical position of 18px.) + * Converts a "flex-relative" coordinate in a single axis (a main- or cross-axis + * coordinate) into a coordinate in the corresponding physical (x or y) axis. If + * the flex-relative axis in question already maps *directly* to a physical + * axis (i.e. if it's LTR or TTB), then the physical coordinate has the same + * numeric value as the provided flex-relative coordinate. Otherwise, we have to + * subtract the flex-relative coordinate from the flex container's size in that + * axis, to flip the polarity. (So e.g. a main-axis position of 2px in a RTL + * 20px-wide container would correspond to a physical coordinate (x-value) of + * 18px.) */ static nscoord -PhysicalPosFromLogicalPos(nscoord aLogicalPosn, - nscoord aLogicalContainerSize, - AxisOrientationType aAxis) { +PhysicalCoordFromFlexRelativeCoord(nscoord aFlexRelativeCoord, + nscoord aContainerSize, + AxisOrientationType aAxis) { if (AxisGrowsInPositiveDirection(aAxis)) { - return aLogicalPosn; + return aFlexRelativeCoord; } - return aLogicalContainerSize - aLogicalPosn; + return aContainerSize - aFlexRelativeCoord; } // Helper-macro to let us pick one of two expressions to evaluate @@ -162,20 +146,54 @@ PhysicalPosFromLogicalPos(nscoord aLogicalPosn, // evaluate), these macros will ensure that only the expression for the correct // axis gets evaluated. #define GET_MAIN_COMPONENT(axisTracker_, width_, height_) \ - IsAxisHorizontal((axisTracker_).GetMainAxis()) ? (width_) : (height_) + (axisTracker_).IsMainAxisHorizontal() ? (width_) : (height_) #define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \ - IsAxisHorizontal((axisTracker_).GetCrossAxis()) ? (width_) : (height_) + (axisTracker_).IsCrossAxisHorizontal() ? (width_) : (height_) + +// Logical versions of helper-macros above: +#define GET_MAIN_COMPONENT_LOGICAL(axisTracker_, isize_, bsize_) \ + (axisTracker_).IsRowOriented() ? (isize_) : (bsize_) + +#define GET_CROSS_COMPONENT_LOGICAL(axisTracker_, inline_, block_) \ + (axisTracker_).IsRowOriented() ? (block_) : (inline_) // Encapsulates our flex container's main & cross axes. class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker { public: - explicit FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame); + FlexboxAxisTracker(const nsStylePosition* aStylePosition, + const WritingMode& aWM); // Accessors: + // XXXdholbert [BEGIN DEPRECATED] AxisOrientationType GetMainAxis() const { return mMainAxis; } AxisOrientationType GetCrossAxis() const { return mCrossAxis; } + bool IsMainAxisHorizontal() const { + // If we're row-oriented, and our writing mode is NOT vertical, + // or we're column-oriented and our writing mode IS vertical, + // then our main axis is horizontal. This handles all cases: + return mIsRowOriented != mWM.IsVertical(); + } + bool IsCrossAxisHorizontal() const { + return !IsMainAxisHorizontal(); + } + // XXXdholbert [END DEPRECATED] + + // Returns true if our main axis is in the reverse direction of our + // writing mode's corresponding axis. (From 'flex-direction: *-reverse') + bool IsMainAxisReversed() const { + return mIsMainAxisReversed; + } + // Returns true if our cross axis is in the reverse direction of our + // writing mode's corresponding axis. (From 'flex-wrap: *-reverse') + bool IsCrossAxisReversed() const { + return mIsCrossAxisReversed; + } + + bool IsRowOriented() const { return mIsRowOriented; } + bool IsColumnOriented() const { return !mIsRowOriented; } + nscoord GetMainComponent(const nsSize& aSize) const { return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height); } @@ -191,46 +209,84 @@ public: } nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const { - return IsAxisHorizontal(mMainAxis) ? + return IsMainAxisHorizontal() ? aMargin.LeftRight() : aMargin.TopBottom(); } nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const { - return IsAxisHorizontal(mCrossAxis) ? + return IsCrossAxisHorizontal() ? aMargin.LeftRight() : aMargin.TopBottom(); } - /** - * Converts a logical point into a "physical" x,y point. - * - * In the simplest case where the main-axis is left-to-right and the - * cross-axis is top-to-bottom, this just returns - * nsPoint(aMainPosn, aCrossPosn). - * - * @arg aMainPosn The main-axis position -- i.e an offset from the - * main-start edge of the container's content box. - * @arg aCrossPosn The cross-axis position -- i.e an offset from the - * cross-start edge of the container's content box. - * @return A nsPoint representing the same position (in coordinates - * relative to the container's content box). - */ - nsPoint PhysicalPointFromLogicalPoint(nscoord aMainPosn, - nscoord aCrossPosn, - nscoord aContainerMainSize, - nscoord aContainerCrossSize) const { - nscoord physicalPosnInMainAxis = - PhysicalPosFromLogicalPos(aMainPosn, aContainerMainSize, mMainAxis); - nscoord physicalPosnInCrossAxis = - PhysicalPosFromLogicalPos(aCrossPosn, aContainerCrossSize, mCrossAxis); + // Returns aFrame's computed value for 'height' or 'width' -- whichever is in + // the cross-axis. (NOTE: This is cross-axis-specific for now. If we need a + // main-axis version as well, we could generalize or clone this function.) + const nsStyleCoord& ComputedCrossSize(const nsIFrame* aFrame) const { + const nsStylePosition* stylePos = aFrame->StylePosition(); - return IsAxisHorizontal(mMainAxis) ? - nsPoint(physicalPosnInMainAxis, physicalPosnInCrossAxis) : - nsPoint(physicalPosnInCrossAxis, physicalPosnInMainAxis); + return IsCrossAxisHorizontal() ? + stylePos->mWidth : + stylePos->mHeight; } - nsSize PhysicalSizeFromLogicalSizes(nscoord aMainSize, - nscoord aCrossSize) const { - return IsAxisHorizontal(mMainAxis) ? + + /** + * Converts a "flex-relative" point (a main-axis & cross-axis coordinate) + * into a LogicalPoint, using the flex container's writing mode. + * + * @arg aMainCoord The main-axis coordinate -- i.e an offset from the + * main-start edge of the flex container's content box. + * @arg aCrossCoord The cross-axis coordinate -- i.e an offset from the + * cross-start edge of the flex container's content box. + * @arg aContainerMainSize The main size of flex container's content box. + * @arg aContainerCrossSize The cross size of flex container's content box. + * @return A LogicalPoint, with the flex container's writing mode, that + * represents the same position. The logical coordinates are + * relative to the flex container's content box. + */ + LogicalPoint + LogicalPointFromFlexRelativePoint(nscoord aMainCoord, + nscoord aCrossCoord, + nscoord aContainerMainSize, + nscoord aContainerCrossSize) const { + nscoord logicalCoordInMainAxis = mIsMainAxisReversed ? + aContainerMainSize - aMainCoord : aMainCoord; + nscoord logicalCoordInCrossAxis = mIsCrossAxisReversed ? + aContainerCrossSize - aCrossCoord : aCrossCoord; + + return mIsRowOriented ? + LogicalPoint(mWM, logicalCoordInMainAxis, logicalCoordInCrossAxis) : + LogicalPoint(mWM, logicalCoordInCrossAxis, logicalCoordInMainAxis); + } + + /** + * Converts a "flex-relative" size (a main-axis & cross-axis size) + * into a LogicalSize, using the flex container's writing mode. + * + * @arg aMainSize The main-axis size. + * @arg aCrossSize The cross-axis size. + * @return A LogicalSize, with the flex container's writing mode, that + * represents the same size. + */ + LogicalSize LogicalSizeFromFlexRelativeSizes(nscoord aMainSize, + nscoord aCrossSize) const { + return mIsRowOriented ? + LogicalSize(mWM, aMainSize, aCrossSize) : + LogicalSize(mWM, aCrossSize, aMainSize); + } + + /** + * Converts a "flex-relative" size (a main-axis & cross-axis size) + * into nsSize (using physical coordinates). + * + * @arg aMainSize The main-axis size. + * @arg aCrossSize The cross-axis size. + * @return A nsSize that represents the same size, using the corresponding + * physical coordinates. + */ + nsSize PhysicalSizeFromFlexRelativeSizes(nscoord aMainSize, + nscoord aCrossSize) const { + return IsMainAxisHorizontal() ? nsSize(aMainSize, aCrossSize) : nsSize(aCrossSize, aMainSize); } @@ -244,8 +300,29 @@ public: } private: + // Delete copy-constructor & reassignment operator, to prevent accidental + // (unnecessary) copying. + FlexboxAxisTracker(const FlexboxAxisTracker&) = delete; + FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete; + + // XXXdholbert [BEGIN DEPRECATED] AxisOrientationType mMainAxis; AxisOrientationType mCrossAxis; + // XXXdholbert [END DEPRECATED] + + const WritingMode mWM; // The flex container's writing mode. + + bool mIsRowOriented; // Is our main axis the inline axis? + // (Are we 'flex-direction:row[-reverse]'?) + + bool mIsMainAxisReversed; // Is our main axis in the opposite direction + // as mWM's corresponding axis? (e.g. RTL vs LTR) + bool mIsCrossAxisReversed; // Is our cross axis in the opposite direction + // as mWM's corresponding axis? (e.g. BTT vs TTB) + + // Implementation detail -- this indicates whether we've decided to + // transparently reverse our axes & our child ordering, to avoid having + // frames flow from bottom to top in either axis (& to make pagination saner). bool mAreAxesInternallyReversed; }; @@ -323,11 +400,11 @@ public: // Returns the distance between this FlexItem's baseline and the cross-start // edge of its margin-box. Used in baseline alignment. - // (This function needs to be told what cross axis is & which edge we're - // measuring the baseline from, so that it can look up the appropriate - // components from mMargin.) - nscoord GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis, - AxisEdgeType aEdge) const; + // (This function needs to be told which edge we're measuring the baseline + // from, so that it can look up the appropriate components from mMargin.) + nscoord GetBaselineOffsetFromOuterCrossEdge( + AxisEdgeType aEdge, + const FlexboxAxisTracker& aAxisTracker) const; float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; } @@ -982,8 +1059,8 @@ IsOrderLEQ(nsIFrame* aFrame1, bool nsFlexContainerFrame::IsHorizontal() { - const FlexboxAxisTracker axisTracker(this); - return IsAxisHorizontal(axisTracker.GetMainAxis()); + const FlexboxAxisTracker axisTracker(StylePosition(), GetWritingMode()); + return axisTracker.IsMainAxisHorizontal(); } FlexItem* @@ -1008,15 +1085,15 @@ nsFlexContainerFrame::GenerateFlexItemForChild( // MAIN SIZES (flex base size, min/max size) // ----------------------------------------- - nscoord flexBaseSize = GET_MAIN_COMPONENT(aAxisTracker, - childRS.ComputedWidth(), - childRS.ComputedHeight()); - nscoord mainMinSize = GET_MAIN_COMPONENT(aAxisTracker, - childRS.ComputedMinWidth(), - childRS.ComputedMinHeight()); - nscoord mainMaxSize = GET_MAIN_COMPONENT(aAxisTracker, - childRS.ComputedMaxWidth(), - childRS.ComputedMaxHeight()); + nscoord flexBaseSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedISize(), + childRS.ComputedBSize()); + nscoord mainMinSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedMinISize(), + childRS.ComputedMinBSize()); + nscoord mainMaxSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedMaxISize(), + childRS.ComputedMaxBSize()); // This is enforced by the nsHTMLReflowState where these values come from: MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size"); @@ -1025,15 +1102,18 @@ nsFlexContainerFrame::GenerateFlexItemForChild( // Grab the cross size from the reflow state. This might be the right value, // or we might resolve it to something else in SizeItemInCrossAxis(); hence, // it's tentative. See comment under "Cross Size Determination" for more. - nscoord tentativeCrossSize = GET_CROSS_COMPONENT(aAxisTracker, - childRS.ComputedWidth(), - childRS.ComputedHeight()); - nscoord crossMinSize = GET_CROSS_COMPONENT(aAxisTracker, - childRS.ComputedMinWidth(), - childRS.ComputedMinHeight()); - nscoord crossMaxSize = GET_CROSS_COMPONENT(aAxisTracker, - childRS.ComputedMaxWidth(), - childRS.ComputedMaxHeight()); + nscoord tentativeCrossSize = + GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedISize(), + childRS.ComputedBSize()); + nscoord crossMinSize = + GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedMinISize(), + childRS.ComputedMinBSize()); + nscoord crossMaxSize = + GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + childRS.ComputedMaxISize(), + childRS.ComputedMaxBSize()); // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES // Check if we're a themed widget, in which case we might have a minimum @@ -1117,7 +1197,7 @@ IsCrossSizeDefinite(const nsHTMLReflowState& aItemReflowState, const FlexboxAxisTracker& aAxisTracker) { const nsStylePosition* pos = aItemReflowState.mStylePosition; - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aAxisTracker.IsCrossAxisHorizontal()) { return pos->mWidth.GetUnit() != eStyleUnit_Auto; } // else, vertical. (We need to use IsAutoHeight() to catch e.g. %-height @@ -1152,17 +1232,17 @@ CrossSizeToUseWithRatio(const FlexItem& aFlexItem, if (IsCrossSizeDefinite(aItemReflowState, aAxisTracker)) { // Definite cross size. - return GET_CROSS_COMPONENT(aAxisTracker, - aItemReflowState.ComputedWidth(), - aItemReflowState.ComputedHeight()); + return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + aItemReflowState.ComputedISize(), + aItemReflowState.ComputedBSize()); } if (aMinSizeFallback) { // Indefinite cross-size, and we're resolving main min-size, so we'll fall // back to ussing the cross min-size (which should be definite). - return GET_CROSS_COMPONENT(aAxisTracker, - aItemReflowState.ComputedMinWidth(), - aItemReflowState.ComputedMinHeight()); + return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + aItemReflowState.ComputedMinISize(), + aItemReflowState.ComputedMinBSize()); } // Indefinite cross-size. @@ -1180,7 +1260,7 @@ MainSizeFromAspectRatio(nscoord aCrossSize, MOZ_ASSERT(aAxisTracker.GetCrossComponent(aIntrinsicRatio) != 0, "Invalid ratio; will divide by 0! Caller should've checked..."); - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aAxisTracker.IsCrossAxisHorizontal()) { // cross axis horiz --> aCrossSize is a width. Converting to height. return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.height, aIntrinsicRatio.width); } @@ -1221,9 +1301,9 @@ PartiallyResolveAutoMinSize(const FlexItem& aFlexItem, // * the computed max-width (max-height), if that value is definite: nscoord maxSize = - GET_MAIN_COMPONENT(aAxisTracker, - aItemReflowState.ComputedMaxWidth(), - aItemReflowState.ComputedMaxHeight()); + GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, + aItemReflowState.ComputedMaxISize(), + aItemReflowState.ComputedMaxBSize()); if (maxSize != NS_UNCONSTRAINEDSIZE) { minMainSize = std::min(minMainSize, maxSize); } @@ -1297,7 +1377,7 @@ nsFlexContainerFrame:: { // (Note: We should never have a used flex-basis of "auto" if our main axis // is horizontal; width values should always be resolvable without reflow.) - const bool isMainSizeAuto = (!IsAxisHorizontal(aAxisTracker.GetMainAxis()) && + const bool isMainSizeAuto = (!aAxisTracker.IsMainAxisHorizontal() && NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize()); const bool isMainMinSizeAuto = aFlexItem.NeedsMinSizeAutoResolution(); @@ -1324,13 +1404,13 @@ nsFlexContainerFrame:: // XXXdholbert Maybe this should share logic with ComputeCrossSize()... // Alternately, maybe tentative container cross size should be passed down. nscoord containerCrossSize = - GET_CROSS_COMPONENT(aAxisTracker, - flexContainerRS->ComputedWidth(), - flexContainerRS->ComputedHeight()); + GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, + flexContainerRS->ComputedISize(), + flexContainerRS->ComputedBSize()); // Is container's cross size "definite"? // (Container's cross size is definite if cross-axis is horizontal, or if // cross-axis is vertical and the cross-size is not NS_AUTOHEIGHT.) - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis()) || + if (aAxisTracker.IsCrossAxisHorizontal() || containerCrossSize != NS_AUTOHEIGHT) { aFlexItem.ResolveStretchedCrossSize(containerCrossSize, aAxisTracker); } @@ -1364,7 +1444,7 @@ nsFlexContainerFrame:: // Measure content, if needed (w/ intrinsic-width method or a reflow) if (minSizeNeedsToMeasureContent || flexBasisNeedsToMeasureContent) { - if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { + if (aAxisTracker.IsMainAxisHorizontal()) { nsRenderingContext rctx( aPresContext->PresShell()->CreateReferenceRenderingContext()); if (minSizeNeedsToMeasureContent) { @@ -1422,7 +1502,7 @@ nsFlexContainerFrame:: nsHTMLReflowState childRSForMeasuringHeight(aPresContext, aParentReflowState, aFlexItem.Frame(), availSize, - -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); childRSForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true; childRSForMeasuringHeight.Init(aPresContext); @@ -1535,11 +1615,11 @@ FlexItem::FlexItem(nsHTMLReflowState& aFlexItemReflowState, // Moreover: for the time being (until we support writing-modes), // all inline axes are horizontal -- so we can just check if the cross axis // is horizontal. - // FIXME: Once we support writing-mode (vertical text), this IsAxisHorizontal - // check won't be sufficient anymore -- we'll actually need to compare our - // inline axis vs. the cross axis. + // FIXME: Once we support writing-mode (vertical text), this + // IsCrossAxisHorizontal check won't be sufficient anymore -- we'll actually + // need to compare our inline axis vs. the cross axis. if (mAlignSelf == NS_STYLE_ALIGN_ITEMS_BASELINE && - IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + aAxisTracker.IsCrossAxisHorizontal()) { mAlignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START; } } @@ -1609,19 +1689,21 @@ FlexItem::CheckForMinSizeAuto(const nsHTMLReflowState& aFlexItemReflowState, } nscoord -FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis, - AxisEdgeType aEdge) const +FlexItem::GetBaselineOffsetFromOuterCrossEdge( + AxisEdgeType aEdge, + const FlexboxAxisTracker& aAxisTracker) const { // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical // measurement -- it's the distance from the border-top edge of this FlexItem // to its baseline. So, we can really only do baseline alignment when the // cross axis is vertical. (The FlexItem constructor enforces this when // resolving the item's "mAlignSelf" value). - MOZ_ASSERT(!IsAxisHorizontal(aCrossAxis), + MOZ_ASSERT(!aAxisTracker.IsCrossAxisHorizontal(), "Only expecting to be doing baseline computations when the " "cross axis is vertical"); - mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge]; + AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis(); + mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge]; nscoord marginTopToBaseline = ResolvedAscent() + mMargin.top; @@ -1638,7 +1720,7 @@ FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis, // Measuring from bottom: The distance from the margin-box bottom edge to the // baseline is just the margin-box cross size (i.e. outer cross size), minus // the already-computed distance from margin-top to baseline. - return GetOuterCrossSize(aCrossAxis) - marginTopToBaseline; + return GetOuterCrossSize(crossAxis) - marginTopToBaseline; } uint32_t @@ -1684,7 +1766,7 @@ FlexItem::CanMainSizeInfluenceCrossSize( return false; } - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aAxisTracker.IsCrossAxisHorizontal()) { // If the cross axis is horizontal, then changes to the item's main size // (height) can't influence its cross size (width), if the item is a block // with a horizontal writing-mode. @@ -1728,42 +1810,47 @@ public: // Advances our current position from the start side of a child frame's // border-box to the frame's upper or left edge (depending on our axis). - // (Note that this is a no-op if our axis grows in positive direction.) + // (Note that this is a no-op if our axis grows in the same direction as + // the corresponding logical axis.) void EnterChildFrame(nscoord aChildFrameSize) { - if (!AxisGrowsInPositiveDirection(mAxis)) + if (mIsAxisReversed) { mPosition += aChildFrameSize; + } } // Advances our current position from a frame's upper or left border-box edge // (whichever is in the axis we're tracking) to the 'end' side of the frame // in the axis that we're tracking. (Note that this is a no-op if our axis - // grows in the negative direction.) + // is reversed with respect to the corresponding logical axis.) void ExitChildFrame(nscoord aChildFrameSize) { - if (AxisGrowsInPositiveDirection(mAxis)) + if (!mIsAxisReversed) { mPosition += aChildFrameSize; + } } protected: // Protected constructor, to be sure we're only instantiated via a subclass. - explicit PositionTracker(AxisOrientationType aAxis) + PositionTracker(AxisOrientationType aAxis, bool aIsAxisReversed) : mPosition(0), - mAxis(aAxis) + mAxis(aAxis), + mIsAxisReversed(aIsAxisReversed) {} -private: - // Private copy-constructor, since we don't want any instances of our - // subclasses to be accidentally copied. - PositionTracker(const PositionTracker& aOther) - : mPosition(aOther.mPosition), - mAxis(aOther.mAxis) - {} + // Delete copy-constructor & reassignment operator, to prevent accidental + // (unnecessary) copying. + PositionTracker(const PositionTracker&) = delete; + PositionTracker& operator=(const PositionTracker&) = delete; -protected: // Member data: nscoord mPosition; // The position we're tracking - const AxisOrientationType mAxis; // The axis along which we're moving + // XXXdholbert [BEGIN DEPRECATED] + const AxisOrientationType mAxis; // The axis along which we're moving. + // XXXdholbert [END DEPRECATED] + const bool mIsAxisReversed; // Is the axis along which we're moving reversed + // (e.g. LTR vs RTL) with respect to the + // corresponding axis on the flex container's WM? }; // Tracks our position in the main axis, when we're laying out flex items. @@ -2382,7 +2469,8 @@ MainAxisPositionTracker:: const FlexLine* aLine, uint8_t aJustifyContent, nscoord aContentBoxMainSize) - : PositionTracker(aAxisTracker.GetMainAxis()), + : PositionTracker(aAxisTracker.GetMainAxis(), + aAxisTracker.IsMainAxisReversed()), mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below mNumAutoMarginsInMainAxis(0), mNumPackingSpacesRemaining(0), @@ -2533,7 +2621,8 @@ CrossAxisPositionTracker:: nscoord aContentBoxCrossSize, bool aIsCrossSizeDefinite, const FlexboxAxisTracker& aAxisTracker) - : PositionTracker(aAxisTracker.GetCrossAxis()), + : PositionTracker(aAxisTracker.GetCrossAxis(), + aAxisTracker.IsCrossAxisReversed()), mPackingSpaceRemaining(0), mNumPackingSpacesRemaining(0), mAlignContent(aAlignContent) @@ -2686,7 +2775,8 @@ CrossAxisPositionTracker::TraversePackingSpace() SingleLineCrossAxisPositionTracker:: SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker) - : PositionTracker(aAxisTracker.GetCrossAxis()) + : PositionTracker(aAxisTracker.GetCrossAxis(), + aAxisTracker.IsCrossAxisReversed()) { } @@ -2732,8 +2822,8 @@ FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker) // crossEndToBaseline. nscoord crossStartToBaseline = - item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(), - eAxisEdge_Start); + item->GetBaselineOffsetFromOuterCrossEdge(eAxisEdge_Start, + aAxisTracker); nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline; // Now, update our "largest" values for these (across all the flex items @@ -2775,7 +2865,7 @@ FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize, // conditions don't hold up, we won't stretch. if (mAlignSelf != NS_STYLE_ALIGN_ITEMS_STRETCH || GetNumAutoMarginsInAxis(crossAxis) != 0 || - eStyleUnit_Auto != GetSizePropertyForAxis(mFrame, crossAxis).GetUnit()) { + eStyleUnit_Auto != aAxisTracker.ComputedCrossSize(mFrame).GetUnit()) { return; } @@ -2882,10 +2972,13 @@ SingleLineCrossAxisPositionTracker:: // Normally, baseline-aligned items are collectively aligned with the // line's cross-start edge; however, if our cross axis is (internally) // reversed, we instead align them with the cross-end edge. + AxisEdgeType baselineAlignEdge = + aAxisTracker.AreAxesInternallyReversed() ? + eAxisEdge_End : eAxisEdge_Start; + nscoord itemBaselineOffset = - aItem.GetBaselineOffsetFromOuterCrossEdge(mAxis, - aAxisTracker.AreAxesInternallyReversed() ? - eAxisEdge_End : eAxisEdge_Start); + aItem.GetBaselineOffsetFromOuterCrossEdge(baselineAlignEdge, + aAxisTracker); nscoord lineBaselineOffset = aLine.GetBaselineOffset(); @@ -2914,50 +3007,80 @@ SingleLineCrossAxisPositionTracker:: } } -FlexboxAxisTracker::FlexboxAxisTracker( - nsFlexContainerFrame* aFlexContainerFrame) - : mAreAxesInternallyReversed(false) +// Utility function to convert an InlineDir to an AxisOrientationType +static inline AxisOrientationType +InlineDirToAxisOrientation(WritingMode::InlineDir aInlineDir) { - const nsStylePosition* pos = aFlexContainerFrame->StylePosition(); - uint32_t flexDirection = pos->mFlexDirection; - uint32_t cssDirection = - aFlexContainerFrame->StyleVisibility()->mDirection; + switch (aInlineDir) { + case WritingMode::eInlineLTR: + return eAxis_LR; + case WritingMode::eInlineRTL: + return eAxis_RL; + case WritingMode::eInlineTTB: + return eAxis_TB; + case WritingMode::eInlineBTT: + return eAxis_BT; + } - MOZ_ASSERT(cssDirection == NS_STYLE_DIRECTION_LTR || - cssDirection == NS_STYLE_DIRECTION_RTL, - "Unexpected computed value for 'direction' property"); - // (Not asserting for flexDirection here; it's checked by the switch below.) + MOZ_ASSERT_UNREACHABLE("Unhandled InlineDir"); + return eAxis_LR; // in case of unforseen error, assume English LTR text flow. +} - // These are defined according to writing-modes' definitions of - // start/end (for the inline dimension) and before/after (for the block - // dimension), here: - // http://www.w3.org/TR/css3-writing-modes/#logical-directions - // (NOTE: I'm intentionally not calling this "inlineAxis"/"blockAxis", since - // those terms have explicit definition in the writing-modes spec, which are - // the opposite of how I'd be using them here.) - // XXXdholbert Once we support the 'writing-mode' property, use its value - // here to further customize inlineDimension & blockDimension. +// Utility function to convert a BlockDir to an AxisOrientationType +static inline AxisOrientationType +BlockDirToAxisOrientation(WritingMode::BlockDir aBlockDir) +{ + switch (aBlockDir) { + case WritingMode::eBlockLR: + return eAxis_LR; + case WritingMode::eBlockRL: + return eAxis_RL; + case WritingMode::eBlockTB: + return eAxis_TB; + // NOTE: WritingMode::eBlockBT (bottom-to-top) does not exist. + } + + MOZ_ASSERT_UNREACHABLE("Unhandled BlockDir"); + return eAxis_TB; // in case of unforseen error, assume English TTB block-flow +} + +FlexboxAxisTracker::FlexboxAxisTracker(const nsStylePosition* aStylePosition, + const WritingMode& aWM) + : mWM(aWM), + mAreAxesInternallyReversed(false) +{ + uint32_t flexDirection = aStylePosition->mFlexDirection; // Inline dimension ("start-to-end"): + // (NOTE: I'm intentionally not calling these "inlineAxis"/"blockAxis", since + // those terms have explicit definition in the writing-modes spec, which are + // the opposite of how I'd be using them here.) AxisOrientationType inlineDimension = - cssDirection == NS_STYLE_DIRECTION_RTL ? eAxis_RL : eAxis_LR; - - // Block dimension ("before-to-after"): - AxisOrientationType blockDimension = eAxis_TB; + InlineDirToAxisOrientation(mWM.GetInlineDir()); + AxisOrientationType blockDimension = + BlockDirToAxisOrientation(mWM.GetBlockDir()); // Determine main axis: switch (flexDirection) { case NS_STYLE_FLEX_DIRECTION_ROW: mMainAxis = inlineDimension; + mIsRowOriented = true; + mIsMainAxisReversed = false; break; case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE: mMainAxis = GetReverseAxis(inlineDimension); + mIsRowOriented = true; + mIsMainAxisReversed = true; break; case NS_STYLE_FLEX_DIRECTION_COLUMN: mMainAxis = blockDimension; + mIsRowOriented = false; + mIsMainAxisReversed = false; break; case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE: mMainAxis = GetReverseAxis(blockDimension); + mIsRowOriented = false; + mIsMainAxisReversed = true; break; default: MOZ_CRASH("Unexpected computed value for 'flex-flow' property"); @@ -2974,8 +3097,11 @@ FlexboxAxisTracker::FlexboxAxisTracker( } // "flex-wrap: wrap-reverse" reverses our cross axis. - if (pos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) { + if (aStylePosition->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) { mCrossAxis = GetReverseAxis(mCrossAxis); + mIsCrossAxisReversed = true; + } else { + mIsCrossAxisReversed = false; } // Master switch to enable/disable bug 983427's code for reversing our axes @@ -2991,11 +3117,10 @@ FlexboxAxisTracker::FlexboxAxisTracker( mMainAxis = GetReverseAxis(mMainAxis); mCrossAxis = GetReverseAxis(mCrossAxis); mAreAxesInternallyReversed = true; + mIsMainAxisReversed = !mIsMainAxisReversed; + mIsCrossAxisReversed = !mIsCrossAxisReversed; } } - - MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis), - "main & cross axes should be in different dimensions"); } // Allocates a new FlexLine, adds it to the given LinkedList (at the front or @@ -3018,7 +3143,7 @@ nsFlexContainerFrame::GenerateFlexLines( nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nscoord aContentBoxMainSize, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, const nsTArray& aStruts, const FlexboxAxisTracker& aAxisTracker, LinkedList& aLines) @@ -3049,22 +3174,23 @@ nsFlexContainerFrame::GenerateFlexLines( wrapThreshold = aContentBoxMainSize; // If the flex container doesn't have a definite content-box main-size - // (e.g. if we're 'height:auto'), make sure we at least wrap when we hit - // its max main-size. + // (e.g. if main axis is vertical & 'height' is 'auto'), make sure we at + // least wrap when we hit its max main-size. if (wrapThreshold == NS_UNCONSTRAINEDSIZE) { const nscoord flexContainerMaxMainSize = - GET_MAIN_COMPONENT(aAxisTracker, - aReflowState.ComputedMaxWidth(), - aReflowState.ComputedMaxHeight()); + GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, + aReflowState.ComputedMaxISize(), + aReflowState.ComputedMaxBSize()); wrapThreshold = flexContainerMaxMainSize; } - // Also: if we're vertical and paginating, we may need to wrap sooner - // (before we run off the end of the page) - if (!IsAxisHorizontal(aAxisTracker.GetMainAxis()) && - aAvailableHeightForContent != NS_UNCONSTRAINEDSIZE) { - wrapThreshold = std::min(wrapThreshold, aAvailableHeightForContent); + // Also: if we're column-oriented and paginating in the block dimension, + // we may need to wrap to a new flex line sooner (before we grow past the + // available BSize, potentially running off the end of the page). + if (aAxisTracker.IsColumnOriented() && + aAvailableBSizeForContent != NS_UNCONSTRAINEDSIZE) { + wrapThreshold = std::min(wrapThreshold, aAvailableBSizeForContent); } } @@ -3132,12 +3258,20 @@ nsFlexContainerFrame::GetMainSizeFromReflowState( const nsHTMLReflowState& aReflowState, const FlexboxAxisTracker& aAxisTracker) { - if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { - // Horizontal case is easy -- our main size is our computed width - // (which is already resolved). + if (aAxisTracker.IsRowOriented()) { + // Row-oriented --> our main axis is the inline axis, so our main size + // is our inline size (which should already be resolved). + // XXXdholbert ISize may be (wrongly) unconstrained right now: bug 1163238 + // Uncomment when that's fixed: + /* + NS_WARN_IF_FALSE(aReflowState.ComputedISize() != NS_UNCONSTRAINEDSIZE, + "Unconstrained inline size; this should only result from " + "huge sizes (not intrinsic sizing w/ orthogonal flows)"); + */ return aReflowState.ComputedISize(); } + // Note: This may be unconstrained, if our block size is "auto": return GetEffectiveComputedBSize(aReflowState); } @@ -3154,113 +3288,134 @@ GetLargestLineMainSize(const FlexLine* aFirstLine) return largestLineOuterSize; } -// Returns the content-box main-size of our flex container, based on the -// available height (if appropriate) and the main-sizes of the flex items. +/* Resolves the content-box main-size of a flex container frame, + * primarily based on: + * - the "tentative" main size, taken from the reflow state ("tentative" + * because it may be unconstrained or may run off the page). + * - the available BSize (needed if the main axis is the block axis). + * - the sizes of our lines of flex items. + * + * Guaranteed to return a definite length, i.e. not NS_UNCONSTRAINEDSIZE, + * aside from cases with huge lengths which happen to compute to that value. + * XXXdholbert (this^ isn't quite true, if we're row-oriented and in an + * orthogonal flow, per mentions of bug 1163238 in GetMainSizeFromReflowState.) + * + * (Note: This function should be structurally similar to 'ComputeCrossSize()', + * except that here, the caller has already grabbed the tentative size from the + * reflow state.) + */ static nscoord -ClampFlexContainerMainSize(const nsHTMLReflowState& aReflowState, - const FlexboxAxisTracker& aAxisTracker, - nscoord aUnclampedMainSize, - nscoord aAvailableHeightForContent, - const FlexLine* aFirstLine, - nsReflowStatus& aStatus) +ResolveFlexContainerMainSize(const nsHTMLReflowState& aReflowState, + const FlexboxAxisTracker& aAxisTracker, + nscoord aTentativeMainSize, + nscoord aAvailableBSizeForContent, + const FlexLine* aFirstLine, + nsReflowStatus& aStatus) { MOZ_ASSERT(aFirstLine, "null first line pointer"); - if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { - // Horizontal case is easy -- our main size should already be resolved - // before we get a call to Reflow. We don't have to worry about doing - // page-breaking or shrinkwrapping in the horizontal axis. - return aUnclampedMainSize; + if (aAxisTracker.IsRowOriented()) { + // Row-oriented --> our main axis is the inline axis, so our main size + // is our inline size (which should already be resolved). + return aTentativeMainSize; } - if (aUnclampedMainSize != NS_INTRINSICSIZE) { - // Vertical case, with fixed height: - if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE || - aUnclampedMainSize < aAvailableHeightForContent) { + if (aTentativeMainSize != NS_INTRINSICSIZE) { + // Column-oriented case, with fixed BSize: + if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE || + aTentativeMainSize < aAvailableBSizeForContent) { // Not in a fragmenting context, OR no need to fragment because we have - // more available height than we need. Either way, just use our fixed - // height. (Note that the reflow state has already done the appropriate - // min/max-height clamping.) - return aUnclampedMainSize; + // more available BSize than we need. Either way, we don't need to clamp. + // (Note that the reflow state has already done the appropriate + // min/max-BSize clamping.) + return aTentativeMainSize; } - // Fragmenting *and* our fixed height is too tall for available height: + // Fragmenting *and* our fixed BSize is larger than available BSize: // Mark incomplete so we get a next-in-flow, and take up all of the - // available height (or the amount of height required by our children, if - // that's larger; but of course not more than our own computed height). + // available BSize (or the amount of BSize required by our children, if + // that's larger; but of course not more than our own computed BSize). // XXXdholbert For now, we don't support pushing children to our next - // continuation or splitting children, so "amount of height required by - // our children" is just the main-size (height) of our longest flex line. + // continuation or splitting children, so "amount of BSize required by + // our children" is just the main-size (BSize) of our longest flex line. NS_FRAME_SET_INCOMPLETE(aStatus); nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine); - if (largestLineOuterSize <= aAvailableHeightForContent) { - return aAvailableHeightForContent; + if (largestLineOuterSize <= aAvailableBSizeForContent) { + return aAvailableBSizeForContent; } - return std::min(aUnclampedMainSize, largestLineOuterSize); + return std::min(aTentativeMainSize, largestLineOuterSize); } - // Vertical case, with auto-height: - // Resolve auto-height to the largest FlexLine-length, clamped to our - // computed min/max main-size properties (min-height & max-height). - // XXXdholbert Handle constrained-aAvailableHeightForContent case here. + // Column-oriented case, with auto BSize: + // Resolve auto BSize to the largest FlexLine length, clamped to our + // computed min/max main-size properties. + // XXXdholbert Handle constrained-aAvailableBSizeForContent case here. nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine); return NS_CSS_MINMAX(largestLineOuterSize, - aReflowState.ComputedMinHeight(), - aReflowState.ComputedMaxHeight()); + aReflowState.ComputedMinBSize(), + aReflowState.ComputedMaxBSize()); } nscoord nsFlexContainerFrame::ComputeCrossSize(const nsHTMLReflowState& aReflowState, const FlexboxAxisTracker& aAxisTracker, nscoord aSumLineCrossSizes, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, bool* aIsDefinite, nsReflowStatus& aStatus) { MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null"); - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { - // Cross axis is horizontal: our cross size is our computed width - // (which is already resolved). + if (aAxisTracker.IsColumnOriented()) { + // Column-oriented --> our cross axis is the inline axis, so our cross size + // is our inline size (which should already be resolved). + // XXXdholbert ISize may be (wrongly) unconstrained right now: bug 1163238. + // Uncomment when that's fixed: + /* + NS_WARN_IF_FALSE(aReflowState.ComputedISize() != NS_UNCONSTRAINEDSIZE, + "Unconstrained inline size; this should only result from " + "huge sizes (not intrinsic sizing w/ orthogonal flows)"); + */ *aIsDefinite = true; return aReflowState.ComputedISize(); } nscoord effectiveComputedBSize = GetEffectiveComputedBSize(aReflowState); if (effectiveComputedBSize != NS_INTRINSICSIZE) { - // Cross-axis is vertical, and we have a fixed height: + // Row-oriented case (cross axis is block-axis), with fixed BSize: *aIsDefinite = true; - if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE || - effectiveComputedBSize < aAvailableHeightForContent) { + if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE || + effectiveComputedBSize < aAvailableBSizeForContent) { // Not in a fragmenting context, OR no need to fragment because we have - // more available height than we need. Either way, just use our fixed - // height. (Note that the reflow state has already done the appropriate - // min/max-height clamping.) + // more available BSize than we need. Either way, just use our fixed + // BSize. (Note that the reflow state has already done the appropriate + // min/max-BSize clamping.) return effectiveComputedBSize; } - // Fragmenting *and* our fixed height is too tall for available height: + // Fragmenting *and* our fixed BSize is too tall for available BSize: // Mark incomplete so we get a next-in-flow, and take up all of the - // available height (or the amount of height required by our children, if - // that's larger; but of course not more than our own computed height). + // available BSize (or the amount of BSize required by our children, if + // that's larger; but of course not more than our own computed BSize). // XXXdholbert For now, we don't support pushing children to our next - // continuation or splitting children, so "amount of height required by - // our children" is just our line-height. + // continuation or splitting children, so "amount of BSize required by + // our children" is just the sum of our FlexLines' BSizes (cross sizes). NS_FRAME_SET_INCOMPLETE(aStatus); - if (aSumLineCrossSizes <= aAvailableHeightForContent) { - return aAvailableHeightForContent; + if (aSumLineCrossSizes <= aAvailableBSizeForContent) { + return aAvailableBSizeForContent; } return std::min(effectiveComputedBSize, aSumLineCrossSizes); } - // Cross axis is vertical and we have auto-height: shrink-wrap our line(s), - // subject to our min-size / max-size constraints in that (vertical) axis. - // XXXdholbert Handle constrained-aAvailableHeightForContent case here. + // Row-oriented case (cross axis is block axis), with auto BSize: + // Shrink-wrap our line(s), subject to our min-size / max-size + // constraints in that (block) axis. + // XXXdholbert Handle constrained-aAvailableBSizeForContent case here. *aIsDefinite = false; return NS_CSS_MINMAX(aSumLineCrossSizes, - aReflowState.ComputedMinHeight(), - aReflowState.ComputedMaxHeight()); + aReflowState.ComputedMinBSize(), + aReflowState.ComputedMaxBSize()); } void @@ -3293,20 +3448,22 @@ FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent, } /** - * Given the flex container's "logical ascent" (i.e. distance from the + * Given the flex container's "flex-relative ascent" (i.e. distance from the * flex container's content-box cross-start edge to its baseline), returns * its actual physical ascent value (the distance from the *border-box* top * edge to its baseline). */ static nscoord -ComputePhysicalAscentFromLogicalAscent(nscoord aLogicalAscent, - nscoord aContentBoxCrossSize, - const nsHTMLReflowState& aReflowState, - const FlexboxAxisTracker& aAxisTracker) +ComputePhysicalAscentFromFlexRelativeAscent( + nscoord aFlexRelativeAscent, + nscoord aContentBoxCrossSize, + const nsHTMLReflowState& aReflowState, + const FlexboxAxisTracker& aAxisTracker) { return aReflowState.ComputedPhysicalBorderPadding().top + - PhysicalPosFromLogicalPos(aLogicalAscent, aContentBoxCrossSize, - aAxisTracker.GetCrossAxis()); + PhysicalCoordFromFlexRelativeCoord(aFlexRelativeAscent, + aContentBoxCrossSize, + aAxisTracker.GetCrossAxis()); } void @@ -3316,7 +3473,7 @@ nsFlexContainerFrame::SizeItemInCrossAxis( nsHTMLReflowState& aChildReflowState, FlexItem& aItem) { - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aAxisTracker.IsCrossAxisHorizontal()) { MOZ_ASSERT(aItem.HasIntrinsicRatio(), "For now, caller's CanMainSizeInfluenceCrossSize check should " "only allow us to get here for items with intrinsic ratio"); @@ -3441,18 +3598,20 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, return; } - // We (and our children) can only depend on our ancestor's height if we have - // a percent-height, or if we're positioned and we have "top" and "bottom" - // set and have height:auto. (There are actually other cases, too -- e.g. if - // our parent is itself a vertical flex container and we're flexible -- but + // We (and our children) can only depend on our ancestor's bsize if we have + // a percent-bsize, or if we're positioned and we have "block-start" and "block-end" + // set and have block-size:auto. (There are actually other cases, too -- e.g. if + // our parent is itself a block-dir flex container and we're flexible -- but // we'll let our ancestors handle those sorts of cases.) + WritingMode wm = aReflowState.GetWritingMode(); const nsStylePosition* stylePos = StylePosition(); - if (stylePos->mHeight.HasPercent() || + const nsStyleCoord& bsize = stylePos->BSize(wm); + if (bsize.HasPercent() || (StyleDisplay()->IsAbsolutelyPositionedStyle() && - eStyleUnit_Auto == stylePos->mHeight.GetUnit() && - eStyleUnit_Auto != stylePos->mOffset.GetTopUnit() && - eStyleUnit_Auto != stylePos->mOffset.GetBottomUnit())) { - AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + eStyleUnit_Auto == bsize.GetUnit() && + eStyleUnit_Auto != stylePos->mOffset.GetBStartUnit(wm) && + eStyleUnit_Auto != stylePos->mOffset.GetBEndUnit(wm))) { + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } #ifdef DEBUG @@ -3477,18 +3636,20 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, SortChildrenIfNeeded(); } - const FlexboxAxisTracker axisTracker(this); + const FlexboxAxisTracker axisTracker(aReflowState.mStylePosition, + aReflowState.GetWritingMode()); - // If we're being fragmented into a constrained height, subtract off - // borderpadding-top from it, to get the available height for our - // content box. (Don't subtract if we're skipping top border/padding, - // though.) - nscoord availableHeightForContent = aReflowState.AvailableHeight(); - if (availableHeightForContent != NS_UNCONSTRAINEDSIZE && - !GetSkipSides().Top()) { - availableHeightForContent -= aReflowState.ComputedPhysicalBorderPadding().top; - // (Don't let that push availableHeightForContent below zero, though): - availableHeightForContent = std::max(availableHeightForContent, 0); + // If we're being fragmented into a constrained BSize, then subtract off + // borderpadding BStart from that constrained BSize, to get the available + // BSize for our content box. (No need to subtract the borderpadding BStart + // if we're already skipping it via GetLogicalSkipSides, though.) + nscoord availableBSizeForContent = aReflowState.AvailableBSize(); + if (availableBSizeForContent != NS_UNCONSTRAINEDSIZE && + !(GetLogicalSkipSides(&aReflowState).BStart())) { + availableBSizeForContent -= + aReflowState.ComputedLogicalBorderPadding().BStart(wm); + // (Don't let that push availableBSizeForContent below zero, though): + availableBSizeForContent = std::max(availableBSizeForContent, 0); } nscoord contentBoxMainSize = GetMainSizeFromReflowState(aReflowState, @@ -3496,13 +3657,13 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, nsAutoTArray struts; DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus, - contentBoxMainSize, availableHeightForContent, + contentBoxMainSize, availableBSizeForContent, struts, axisTracker); if (!struts.IsEmpty()) { // We're restarting flex layout, with new knowledge of collapsed items. DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus, - contentBoxMainSize, availableHeightForContent, + contentBoxMainSize, availableBSizeForContent, struts, axisTracker); } } @@ -3575,7 +3736,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, nscoord aContentBoxMainSize, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, nsTArray& aStruts, const FlexboxAxisTracker& aAxisTracker) { @@ -3586,13 +3747,13 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, GenerateFlexLines(aPresContext, aReflowState, aContentBoxMainSize, - aAvailableHeightForContent, + aAvailableBSizeForContent, aStruts, aAxisTracker, lines); aContentBoxMainSize = - ClampFlexContainerMainSize(aReflowState, aAxisTracker, - aContentBoxMainSize, aAvailableHeightForContent, - lines.getFirst(), aStatus); + ResolveFlexContainerMainSize(aReflowState, aAxisTracker, + aContentBoxMainSize, aAvailableBSizeForContent, + lines.getFirst(), aStatus); for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) { line->ResolveFlexibleLengths(aContentBoxMainSize); @@ -3626,7 +3787,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, item->Frame(), availSize); if (!sizeOverride) { // Directly override the computed main-size, by tweaking reflow state: - if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { + if (aAxisTracker.IsMainAxisHorizontal()) { childReflowState.SetComputedWidth(item->GetMainSize()); } else { childReflowState.SetComputedHeight(item->GetMainSize()); @@ -3645,7 +3806,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, bool isCrossSizeDefinite; const nscoord contentBoxCrossSize = ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes, - aAvailableHeightForContent, &isCrossSizeDefinite, aStatus); + aAvailableBSizeForContent, &isCrossSizeDefinite, aStatus); // Set up state for cross-axis alignment, at a high level (outside the // scope of a particular flex line) @@ -3680,7 +3841,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, flexContainerAscent = nscoord_MIN; } else { flexContainerAscent = - ComputePhysicalAscentFromLogicalAscent( + ComputePhysicalAscentFromFlexRelativeAscent( crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset, contentBoxCrossSize, aReflowState, aAxisTracker); } @@ -3714,7 +3875,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, flexContainerAscent = nscoord_MIN; } else { flexContainerAscent = - ComputePhysicalAscentFromLogicalAscent( + ComputePhysicalAscentFromFlexRelativeAscent( crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset, contentBoxCrossSize, aReflowState, aAxisTracker); } @@ -3723,55 +3884,61 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, // Before giving each child a final reflow, calculate the origin of the // flex container's content box (with respect to its border-box), so that // we can compute our flex item's final positions. - nsMargin containerBorderPadding(aReflowState.ComputedPhysicalBorderPadding()); - containerBorderPadding.ApplySkipSides(GetSkipSides(&aReflowState)); - const nsPoint containerContentBoxOrigin(containerBorderPadding.left, - containerBorderPadding.top); - WritingMode outerWM = aReflowState.GetWritingMode(); - nscoord containerWidth = IsAxisHorizontal(aAxisTracker.GetMainAxis()) ? + WritingMode flexWM = aReflowState.GetWritingMode(); + LogicalMargin containerBP = aReflowState.ComputedLogicalBorderPadding(); + + // Unconditionally skip block-end border & padding for now, regardless of + // writing-mode/GetLogicalSkipSides. We add it lower down, after we've + // established baseline and decided whether bottom border-padding fits (if + // we're fragmented). + const LogicalSides skipSides = + GetLogicalSkipSides(&aReflowState) | LogicalSides(eLogicalSideBitsBEnd); + containerBP.ApplySkipSides(skipSides); + + const LogicalPoint containerContentBoxOrigin(flexWM, + containerBP.IStart(flexWM), + containerBP.BStart(flexWM)); + + nscoord containerWidth = aAxisTracker.IsMainAxisHorizontal() ? aContentBoxMainSize : contentBoxCrossSize; + containerWidth += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); // FINAL REFLOW: Give each child frame another chance to reflow, now that // we know its final size and position. for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) { for (const FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) { - nsPoint physicalPosn = aAxisTracker.PhysicalPointFromLogicalPoint( + LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint( item->GetMainPosition(), item->GetCrossPosition(), aContentBoxMainSize, contentBoxCrossSize); - // Adjust physicalPosn to be relative to the container's border-box + // Adjust framePos to be relative to the container's border-box // (i.e. its frame rect), instead of the container's content-box: - physicalPosn += containerContentBoxOrigin; - - //XXX Can we calculate the logical position more directly instead - // of this double conversion? - nsSize finalFlexedPhysicalSize = - aAxisTracker.PhysicalSizeFromLogicalSizes(item->GetMainSize(), - item->GetCrossSize()); - LogicalPoint framePos(outerWM, physicalPosn, - containerWidth - finalFlexedPhysicalSize.width - - item->GetBorderPadding().LeftRight()); + framePos += containerContentBoxOrigin; // (Intentionally snapshotting this before ApplyRelativePositioning, to // maybe use for setting the flex container's baseline.) - const nscoord itemNormalBPos = framePos.B(outerWM); + const nscoord itemNormalBPos = framePos.B(flexWM); // Check if we actually need to reflow the item -- if we already reflowed // it with the right size, we can just reposition it as-needed. bool itemNeedsReflow = true; // (Start out assuming the worst.) if (item->HadMeasuringReflow()) { + LogicalSize finalFlexItemCBSize = + aAxisTracker.LogicalSizeFromFlexRelativeSizes(item->GetMainSize(), + item->GetCrossSize()); // We've already reflowed the child once. Was the size we gave it in // that reflow the same as its final (post-flexing/stretching) size? - if (finalFlexedPhysicalSize == - item->Frame()->GetContentRectRelativeToSelf().Size()) { + if (finalFlexItemCBSize == + LogicalSize(flexWM, + item->Frame()->GetContentRectRelativeToSelf().Size())) { // Even if our size hasn't changed, some of our descendants might - // care that our height is now considered "definite" (whereas it + // care that our bsize is now considered "definite" (whereas it // wasn't in our previous "measuring" reflow), if they have a - // relative height. + // relative bsize. if (!(item->Frame()->GetStateBits() & - NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { + NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { // Item has the correct size (and its children don't care that // it's now "definite"). Let's just make sure it's at the right // position. @@ -3796,9 +3963,14 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, } } + // XXXdholbert preserving the physical-coords var "containerBorderPadding" + // for now, while it has usages; removing it & its usages in bug 1155312. + nsMargin containerBorderPadding(aReflowState.ComputedPhysicalBorderPadding()); + containerBorderPadding.ApplySkipSides(GetSkipSides(&aReflowState)); + nsSize desiredContentBoxSize = - aAxisTracker.PhysicalSizeFromLogicalSizes(aContentBoxMainSize, - contentBoxCrossSize); + aAxisTracker.PhysicalSizeFromFlexRelativeSizes(aContentBoxMainSize, + contentBoxCrossSize); aDesiredSize.Width() = desiredContentBoxSize.width + containerBorderPadding.LeftRight(); @@ -3905,7 +4077,7 @@ nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext, bool didOverrideComputedHeight = false; // Override computed main-size - if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { + if (aAxisTracker.IsMainAxisHorizontal()) { childReflowState.SetComputedWidth(aItem.GetMainSize()); didOverrideComputedWidth = true; } else { @@ -3924,7 +4096,7 @@ nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext, // cheaper to just directly set it instead of setting a frame property.) if (aItem.IsStretched() || aItem.HasIntrinsicRatio()) { - if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aAxisTracker.IsCrossAxisHorizontal()) { childReflowState.SetComputedWidth(aItem.GetCrossSize()); didOverrideComputedWidth = true; } else { @@ -3932,9 +4104,9 @@ nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext, didOverrideComputedHeight = true; } } - if (aItem.IsStretched() && !IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { + if (aItem.IsStretched() && !aAxisTracker.IsCrossAxisHorizontal()) { // If this item's height is stretched, it's a relative height. - aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } // XXXdholbert Might need to actually set the correct margins in the @@ -3997,7 +4169,8 @@ nsFlexContainerFrame::GetMinISize(nsRenderingContext* aRenderingContext) nscoord minWidth = 0; DISPLAY_MIN_WIDTH(this, minWidth); - FlexboxAxisTracker axisTracker(this); + const nsStylePosition* stylePos = StylePosition(); + const FlexboxAxisTracker axisTracker(stylePos, GetWritingMode()); for (nsIFrame* childFrame : mFrames) { nscoord childMinWidth = @@ -4007,8 +4180,8 @@ nsFlexContainerFrame::GetMinISize(nsRenderingContext* aRenderingContext) // the sum of its items' min widths. // For a vertical flex container, or for a multi-line horizontal flex // container, the intrinsic min width is the max of its items' min widths. - if (IsAxisHorizontal(axisTracker.GetMainAxis()) && - NS_STYLE_FLEX_WRAP_NOWRAP == StylePosition()->mFlexWrap) { + if (axisTracker.IsMainAxisHorizontal() && + NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) { minWidth += childMinWidth; } else { minWidth = std::max(minWidth, childMinWidth); @@ -4028,13 +4201,13 @@ nsFlexContainerFrame::GetPrefISize(nsRenderingContext* aRenderingContext) // Whenever anything happens that might change it, set it to // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicISizesDirty // does) - FlexboxAxisTracker axisTracker(this); + const FlexboxAxisTracker axisTracker(StylePosition(), GetWritingMode()); for (nsIFrame* childFrame : mFrames) { nscoord childPrefWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame, nsLayoutUtils::PREF_ISIZE); - if (IsAxisHorizontal(axisTracker.GetMainAxis())) { + if (axisTracker.IsMainAxisHorizontal()) { prefWidth += childPrefWidth; } else { prefWidth = std::max(prefWidth, childPrefWidth); diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h index 3b7b07a1d4..df1d11418c 100644 --- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -86,7 +86,7 @@ protected: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, nscoord aContentBoxMainSize, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, nsTArray& aStruts, const FlexboxAxisTracker& aAxisTracker); @@ -151,7 +151,7 @@ protected: void GenerateFlexLines(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nscoord aContentBoxMainSize, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, const nsTArray& aStruts, const FlexboxAxisTracker& aAxisTracker, mozilla::LinkedList& aLines); @@ -162,7 +162,7 @@ protected: nscoord ComputeCrossSize(const nsHTMLReflowState& aReflowState, const FlexboxAxisTracker& aAxisTracker, nscoord aSumLineCrossSizes, - nscoord aAvailableHeightForContent, + nscoord aAvailableBSizeForContent, bool* aIsDefinite, nsReflowStatus& aStatus); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 7bffcb7638..1d73d948ee 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -54,7 +54,7 @@ #include "nsRange.h" #include "nsITextControlFrame.h" #include "nsNameSpaceManager.h" -#include "nsIPercentHeightObserver.h" +#include "nsIPercentBSizeObserver.h" #include "nsStyleStructInlines.h" #include "FrameLayerBuilder.h" @@ -4469,15 +4469,16 @@ nsFrame::DidReflow(nsPresContext* aPresContext, NS_FRAME_HAS_DIRTY_CHILDREN); } - // Notify the percent height observer if there is a percent height. + // Notify the percent bsize observer if there is a percent bsize. // The observer may be able to initiate another reflow with a computed - // height. This happens in the case where a table cell has no computed - // height but can fabricate one when the cell height is known. - if (aReflowState && aReflowState->mPercentHeightObserver && + // bsize. This happens in the case where a table cell has no computed + // bsize but can fabricate one when the cell bsize is known. + if (aReflowState && aReflowState->mPercentBSizeObserver && !GetPrevInFlow()) { - const nsStyleCoord &height = aReflowState->mStylePosition->mHeight; - if (height.HasPercent()) { - aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState); + const nsStyleCoord &bsize = + aReflowState->mStylePosition->BSize(aReflowState->GetWritingMode()); + if (bsize.HasPercent()) { + aReflowState->mPercentBSizeObserver->NotifyPercentBSize(*aReflowState); } } @@ -8584,7 +8585,7 @@ nsFrame::BoxReflow(nsBoxLayoutState& aState, LogicalSize logicalSize(wm, nsSize(aWidth, aHeight)); logicalSize.BSize(wm) = NS_INTRINSICSIZE; nsHTMLReflowState reflowState(aPresContext, *parentRS, this, - logicalSize, -1, -1, + logicalSize, nullptr, nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); // XXX_jwir3: This is somewhat fishy. If this is actually changing the value @@ -9110,8 +9111,7 @@ DR_init_constraints_cookie::~DR_init_constraints_cookie() DR_init_offsets_cookie::DR_init_offsets_cookie( nsIFrame* aFrame, nsCSSOffsetState* aState, - nscoord aHorizontalPercentBasis, - nscoord aVerticalPercentBasis, + const LogicalSize& aPercentBasis, const nsMargin* aMargin, const nsMargin* aPadding) : mFrame(aFrame) @@ -9119,8 +9119,7 @@ DR_init_offsets_cookie::DR_init_offsets_cookie( { MOZ_COUNT_CTOR(DR_init_offsets_cookie); mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState, - aHorizontalPercentBasis, - aVerticalPercentBasis, + aPercentBasis, aMargin, aPadding); } @@ -9753,8 +9752,8 @@ static void DisplayReflowEnterPrint(nsPresContext* aPresContext, if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) printf("dirty-children "); - if (aReflowState.mFlags.mSpecialHeightReflow) - printf("special-height "); + if (aReflowState.mFlags.mSpecialBSizeReflow) + printf("special-bsize "); if (aReflowState.IsHResize()) printf("h-resize "); @@ -10063,8 +10062,7 @@ nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame, /* static */ void* nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, nsCSSOffsetState* aState, - nscoord aHorizontalPercentBasis, - nscoord aVerticalPercentBasis, + const LogicalSize& aPercentBasis, const nsMargin* aBorder, const nsMargin* aPadding) { @@ -10081,8 +10079,9 @@ nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, char horizPctBasisStr[16]; char vertPctBasisStr[16]; - DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr, 16); - DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr, 16); + WritingMode wm = aState->GetWritingMode(); + DR_state->PrettyUC(aPercentBasis.ISize(wm), horizPctBasisStr, 16); + DR_state->PrettyUC(aPercentBasis.BSize(wm), vertPctBasisStr, 16); printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr); DR_state->PrintMargin("b", aBorder); diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index f40ebf84c8..fc5edc0131 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -822,8 +822,7 @@ public: struct DR_init_offsets_cookie { DR_init_offsets_cookie(nsIFrame* aFrame, nsCSSOffsetState* aState, - nscoord aHorizontalPercentBasis, - nscoord aVerticalPercentBasis, + const mozilla::LogicalSize& aPercentBasis, const nsMargin* aBorder, const nsMargin* aPadding); ~DR_init_offsets_cookie(); @@ -862,8 +861,8 @@ public: dr_bdr, dr_pad) \ DR_init_constraints_cookie dr_cookie(dr_frame, dr_state, dr_cbw, dr_cbh, \ dr_bdr, dr_pad) -#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) \ - DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) +#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad) \ + DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad) #define DISPLAY_INIT_TYPE(dr_frame, dr_result) \ DR_init_type_cookie dr_cookie(dr_frame, dr_result) @@ -880,7 +879,7 @@ public: #define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, \ dr_bdr, dr_pad) \ PR_BEGIN_MACRO PR_END_MACRO -#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_hpb, dr_vpb, dr_bdr, dr_pad) \ +#define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_bdr, dr_pad) \ PR_BEGIN_MACRO PR_END_MACRO #define DISPLAY_INIT_TYPE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO diff --git a/layout/generic/nsFrameIdList.h b/layout/generic/nsFrameIdList.h index b350200a48..480f416291 100644 --- a/layout/generic/nsFrameIdList.h +++ b/layout/generic/nsFrameIdList.h @@ -50,7 +50,7 @@ FRAME_ID(nsIMathMLFrame) FRAME_ID(nsIMenuFrame) FRAME_ID(nsIObjectFrame) FRAME_ID(nsIPageSequenceFrame) -FRAME_ID(nsIPercentHeightObserver) +FRAME_ID(nsIPercentBSizeObserver) FRAME_ID(nsIRootBox) FRAME_ID(nsISVGChildFrame) FRAME_ID(nsISVGSVGFrame) diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp index e28fa469ca..4381765962 100644 --- a/layout/generic/nsFrameSetFrame.cpp +++ b/layout/generic/nsFrameSetFrame.cpp @@ -862,7 +862,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext, nsIPresShell *shell = aPresContext->PresShell(); nsStyleSet *styleSet = shell->StyleSet(); - GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); //printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); // Always get the size so that the caller knows how big we are diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h index d0722645cf..eda65bc3b7 100644 --- a/layout/generic/nsFrameStateBits.h +++ b/layout/generic/nsFrameStateBits.h @@ -72,10 +72,10 @@ FRAME_STATE_BIT(Generic, 3, NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) FRAME_STATE_BIT(Generic, 4, NS_FRAME_EXTERNAL_REFERENCE) // If this bit is set, this frame or one of its descendants has a -// percentage height that depends on an ancestor of this frame. +// percentage block-size that depends on an ancestor of this frame. // (Or it did at one point in the past, since we don't necessarily clear // the bit when it's no longer needed; it's an optimization.) -FRAME_STATE_BIT(Generic, 5, NS_FRAME_CONTAINS_RELATIVE_HEIGHT) +FRAME_STATE_BIT(Generic, 5, NS_FRAME_CONTAINS_RELATIVE_BSIZE) // If this bit is set, then the frame corresponds to generated content FRAME_STATE_BIT(Generic, 6, NS_FRAME_GENERATED_CONTENT) @@ -550,7 +550,7 @@ FRAME_STATE_BIT(Placeholder, 23, PLACEHOLDER_FOR_POPUP) FRAME_STATE_GROUP(TableCell, nsTableCellFrame) -FRAME_STATE_BIT(TableCell, 28, NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT) +FRAME_STATE_BIT(TableCell, 28, NS_TABLE_CELL_HAS_PCT_OVER_BSIZE) FRAME_STATE_BIT(TableCell, 29, NS_TABLE_CELL_HAD_SPECIAL_REFLOW) FRAME_STATE_BIT(TableCell, 31, NS_TABLE_CELL_CONTENT_EMPTY) @@ -579,10 +579,10 @@ FRAME_STATE_BIT(TableRowAndRowGroup, 28, NS_REPEATED_ROW_OR_ROWGROUP) FRAME_STATE_GROUP(TableRow, nsTableRowFrame) // Indicates whether this row has any cells that have -// non-auto-height and rowspan=1 -FRAME_STATE_BIT(TableRow, 29, NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT) +// non-auto-bsize and rowspan=1 +FRAME_STATE_BIT(TableRow, 29, NS_ROW_HAS_CELL_WITH_STYLE_BSIZE) -FRAME_STATE_BIT(TableRow, 30, NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT) +FRAME_STATE_BIT(TableRow, 30, NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE) // == Frame state bits that apply to table row group frames =================== @@ -590,7 +590,7 @@ FRAME_STATE_BIT(TableRow, 30, NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT) FRAME_STATE_GROUP(TableRowGroup, nsTableRowGroupFrame) FRAME_STATE_BIT(TableRowGroup, 27, NS_ROWGROUP_HAS_ROW_CURSOR) -FRAME_STATE_BIT(TableRowGroup, 30, NS_ROWGROUP_HAS_STYLE_HEIGHT) +FRAME_STATE_BIT(TableRowGroup, 30, NS_ROWGROUP_HAS_STYLE_BSIZE) // thead or tfoot should be repeated on every printed page FRAME_STATE_BIT(TableRowGroup, 31, NS_ROWGROUP_REPEATABLE) diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b509695bec..c01f5d4f60 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -408,15 +408,18 @@ nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState, return true; } +// XXX Height/BSize mismatch needs to be addressed here; check the caller! +// Currently this will only behave as expected for horizontal writing modes. +// (See bug 1175509.) bool nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState* aState) { // Return true if ReflowScrolledFrame is going to do something different // based on the presence of a horizontal scrollbar. - return (mHelper.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) || - aState->mReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE || - aState->mReflowState.ComputedMinHeight() > 0 || - aState->mReflowState.ComputedMaxHeight() != NS_UNCONSTRAINEDSIZE; + return (mHelper.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) || + aState->mReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE || + aState->mReflowState.ComputedMinBSize() > 0 || + aState->mReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE; } void @@ -492,9 +495,9 @@ nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState, kidReflowState(presContext, aState->mReflowState, mHelper.mScrolledFrame, LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE), - -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); const nsMargin physicalPadding = padding.GetPhysicalMargin(wm); - kidReflowState.Init(presContext, -1, -1, nullptr, + kidReflowState.Init(presContext, nullptr, nullptr, &physicalPadding); kidReflowState.mFlags.mAssumingHScrollbar = aAssumeHScroll; kidReflowState.mFlags.mAssumingVScrollbar = aAssumeVScroll; @@ -1789,6 +1792,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter, , mResolution(1.0) , mScrollPosForLayerPixelAlignment(-1, -1) , mLastUpdateImagesPos(-1, -1) + , mAncestorClip(nullptr) , mNeverHasVerticalScrollbar(false) , mNeverHasHorizontalScrollbar(false) , mHasVerticalScrollbar(false) @@ -2754,6 +2758,9 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } + // Clear the scroll port clip that was set during the last paint. + mAncestorClip = nullptr; + // We put non-overlay scrollbars in their own layers when this is the root // scroll frame and we are a toplevel content document. In this situation, // the scrollbar(s) would normally be assigned their own layer anyway, since @@ -2936,6 +2943,11 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (usingDisplayport) { + // Capture the clip state of the parent scroll frame. This will be saved + // on FrameMetrics for layers with this frame as their animated geoemetry + // root. + mAncestorClip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); + // If we are using a display port, then ignore any pre-existing clip // passed down from our parents. The pre-existing clip would just defeat // the purpose of a display port which is to paint regions that are not @@ -3039,14 +3051,13 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, scrolledContent.MoveTo(aLists); } -void +Maybe ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - nsTArray* aOutput) const + const ContainerLayerParameters& aParameters) const { if (!mShouldBuildScrollableLayer || mIsScrollableLayerInRootContainer) { - return; + return Nothing(); } bool needsParentLayerClip = true; @@ -3056,18 +3067,22 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, } nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame); - bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument(); + bool isRootContent = mIsRoot && mOuter->PresContext()->IsRootContentDocument(); Maybe parentLayerClip; if (needsParentLayerClip) { nsRect clip = nsRect(mScrollPort.TopLeft() + toReferenceFrame, nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter)); - if (isRoot) { + if (isRootContent) { double res = mOuter->PresContext()->PresShell()->GetResolution(); clip.width = NSToCoordRound(clip.width / res); clip.height = NSToCoordRound(clip.height / res); } + if (mAncestorClip && mAncestorClip->HasClip()) { + clip = mAncestorClip->GetClipRect().Intersect(clip); + } + parentLayerClip = Some(clip); } @@ -3075,7 +3090,7 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, #if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ) // Android without apzc (aka the java pan zoom code) only uses async scrolling // for the root scroll frame of the root content document. - if (!isRoot) { + if (!isRootContent) { thisScrollFrameUsesAsyncScrolling = false; } #endif @@ -3100,17 +3115,21 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, } // Return early, since if we don't use APZ we don't need FrameMetrics. - return; + return Nothing(); } MOZ_ASSERT(mScrolledFrame->GetContent()); + FrameMetricsAndClip result; + nsRect scrollport = mScrollPort + toReferenceFrame; - *aOutput->AppendElement() = - nsLayoutUtils::ComputeFrameMetrics( - mScrolledFrame, mOuter, mOuter->GetContent(), - aContainerReferenceFrame, aLayer, mScrollParentID, - scrollport, parentLayerClip, isRoot, aParameters); + result.metrics = nsLayoutUtils::ComputeFrameMetrics( + mScrolledFrame, mOuter, mOuter->GetContent(), + aContainerReferenceFrame, aLayer, mScrollParentID, + scrollport, parentLayerClip, isRootContent, aParameters); + result.clip = mAncestorClip; + + return Some(result); } bool diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index fbe9bd351d..7f5fff8338 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -377,9 +377,9 @@ public: } } bool WantAsyncScroll() const; - void ComputeFrameMetrics(Layer* aLayer, nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - nsTArray* aOutput) const; + Maybe ComputeFrameMetrics( + Layer* aLayer, nsIFrame* aContainerReferenceFrame, + const ContainerLayerParameters& aParameters) const; // nsIScrollbarMediator void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection, @@ -458,6 +458,9 @@ public: FrameMetrics::ViewID mScrollParentID; + // The scroll port clip. Only valid during painting. + const DisplayItemClip* mAncestorClip; + bool mNeverHasVerticalScrollbar:1; bool mNeverHasHorizontalScrollbar:1; bool mHasVerticalScrollbar:1; @@ -831,11 +834,11 @@ public: virtual bool WantAsyncScroll() const override { return mHelper.WantAsyncScroll(); } - virtual void ComputeFrameMetrics(Layer* aLayer, nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - nsTArray* aOutput) const override { - mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, - aParameters, aOutput); + virtual mozilla::Maybe ComputeFrameMetrics( + Layer* aLayer, nsIFrame* aContainerReferenceFrame, + const ContainerLayerParameters& aParameters) const override + { + return mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, aParameters); } virtual bool IsIgnoringViewportClipping() const override { return mHelper.IsIgnoringViewportClipping(); @@ -1226,11 +1229,11 @@ public: virtual bool WantAsyncScroll() const override { return mHelper.WantAsyncScroll(); } - virtual void ComputeFrameMetrics(Layer* aLayer, nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - nsTArray* aOutput) const override { - mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, - aParameters, aOutput); + virtual mozilla::Maybe ComputeFrameMetrics( + Layer* aLayer, nsIFrame* aContainerReferenceFrame, + const ContainerLayerParameters& aParameters) const override + { + return mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, aParameters); } virtual bool IsIgnoringViewportClipping() const override { return mHelper.IsIgnoringViewportClipping(); diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index 6dd5d39a3a..4d1d37cbac 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -194,7 +194,7 @@ nsTableRowGroupFrame* NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); class nsTableCellFrame; nsTableCellFrame* -NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsBorderCollapse); +NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsTableFrame* aTableFrame); nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult, diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index ff26d7e16b..76cd3b28dc 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -21,7 +21,7 @@ #include "nsImageFrame.h" #include "nsTableFrame.h" #include "nsTableCellFrame.h" -#include "nsIPercentHeightObserver.h" +#include "nsIPercentBSizeObserver.h" #include "nsLayoutUtils.h" #include "mozilla/Preferences.h" #include "nsFontInflationData.h" @@ -74,7 +74,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, mLineLayout = nullptr; memset(&mFlags, 0, sizeof(mFlags)); mDiscoveredClearance = nullptr; - mPercentHeightObserver = nullptr; + mPercentBSizeObserver = nullptr; if (aFlags & DUMMY_PARENT_REFLOW_STATE) { mFlags.mDummyParentReflowState = true; @@ -137,33 +137,37 @@ FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) return 0; } -// NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid -// item, we need to make it take the containing-block height as well as the -// width, since flex items and grid items resolve vertical percent margins -// and padding against the containing-block height, rather than its width. +// NOTE: If we ever want to use nsCSSOffsetState for a flex item or a +// grid item, we need to make it take the containing-block block-size as +// well as the inline-size, since flex items and grid items resolve +// block-direction percent margins and padding against the +// containing-block block-size, rather than its inline-size. nsCSSOffsetState::nsCSSOffsetState(nsIFrame *aFrame, nsRenderingContext *aRenderingContext, - nscoord aContainingBlockWidth) + nscoord aContainingBlockISize) : frame(aFrame) , rendContext(aRenderingContext) , mWritingMode(aFrame->GetWritingMode()) { MOZ_ASSERT(!aFrame->IsFlexOrGridItem(), - "We're about to resolve vertical percent margin & padding " - "values against CB width, which is incorrect for flex/grid items"); - InitOffsets(aContainingBlockWidth, aContainingBlockWidth, frame->GetType()); + "We're about to resolve percent margin & padding " + "values against CB inline size, which is incorrect for " + "flex/grid items"); + LogicalSize cbSize(mWritingMode, aContainingBlockISize, + aContainingBlockISize); + InitOffsets(cbSize, frame->GetType()); } // Initialize a reflow state for a child frame's reflow. Some state // is copied from the parent reflow state; the remaining state is // computed. -nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, - const nsHTMLReflowState& aParentReflowState, - nsIFrame* aFrame, - const LogicalSize& aAvailableSpace, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - uint32_t aFlags) +nsHTMLReflowState::nsHTMLReflowState( + nsPresContext* aPresContext, + const nsHTMLReflowState& aParentReflowState, + nsIFrame* aFrame, + const LogicalSize& aAvailableSpace, + const LogicalSize* aContainingBlockSize, + uint32_t aFlags) : nsCSSOffsetState(aFrame, aParentReflowState.rendContext) , mBlockDelta(0) , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE) @@ -173,19 +177,16 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, MOZ_ASSERT(aPresContext, "no pres context"); MOZ_ASSERT(aFrame, "no frame"); MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); - NS_PRECONDITION((aContainingBlockWidth == -1) == - (aContainingBlockHeight == -1), - "cb width and height should only be non-default together"); - NS_PRECONDITION(!mFlags.mSpecialHeightReflow || + NS_PRECONDITION(!mFlags.mSpecialBSizeReflow || !NS_SUBTREE_DIRTY(aFrame), - "frame should be clean when getting special height reflow"); + "frame should be clean when getting special bsize reflow"); parentReflowState = &aParentReflowState; // If the parent is dirty, then the child is as well. // XXX Are the other cases where the parent reflows a child a second // time, as a resize? - if (!mFlags.mSpecialHeightReflow) + if (!mFlags.mSpecialBSizeReflow) frame->AddStateBits(parentReflowState->frame->GetStateBits() & NS_FRAME_IS_DIRTY); @@ -219,9 +220,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, mFlags.mDummyParentReflowState = false; mDiscoveredClearance = nullptr; - mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && - aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) - ? aParentReflowState.mPercentHeightObserver : nullptr; + mPercentBSizeObserver = (aParentReflowState.mPercentBSizeObserver && + aParentReflowState.mPercentBSizeObserver->NeedsToObserve(*this)) + ? aParentReflowState.mPercentBSizeObserver : nullptr; if ((aFlags & DUMMY_PARENT_REFLOW_STATE) || (parentReflowState->mFlags.mDummyParentReflowState && @@ -230,60 +231,62 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, } if (!(aFlags & CALLER_WILL_INIT)) { - Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight); + Init(aPresContext, aContainingBlockSize); } } inline nscoord -nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, +nsCSSOffsetState::ComputeISizeValue(nscoord aContainingBlockISize, nscoord aContentEdgeToBoxSizing, nscoord aBoxSizingToMarginEdge, const nsStyleCoord& aCoord) { - return nsLayoutUtils::ComputeWidthValue(rendContext, frame, - aContainingBlockWidth, + return nsLayoutUtils::ComputeISizeValue(rendContext, frame, + aContainingBlockISize, aContentEdgeToBoxSizing, aBoxSizingToMarginEdge, aCoord); } nscoord -nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, +nsCSSOffsetState::ComputeISizeValue(nscoord aContainingBlockISize, uint8_t aBoxSizing, const nsStyleCoord& aCoord) { - nscoord inside = 0, outside = ComputedPhysicalBorderPadding().LeftRight() + - ComputedPhysicalMargin().LeftRight(); + WritingMode wm = GetWritingMode(); + nscoord inside = 0, outside = ComputedLogicalBorderPadding().IStartEnd(wm) + + ComputedLogicalMargin().IStartEnd(wm); switch (aBoxSizing) { case NS_STYLE_BOX_SIZING_BORDER: - inside = ComputedPhysicalBorderPadding().LeftRight(); + inside = ComputedLogicalBorderPadding().IStartEnd(wm); break; case NS_STYLE_BOX_SIZING_PADDING: - inside = ComputedPhysicalPadding().LeftRight(); + inside = ComputedLogicalPadding().IStartEnd(wm); break; } outside -= inside; - return ComputeWidthValue(aContainingBlockWidth, inside, + return ComputeISizeValue(aContainingBlockISize, inside, outside, aCoord); } nscoord -nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight, - uint8_t aBoxSizing, - const nsStyleCoord& aCoord) +nsCSSOffsetState::ComputeBSizeValue(nscoord aContainingBlockBSize, + uint8_t aBoxSizing, + const nsStyleCoord& aCoord) { + WritingMode wm = GetWritingMode(); nscoord inside = 0; switch (aBoxSizing) { case NS_STYLE_BOX_SIZING_BORDER: - inside = ComputedPhysicalBorderPadding().TopBottom(); + inside = ComputedLogicalBorderPadding().BStartEnd(wm); break; case NS_STYLE_BOX_SIZING_PADDING: - inside = ComputedPhysicalPadding().TopBottom(); + inside = ComputedLogicalPadding().BStartEnd(wm); break; } - return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight, - inside, aCoord); + return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, + inside, aCoord); } void @@ -334,11 +337,10 @@ nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight) } void -nsHTMLReflowState::Init(nsPresContext* aPresContext, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsMargin* aBorder, - const nsMargin* aPadding) +nsHTMLReflowState::Init(nsPresContext* aPresContext, + const LogicalSize* aContainingBlockSize, + const nsMargin* aBorder, + const nsMargin* aPadding) { if (AvailableISize() == NS_UNCONSTRAINEDSIZE) { // Look up the parent chain for an orthogonal inline limit, @@ -371,8 +373,12 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, InitFrameType(type); InitCBReflowState(); - InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, - aBorder, aPadding, type); + LogicalSize cbSize(mWritingMode, -1, -1); + if (aContainingBlockSize) { + cbSize = *aContainingBlockSize; + } + + InitConstraints(aPresContext, cbSize, aBorder, aPadding, type); InitResizeFlags(aPresContext, type); @@ -503,8 +509,10 @@ IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType) void nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType) { - bool isHResize = (frame->GetSize().width != - ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) || + const WritingMode wm = mWritingMode; // just a shorthand + bool isIResize = (frame->ISize(wm) != + ComputedISize() + + ComputedLogicalBorderPadding().IStartEnd(wm)) || aPresContext->PresShell()->IsReflowOnZoomPending(); if ((frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) && @@ -518,7 +526,7 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT // dummy parent reflow state anyway). !mFlags.mDummyParentReflowState; - if (dirty || (!frame->GetParent() && isHResize)) { + if (dirty || (!frame->GetParent() && isIResize)) { // When font size inflation is enabled, a change in either: // * the effective width of a font inflation flow root // * the width of the frame @@ -593,48 +601,48 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT } } - SetHResize(!(frame->GetStateBits() & NS_FRAME_IS_DIRTY) && - isHResize); + SetIResize(!(frame->GetStateBits() & NS_FRAME_IS_DIRTY) && + isIResize); // XXX Should we really need to null check mCBReflowState? (We do for // at least nsBoxFrame). if (IS_TABLE_CELL(aFrameType) && - (mFlags.mSpecialHeightReflow || + (mFlags.mSpecialBSizeReflow || (frame->FirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && - (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { + (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { // Need to set the bit on the cell so that - // mCBReflowState->IsVResize() is set correctly below when + // mCBReflowState->IsBResize() is set correctly below when // reflowing descendant. - SetVResize(true); + SetBResize(true); } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) { // XXX Is this problematic for relatively positioned inlines acting // as containing block for absolutely positioned elements? // Possibly; in that case we should at least be checking // NS_SUBTREE_DIRTY, I'd think. - SetVResize(mCBReflowState->IsVResize()); - } else if (ComputedHeight() == NS_AUTOHEIGHT) { + SetBResize(mCBReflowState->IsBResize()); + } else if (ComputedBSize() == NS_AUTOHEIGHT) { if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && mCBReflowState) { - SetVResize(mCBReflowState->IsVResize()); + SetBResize(mCBReflowState->IsBResize()); } else { - SetVResize(IsHResize()); + SetBResize(IsIResize()); } - SetVResize(IsVResize() || NS_SUBTREE_DIRTY(frame)); + SetBResize(IsBResize() || NS_SUBTREE_DIRTY(frame)); } else { - // not 'auto' height - SetVResize(frame->GetSize().height != - ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom()); + // not 'auto' block-size + SetBResize(frame->BSize(wm) != + ComputedBSize() + ComputedLogicalBorderPadding().BStartEnd(wm)); } - bool dependsOnCBHeight = - (mStylePosition->HeightDependsOnContainer() && + bool dependsOnCBBSize = + (mStylePosition->BSizeDependsOnContainer(wm) && // FIXME: condition this on not-abspos? - mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) || - mStylePosition->MinHeightDependsOnContainer() || - mStylePosition->MaxHeightDependsOnContainer() || - mStylePosition->OffsetHasPercent(NS_SIDE_TOP) || - mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto || + mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto) || + mStylePosition->MinBSizeDependsOnContainer(wm) || + mStylePosition->MaxBSizeDependsOnContainer(wm) || + mStylePosition->OffsetHasPercent(wm.PhysicalSide(eLogicalSideBStart)) || + mStylePosition->mOffset.GetBEndUnit(wm) != eStyleUnit_Auto || frame->IsBoxFrame(); if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { @@ -642,37 +650,37 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, "bad line-height value"); - // line-height depends on block height - frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + // line-height depends on block bsize + frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); // but only on containing blocks if this frame is not a suitable block - dependsOnCBHeight |= !nsLayoutUtils::IsNonWrapperBlock(frame); + dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(frame); } - // If we're the descendant of a table cell that performs special height + // If we're the descendant of a table cell that performs special bsize // reflows and we could be the child that requires them, always set - // the vertical resize in case this is the first pass before the - // special height reflow. However, don't do this if it actually is - // the special height reflow, since in that case it will already be + // the block-axis resize in case this is the first pass before the + // special bsize reflow. However, don't do this if it actually is + // the special bsize reflow, since in that case it will already be // set correctly above if we need it set. - if (!IsVResize() && mCBReflowState && + if (!IsBResize() && mCBReflowState && (IS_TABLE_CELL(mCBReflowState->frame->GetType()) || mCBReflowState->mFlags.mHeightDependsOnAncestorCell) && - !mCBReflowState->mFlags.mSpecialHeightReflow && - dependsOnCBHeight) { - SetVResize(true); + !mCBReflowState->mFlags.mSpecialBSizeReflow && + dependsOnCBBSize) { + SetBResize(true); mFlags.mHeightDependsOnAncestorCell = true; } - // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed. + // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed. - // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT| - // &&ed with the percentage height check. However, this doesn't get - // along with table special height reflows, since a special height - // reflow (a quirk that makes such percentage heights work on children + // It would be nice to check that |ComputedBSize != NS_AUTOHEIGHT| + // &&ed with the percentage bsize check. However, this doesn't get + // along with table special bsize reflows, since a special bsize + // reflow (a quirk that makes such percentage height work on children // of table cells) can cause not just a single percentage height to - // become fixed, but an entire descendant chain of percentage heights + // become fixed, but an entire descendant chain of percentage height // to become fixed. - if (dependsOnCBHeight && mCBReflowState) { + if (dependsOnCBBSize && mCBReflowState) { const nsHTMLReflowState *rs = this; bool hitCBReflowState = false; do { @@ -681,9 +689,9 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT break; } - if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) + if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) break; // no need to go further - rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); // Keep track of whether we've hit the containing block, because // we need to go at least that far. @@ -691,11 +699,14 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT hitCBReflowState = true; } + // XXX What about orthogonal flows? It doesn't make sense to + // keep propagating this bit across an orthogonal boundary, + // where the meaning of BSize changes. Bug 1175517. } while (!hitCBReflowState || (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && !IsQuirkContainingBlockHeight(rs, rs->frame->GetType()))); // Note: We actually don't need to set the - // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases + // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases // where we hit the early break statements in // CalcQuirkContainingBlockHeight. But it doesn't hurt // us to set the bit in these cases. @@ -704,7 +715,7 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) { // If we're reflowing everything, then we'll find out if we need // to re-set this. - frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } } @@ -815,110 +826,118 @@ nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) } /* static */ void -nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection, +nsHTMLReflowState::ComputeRelativeOffsets(WritingMode aWM, nsIFrame* aFrame, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, + const LogicalSize& aCBSize, nsMargin& aComputedOffsets) { + LogicalMargin offsets(aWM); + mozilla::css::Side inlineStart = aWM.PhysicalSide(eLogicalSideIStart); + mozilla::css::Side inlineEnd = aWM.PhysicalSide(eLogicalSideIEnd); + mozilla::css::Side blockStart = aWM.PhysicalSide(eLogicalSideBStart); + mozilla::css::Side blockEnd = aWM.PhysicalSide(eLogicalSideBEnd); + const nsStylePosition* position = aFrame->StylePosition(); - // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right, - // and 'right' moves the boxes to the left. The computed values are always: - // left=-right - bool leftIsAuto = eStyleUnit_Auto == position->mOffset.GetLeftUnit(); - bool rightIsAuto = eStyleUnit_Auto == position->mOffset.GetRightUnit(); + // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart' + // moves the boxes to the end of the line, and 'inlineEnd' moves the + // boxes to the start of the line. The computed values are always: + // inlineStart=-inlineEnd + bool inlineStartIsAuto = + eStyleUnit_Auto == position->mOffset.GetUnit(inlineStart); + bool inlineEndIsAuto = + eStyleUnit_Auto == position->mOffset.GetUnit(inlineEnd); - // If neither 'left' not 'right' are auto, then we're over-constrained and - // we ignore one of them - if (!leftIsAuto && !rightIsAuto) { - if (aCBDirection == NS_STYLE_DIRECTION_RTL) { - leftIsAuto = true; - } else { - rightIsAuto = true; - } + // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're + // over-constrained and we ignore one of them + if (!inlineStartIsAuto && !inlineEndIsAuto) { + inlineEndIsAuto = true; } - if (leftIsAuto) { - if (rightIsAuto) { + if (inlineStartIsAuto) { + if (inlineEndIsAuto) { // If both are 'auto' (their initial values), the computed values are 0 - aComputedOffsets.left = aComputedOffsets.right = 0; + offsets.IStart(aWM) = offsets.IEnd(aWM) = 0; } else { - // 'Right' isn't 'auto' so compute its value - aComputedOffsets.right = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - position->mOffset.GetRight()); + // 'inlineEnd' isn't 'auto' so compute its value + offsets.IEnd(aWM) = nsLayoutUtils:: + ComputeCBDependentValue(aCBSize.ISize(aWM), + position->mOffset.Get(inlineEnd)); - // Computed value for 'left' is minus the value of 'right' - aComputedOffsets.left = -aComputedOffsets.right; + // Computed value for 'inlineStart' is minus the value of 'inlineEnd' + offsets.IStart(aWM) = -offsets.IEnd(aWM); } } else { - NS_ASSERTION(rightIsAuto, "unexpected specified constraint"); - - // 'Left' isn't 'auto' so compute its value - aComputedOffsets.left = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - position->mOffset.GetLeft()); + NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint"); - // Computed value for 'right' is minus the value of 'left' - aComputedOffsets.right = -aComputedOffsets.left; + // 'InlineStart' isn't 'auto' so compute its value + offsets.IStart(aWM) = nsLayoutUtils:: + ComputeCBDependentValue(aCBSize.ISize(aWM), + position->mOffset.Get(inlineStart)); + + // Computed value for 'inlineEnd' is minus the value of 'inlineStart' + offsets.IEnd(aWM) = -offsets.IStart(aWM); } - // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties - // move relatively positioned elements up and down. They also must be each - // other's negative - bool topIsAuto = eStyleUnit_Auto == position->mOffset.GetTopUnit(); - bool bottomIsAuto = eStyleUnit_Auto == position->mOffset.GetBottomUnit(); + // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart' + // and 'blockEnd' properties move relatively positioned elements in + // the block progression direction. They also must be each other's + // negative + bool blockStartIsAuto = + eStyleUnit_Auto == position->mOffset.GetUnit(blockStart); + bool blockEndIsAuto = + eStyleUnit_Auto == position->mOffset.GetUnit(blockEnd); - // Check for percentage based values and a containing block height that - // depends on the content height. Treat them like 'auto' - if (NS_AUTOHEIGHT == aContainingBlockHeight) { - if (position->OffsetHasPercent(NS_SIDE_TOP)) { - topIsAuto = true; + // Check for percentage based values and a containing block block-size + // that depends on the content block-size. Treat them like 'auto' + if (NS_AUTOHEIGHT == aCBSize.BSize(aWM)) { + if (position->OffsetHasPercent(blockStart)) { + blockStartIsAuto = true; } - if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) { - bottomIsAuto = true; + if (position->OffsetHasPercent(blockEnd)) { + blockEndIsAuto = true; } } - // If neither is 'auto', 'bottom' is ignored - if (!topIsAuto && !bottomIsAuto) { - bottomIsAuto = true; + // If neither is 'auto', 'block-end' is ignored + if (!blockStartIsAuto && !blockEndIsAuto) { + blockEndIsAuto = true; } - if (topIsAuto) { - if (bottomIsAuto) { + if (blockStartIsAuto) { + if (blockEndIsAuto) { // If both are 'auto' (their initial values), the computed values are 0 - aComputedOffsets.top = aComputedOffsets.bottom = 0; + offsets.BStart(aWM) = offsets.BEnd(aWM) = 0; } else { - // 'Bottom' isn't 'auto' so compute its value - aComputedOffsets.bottom = nsLayoutUtils:: - ComputeHeightDependentValue(aContainingBlockHeight, - position->mOffset.GetBottom()); - - // Computed value for 'top' is minus the value of 'bottom' - aComputedOffsets.top = -aComputedOffsets.bottom; + // 'blockEnd' isn't 'auto' so compute its value + offsets.BEnd(aWM) = nsLayoutUtils:: + ComputeBSizeDependentValue(aCBSize.BSize(aWM), + position->mOffset.Get(blockEnd)); + + // Computed value for 'blockStart' is minus the value of 'blockEnd' + offsets.BStart(aWM) = -offsets.BEnd(aWM); } } else { - NS_ASSERTION(bottomIsAuto, "unexpected specified constraint"); - - // 'Top' isn't 'auto' so compute its value - aComputedOffsets.top = nsLayoutUtils:: - ComputeHeightDependentValue(aContainingBlockHeight, - position->mOffset.GetTop()); + NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint"); - // Computed value for 'bottom' is minus the value of 'top' - aComputedOffsets.bottom = -aComputedOffsets.top; + // 'blockStart' isn't 'auto' so compute its value + offsets.BStart(aWM) = nsLayoutUtils:: + ComputeBSizeDependentValue(aCBSize.BSize(aWM), + position->mOffset.Get(blockStart)); + + // Computed value for 'blockEnd' is minus the value of 'blockStart' + offsets.BEnd(aWM) = -offsets.BStart(aWM); } - // Store the offset + // Convert the offsets to physical coordinates and store them on the frame + aComputedOffsets = offsets.GetPhysicalMargin(aWM); FrameProperties props = aFrame->Properties(); - nsMargin* offsets = static_cast + nsMargin* physicalOffsets = static_cast (props.Get(nsIFrame::ComputedOffsetProperty())); - if (offsets) { - *offsets = aComputedOffsets; + if (physicalOffsets) { + *physicalOffsets = aComputedOffsets; } else { props.Set(nsIFrame::ComputedOffsetProperty(), new nsMargin(aComputedOffsets)); @@ -972,8 +991,8 @@ nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, nsIFrame* nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, - nscoord& aCBLeftEdge, - nscoord& aCBWidth) + nscoord& aCBIStartEdge, + nscoord& aCBISize) { aFrame = aFrame->GetContainingBlock(); NS_ASSERTION(aFrame != frame, "How did that happen?"); @@ -991,19 +1010,23 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, } else { state = nullptr; } - + + WritingMode wm = GetWritingMode(); if (state) { - aCBLeftEdge = state->ComputedPhysicalBorderPadding().left; - aCBWidth = state->ComputedWidth(); + WritingMode stateWM = state->GetWritingMode(); + aCBIStartEdge = + state->ComputedLogicalBorderPadding().ConvertTo(wm, stateWM).IStart(wm); + aCBISize = state->ComputedSize(wm).ISize(wm); } else { /* Didn't find a reflow state for aFrame. Just compute the information we want, on the assumption that aFrame already knows its size. This really ought to be true by now. */ NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), "aFrame shouldn't be in reflow; we'll lie if it is"); - nsMargin borderPadding = aFrame->GetUsedBorderAndPadding(); - aCBLeftEdge = borderPadding.left; - aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight(); + LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm); + aCBIStartEdge = borderPadding.IStart(wm); + aCBISize = aFrame->GetLogicalSize(wm).ISize(wm) - + borderPadding.IStartEnd(wm); } return aFrame; @@ -1030,7 +1053,7 @@ struct nsHypotheticalBox { #endif } }; - + static bool GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType) { @@ -1057,55 +1080,66 @@ GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameTyp * aOutsideBoxSizing returns the rest. */ void -nsHTMLReflowState::CalculateHorizBorderPaddingMargin( - nscoord aContainingBlockWidth, +nsHTMLReflowState::CalculateInlineBorderPaddingMargin( + nscoord aContainingBlockISize, nscoord* aInsideBoxSizing, nscoord* aOutsideBoxSizing) { - const nsMargin& border = mStyleBorder->GetComputedBorder(); - nsMargin padding, margin; + WritingMode wm = GetWritingMode(); + mozilla::css::Side inlineStart = wm.PhysicalSide(eLogicalSideIStart); + mozilla::css::Side inlineEnd = wm.PhysicalSide(eLogicalSideIEnd); + + const LogicalMargin& border = + LogicalMargin(wm, mStyleBorder->GetComputedBorder()); + LogicalMargin padding(wm), margin(wm); // See if the style system can provide us the padding directly - if (!mStylePadding->GetPadding(padding)) { - // We have to compute the left and right values - padding.left = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - mStylePadding->mPadding.GetLeft()); - padding.right = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - mStylePadding->mPadding.GetRight()); + nsMargin stylePadding; + if (mStylePadding->GetPadding(stylePadding)) { + padding = LogicalMargin(wm, stylePadding); + } else { + // We have to compute the inline start and end values + padding.IStart(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockISize, + mStylePadding->mPadding.Get(inlineStart)); + padding.IEnd(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockISize, + mStylePadding->mPadding.Get(inlineEnd)); } // See if the style system can provide us the margin directly - if (!mStyleMargin->GetMargin(margin)) { + nsMargin styleMargin; + if (mStyleMargin->GetMargin(styleMargin)) { + margin = LogicalMargin(wm, styleMargin); + } else { // We have to compute the left and right values - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) { + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineStart)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.left = 0; // just ignore + margin.IStart(wm) = 0; // just ignore } else { - margin.left = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - mStyleMargin->mMargin.GetLeft()); + margin.IStart(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockISize, + mStyleMargin->mMargin.Get(inlineStart)); } - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) { + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineEnd)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.right = 0; // just ignore + margin.IEnd(wm) = 0; // just ignore } else { - margin.right = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockWidth, - mStyleMargin->mMargin.GetRight()); + margin.IEnd(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockISize, + mStyleMargin->mMargin.Get(inlineEnd)); } } nscoord outside = - padding.LeftRight() + border.LeftRight() + margin.LeftRight(); + padding.IStartEnd(wm) + border.IStartEnd(wm) + margin.IStartEnd(wm); nscoord inside = 0; switch (mStylePosition->mBoxSizing) { case NS_STYLE_BOX_SIZING_BORDER: - inside += border.LeftRight(); + inside += border.IStartEnd(wm); // fall through case NS_STYLE_BOX_SIZING_PADDING: - inside += padding.LeftRight(); + inside += padding.IStartEnd(wm); } outside -= inside; *aInsideBoxSizing = inside; @@ -1146,70 +1180,75 @@ void nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, nsIFrame* aContainingBlock, - nscoord aBlockLeftContentEdge, - nscoord aBlockContentWidth, + nscoord aBlockIStartContentEdge, + nscoord aBlockContentISize, const nsHTMLReflowState* cbrs, nsHypotheticalBox& aHypotheticalBox, nsIAtom* aFrameType) { NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, "mOriginalDisplay has not been properly initialized"); - - // If it's a replaced element and it has a 'auto' value for 'width', see if we - // can get the intrinsic size. This will allow us to exactly determine both the - // left and right edges - bool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto; + + // If it's a replaced element and it has a 'auto' value for + //'inline size', see if we can get the intrinsic size. This will allow + // us to exactly determine both the inline edges + WritingMode cbWM = cbrs->GetWritingMode(); + nsStyleCoord styleISize = mStylePosition->ISize(cbWM); + bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto; nsSize intrinsicSize; bool knowIntrinsicSize = false; - if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { + if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { // See if we can get the intrinsic size of the element knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType); } - // See if we can calculate what the box width would have been if the - // element had been in the flow - nscoord boxWidth; - bool knowBoxWidth = false; + // See if we can calculate what the box inline size would have been if + // the element had been in the flow + nscoord boxISize; + bool knowBoxISize = false; if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) && !NS_FRAME_IS_REPLACED(mFrameType)) { - // For non-replaced inline-level elements the 'width' property doesn't apply, - // so we don't know what the width would have been without reflowing it + // For non-replaced inline-level elements the 'inline size' property + // doesn't apply, so we don't know what the inline size would have + // been without reflowing it } else { // It's either a replaced inline-level element or a block-level element - // Determine the total amount of horizontal border/padding/margin that - // the element would have had if it had been in the flow. Note that we - // ignore any 'auto' and 'inherit' values + // Determine the total amount of inline direction + // border/padding/margin that the element would have had if it had + // been in the flow. Note that we ignore any 'auto' and 'inherit' + // values nscoord insideBoxSizing, outsideBoxSizing; - CalculateHorizBorderPaddingMargin(aBlockContentWidth, - &insideBoxSizing, &outsideBoxSizing); + CalculateInlineBorderPaddingMargin(aBlockContentISize, + &insideBoxSizing, &outsideBoxSizing); - if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { - // It's a replaced element with an 'auto' width so the box width is - // its intrinsic size plus any border/padding/margin + if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { + // It's a replaced element with an 'auto' inline size so the box + // inline size is its intrinsic size plus any border/padding/margin if (knowIntrinsicSize) { - boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing; - knowBoxWidth = true; + boxISize = LogicalSize(cbWM, intrinsicSize).ISize(cbWM) + + outsideBoxSizing + insideBoxSizing; + knowBoxISize = true; } - } else if (isAutoWidth) { - // The box width is the containing block width - boxWidth = aBlockContentWidth; - knowBoxWidth = true; - + } else if (isAutoISize) { + // The box inline size is the containing block inline size + boxISize = aBlockContentISize; + knowBoxISize = true; + } else { // We need to compute it. It's important we do this, because if it's // percentage based this computed value may be different from the computed // value calculated using the absolute containing block width - boxWidth = ComputeWidthValue(aBlockContentWidth, + boxISize = ComputeISizeValue(aBlockContentISize, insideBoxSizing, outsideBoxSizing, - mStylePosition->mWidth) + + styleISize) + insideBoxSizing + outsideBoxSizing; - knowBoxWidth = true; + knowBoxISize = true; } } - + // Get the 'direction' of the block const nsStyleVisibility* blockVis = aContainingBlock->StyleVisibility(); @@ -1296,14 +1335,14 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // The placeholder represents the left edge of the hypothetical box aHypotheticalBox.mLeft = placeholderOffset.x; } else { - aHypotheticalBox.mLeft = aBlockLeftContentEdge; + aHypotheticalBox.mLeft = aBlockIStartContentEdge; } #ifdef DEBUG aHypotheticalBox.mLeftIsExact = true; #endif - if (knowBoxWidth) { - aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth; + if (knowBoxISize) { + aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxISize; #ifdef DEBUG aHypotheticalBox.mRightIsExact = true; #endif @@ -1311,7 +1350,7 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // We can't compute the right edge because we don't know the desired // width. So instead use the right content edge of the block parent, // but remember it's not exact - aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; + aHypotheticalBox.mRight = aBlockIStartContentEdge + aBlockContentISize; #ifdef DEBUG aHypotheticalBox.mRightIsExact = false; #endif @@ -1322,14 +1361,14 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { aHypotheticalBox.mRight = placeholderOffset.x; } else { - aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; + aHypotheticalBox.mRight = aBlockIStartContentEdge + aBlockContentISize; } #ifdef DEBUG aHypotheticalBox.mRightIsExact = true; #endif - - if (knowBoxWidth) { - aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth; + + if (knowBoxISize) { + aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxISize; #ifdef DEBUG aHypotheticalBox.mLeftIsExact = true; #endif @@ -1337,7 +1376,7 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // We can't compute the left edge because we don't know the desired // width. So instead use the left content edge of the block parent, // but remember it's not exact - aHypotheticalBox.mLeft = aBlockLeftContentEdge; + aHypotheticalBox.mLeft = aBlockIStartContentEdge; #ifdef DEBUG aHypotheticalBox.mLeftIsExact = false; #endif @@ -1398,11 +1437,11 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, void nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, const nsHTMLReflowState* cbrs, - nscoord containingBlockWidth, - nscoord containingBlockHeight, + const LogicalSize& aCBSize, nsIAtom* aFrameType) { - NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT, + WritingMode wm = GetWritingMode(); + NS_PRECONDITION(aCBSize.BSize(wm) != NS_AUTOHEIGHT, "containing block height must be constrained"); NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame, @@ -1426,13 +1465,14 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) { // Find the nearest containing block frame to the placeholder frame, // and return its left edge and width. - nscoord cbLeftEdge, cbWidth; + nscoord cbIStartEdge, cbISize; nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame, - cbLeftEdge, - cbWidth); + cbIStartEdge, + cbISize); CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame, - cbLeftEdge, cbWidth, cbrs, hypotheticalBox, aFrameType); + cbIStartEdge, cbISize, cbrs, hypotheticalBox, + aFrameType); } // Initialize the 'left' and 'right' computed offsets @@ -1443,7 +1483,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, leftIsAuto = true; } else { ComputedPhysicalOffsets().left = nsLayoutUtils:: - ComputeCBDependentValue(containingBlockWidth, + ComputeCBDependentValue(aCBSize.ISize(wm), mStylePosition->mOffset.GetLeft()); } if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) { @@ -1451,7 +1491,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, rightIsAuto = true; } else { ComputedPhysicalOffsets().right = nsLayoutUtils:: - ComputeCBDependentValue(containingBlockWidth, + ComputeCBDependentValue(aCBSize.ISize(wm), mStylePosition->mOffset.GetRight()); } @@ -1469,7 +1509,8 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, } else { NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have " "exact value on containing block's start side"); - ComputedPhysicalOffsets().right = containingBlockWidth - hypotheticalBox.mRight; + ComputedPhysicalOffsets().right = aCBSize.ISize(wm) - + hypotheticalBox.mRight; rightIsAuto = false; } } @@ -1481,16 +1522,16 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, topIsAuto = true; } else { ComputedPhysicalOffsets().top = nsLayoutUtils:: - ComputeHeightDependentValue(containingBlockHeight, - mStylePosition->mOffset.GetTop()); + ComputeBSizeDependentValue(aCBSize.BSize(wm), + mStylePosition->mOffset.GetTop()); } if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) { ComputedPhysicalOffsets().bottom = 0; bottomIsAuto = true; } else { ComputedPhysicalOffsets().bottom = nsLayoutUtils:: - ComputeHeightDependentValue(containingBlockHeight, - mStylePosition->mOffset.GetBottom()); + ComputeBSizeDependentValue(aCBSize.BSize(wm), + mStylePosition->mOffset.GetBottom()); } if (topIsAuto && bottomIsAuto) { @@ -1512,12 +1553,9 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, { AutoMaybeDisableFontInflation an(frame); - WritingMode wm = GetWritingMode(); - // Size of the containing block in our writing mode - LogicalSize cbSize(wm, nsSize(containingBlockWidth, containingBlockHeight)); LogicalSize size = - frame->ComputeSize(rendContext, wm, cbSize, - cbSize.ISize(wm), // XXX or AvailableISize()? + frame->ComputeSize(rendContext, wm, aCBSize, + aCBSize.ISize(wm), // XXX or AvailableISize()? ComputedLogicalMargin().Size(wm) + ComputedLogicalOffsets().Size(wm), ComputedLogicalBorderPadding().Size(wm) - @@ -1544,8 +1582,8 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, // we always compute widths. ComputedPhysicalOffsets().left = NS_AUTOOFFSET; } else { - ComputedPhysicalOffsets().left = containingBlockWidth - ComputedPhysicalMargin().left - - ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - + ComputedPhysicalOffsets().left = aCBSize.Width(wm) - ComputedPhysicalMargin().left - + ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right - ComputedPhysicalOffsets().right; } @@ -1559,7 +1597,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, // we always compute widths. ComputedPhysicalOffsets().right = NS_AUTOOFFSET; } else { - ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left - + ComputedPhysicalOffsets().right = aCBSize.Width(wm) - ComputedPhysicalOffsets().left - ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right; } @@ -1571,7 +1609,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, // * we're dealing with a replaced element // * width was constrained by min-width or max-width. - nscoord availMarginSpace = containingBlockWidth - + nscoord availMarginSpace = aCBSize.Width(wm) - ComputedPhysicalOffsets().LeftRight() - ComputedPhysicalMargin().LeftRight() - ComputedPhysicalBorderPadding().LeftRight() - @@ -1633,7 +1671,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, if (heightIsAuto) { ComputedPhysicalOffsets().top = NS_AUTOOFFSET; } else { - ComputedPhysicalOffsets().top = containingBlockHeight - ComputedPhysicalMargin().top - + ComputedPhysicalOffsets().top = aCBSize.Height(wm) - ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom - ComputedPhysicalOffsets().bottom; } @@ -1642,13 +1680,13 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, if (heightIsAuto) { ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET; } else { - ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top - + ComputedPhysicalOffsets().bottom = aCBSize.Height(wm) - ComputedPhysicalOffsets().top - ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom; } } else { // Neither 'top' nor 'bottom' is 'auto'. - nscoord autoHeight = containingBlockHeight - + nscoord autoHeight = aCBSize.Height(wm) - ComputedPhysicalOffsets().TopBottom() - ComputedPhysicalMargin().TopBottom() - ComputedPhysicalBorderPadding().TopBottom(); @@ -1707,8 +1745,10 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, } } -nscoord -GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState) +// This will not be converted to abstract coordinates because it's only +// used in CalcQuirkContainingBlockHeight +nscoord +GetBlockMarginBorderPadding(const nsHTMLReflowState* aReflowState) { nscoord result = 0; if (!aReflowState) return result; @@ -1799,8 +1839,8 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) if ((nsGkAtoms::canvasFrame == frameType) || (nsGkAtoms::pageContentFrame == frameType)) { - result -= GetVerticalMarginBorderPadding(firstAncestorRS); - result -= GetVerticalMarginBorderPadding(secondAncestorRS); + result -= GetBlockMarginBorderPadding(firstAncestorRS); + result -= GetBlockMarginBorderPadding(secondAncestorRS); #ifdef DEBUG // make sure the first ancestor is the HTML and the second is the BODY @@ -1825,7 +1865,7 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) nsGkAtoms::canvasFrame == rs->parentReflowState->frame->GetType()) { // ... then subtract out margin/border/padding for the BODY element - result -= GetVerticalMarginBorderPadding(secondAncestorRS); + result -= GetBlockMarginBorderPadding(secondAncestorRS); } break; } @@ -1836,16 +1876,16 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) // Called by InitConstraints() to compute the containing block rectangle for // the element. Handles the special logic for absolutely positioned elements -void -nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext, - const nsHTMLReflowState* aContainingBlockRS, - nscoord& aContainingBlockWidth, - nscoord& aContainingBlockHeight) +LogicalSize +nsHTMLReflowState::ComputeContainingBlockRectangle( + nsPresContext* aPresContext, + const nsHTMLReflowState* aContainingBlockRS) { // Unless the element is absolutely positioned, the containing block is // formed by the content edge of the nearest block-level ancestor - aContainingBlockWidth = aContainingBlockRS->ComputedWidth(); - aContainingBlockHeight = aContainingBlockRS->ComputedHeight(); + LogicalSize cbSize = aContainingBlockRS->ComputedSize(); + + WritingMode wm = aContainingBlockRS->GetWritingMode(); // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to // special case them here. @@ -1861,33 +1901,41 @@ nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPres // the containing block dimensions to our constructor. // XXXbz we should be taking the in-flows into account too, but // that's very hard. - nsMargin computedBorder = aContainingBlockRS->ComputedPhysicalBorderPadding() - - aContainingBlockRS->ComputedPhysicalPadding(); - aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width - - computedBorder.LeftRight(); - NS_ASSERTION(aContainingBlockWidth >= 0, + + LogicalMargin computedBorder = + aContainingBlockRS->ComputedLogicalBorderPadding() - + aContainingBlockRS->ComputedLogicalPadding(); + cbSize.ISize(wm) = aContainingBlockRS->frame->ISize(wm) - + computedBorder.IStartEnd(wm); + NS_ASSERTION(cbSize.ISize(wm) >= 0, "Negative containing block width!"); - aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height - - computedBorder.TopBottom(); - NS_ASSERTION(aContainingBlockHeight >= 0, + cbSize.BSize(wm) = aContainingBlockRS->frame->BSize(wm) - + computedBorder.BStartEnd(wm); + NS_ASSERTION(cbSize.BSize(wm) >= 0, "Negative containing block height!"); } else { // If the ancestor is block-level, the containing block is formed by the // padding edge of the ancestor - aContainingBlockWidth += aContainingBlockRS->ComputedPhysicalPadding().LeftRight(); - aContainingBlockHeight += aContainingBlockRS->ComputedPhysicalPadding().TopBottom(); + cbSize.ISize(wm) += + aContainingBlockRS->ComputedLogicalPadding().IStartEnd(wm); + cbSize.BSize(wm) += + aContainingBlockRS->ComputedLogicalPadding().BStartEnd(wm); } } else { // an element in quirks mode gets a containing block based on looking for a // parent with a non-auto height if the element has a percent height - // Note: We don't emulate this quirk for percents in calc(). - if (NS_AUTOHEIGHT == aContainingBlockHeight) { + // Note: We don't emulate this quirk for percents in calc() or in + // vertical writing modes. + if (!wm.IsVertical() && + NS_AUTOHEIGHT == cbSize.Height(wm)) { if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) { - aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS); + cbSize.Height(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRS); } } } + + return cbSize.ConvertTo(GetWritingMode(), wm); } static eNormalLineHeightControl GetNormalLineHeightCalcControl(void) @@ -1930,48 +1978,42 @@ GetFlexContainer(nsIFrame* aFrame) // For everything else: the CSS21 spec requires that margin and padding // percentage values are calculated with respect to the inline-size of the // containing block, even for margin & padding in the block axis. -static nscoord -BlockDirOffsetPercentBasis(const nsIFrame* aFrame, - nscoord aContainingBlockISize, - nscoord aContainingBlockBSize) +static LogicalSize +OffsetPercentBasis(const nsIFrame* aFrame, + WritingMode aWM, + const LogicalSize& aContainingBlockSize) { + LogicalSize offsetPercentBasis = aContainingBlockSize; if (!aFrame->IsFlexOrGridItem()) { - return aContainingBlockISize; + offsetPercentBasis.BSize(aWM) = offsetPercentBasis.ISize(aWM); + } else if (offsetPercentBasis.BSize(aWM) == NS_AUTOHEIGHT) { + offsetPercentBasis.BSize(aWM) = 0; } - if (aContainingBlockBSize == NS_AUTOHEIGHT) { - return 0; - } - - return aContainingBlockBSize; + return offsetPercentBasis; } // XXX refactor this code to have methods for each set of properties // we are computing: width,height,line-height; margin; offsets void -nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsMargin* aBorder, - const nsMargin* aPadding, - nsIAtom* aFrameType) +nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, + const LogicalSize& aContainingBlockSize, + const nsMargin* aBorder, + const nsMargin* aPadding, + nsIAtom* aFrameType) { - DISPLAY_INIT_CONSTRAINTS(frame, this, - aContainingBlockWidth, aContainingBlockHeight, - aBorder, aPadding); - WritingMode wm = GetWritingMode(); + DISPLAY_INIT_CONSTRAINTS(frame, this, + aContainingBlockSize.ISize(wm), + aContainingBlockSize.BSize(wm), + aBorder, aPadding); // If this is a reflow root, then set the computed width and // height equal to the available space if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) { // XXXldb This doesn't mean what it used to! - LogicalSize cbSize(wm, nsSize(aContainingBlockWidth, - aContainingBlockHeight)); - InitOffsets(cbSize.ISize(wm), - BlockDirOffsetPercentBasis(frame, cbSize.ISize(wm), - cbSize.BSize(wm)), + InitOffsets(OffsetPercentBasis(frame, wm, aContainingBlockSize), aFrameType, aBorder, aPadding); // Override mComputedMargin since reflow roots start from the // frame's boundary, which is inside the margin. @@ -1998,48 +2040,42 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // If we weren't given a containing block width and height, then // compute one - if (aContainingBlockWidth == -1) { - ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, - aContainingBlockHeight); - } + LogicalSize cbSize = (aContainingBlockSize == LogicalSize(wm, -1, -1)) + ? ComputeContainingBlockRectangle(aPresContext, cbrs) + : aContainingBlockSize; // See if the containing block height is based on the size of its // content nsIAtom* fType; - if (NS_AUTOHEIGHT == aContainingBlockHeight) { + if (NS_AUTOHEIGHT == cbSize.BSize(wm)) { // See if the containing block is a cell frame which needs // to use the mComputedHeight of the cell instead of what the cell block passed in. // XXX It seems like this could lead to bugs with min-height and friends if (cbrs->parentReflowState) { fType = cbrs->frame->GetType(); if (IS_TABLE_CELL(fType)) { - // use the cell's computed height - aContainingBlockHeight = cbrs->ComputedHeight(); + // use the cell's computed block size + cbSize.BSize(wm) = cbrs->ComputedSize(wm).BSize(wm); } } } // XXX Might need to also pass the CB height (not width) for page boxes, // too, if we implement them. - WritingMode cbwm = mCBReflowState->GetWritingMode(); - LogicalSize cbSize(cbwm, nsSize(aContainingBlockWidth, - aContainingBlockHeight)); - InitOffsets(cbSize.ISize(cbwm), - BlockDirOffsetPercentBasis(frame, cbSize.ISize(cbwm), - cbSize.BSize(cbwm)), + InitOffsets(OffsetPercentBasis(frame, wm, cbSize), aFrameType, aBorder, aPadding); - const nsStyleCoord &height = mStylePosition->mHeight; - nsStyleUnit heightUnit = height.GetUnit(); + const nsStyleCoord &blockSize = mStylePosition->BSize(wm); + nsStyleUnit blockSizeUnit = blockSize.GetUnit(); - // Check for a percentage based height and a containing block height - // that depends on the content height - // XXX twiddling heightUnit doesn't help anymore + // Check for a percentage based block size and a containing block + // block size that depends on the content block size + // XXX twiddling blockSizeUnit doesn't help anymore // FIXME Shouldn't we fix that? - if (height.HasPercent()) { - if (NS_AUTOHEIGHT == aContainingBlockHeight) { - // this if clause enables %-height on replaced inline frames, - // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;" + if (blockSize.HasPercent()) { + if (NS_AUTOHEIGHT == cbSize.BSize(wm)) { + // this if clause enables %-blockSize on replaced inline frames, + // such as images. See bug 54119. The else clause "blockSizeUnit = eStyleUnit_Auto;" // used to be called exclusively. if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType || NS_FRAME_REPLACED_CONTAINS_BLOCK( @@ -2047,47 +2083,47 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // Get the containing block reflow state NS_ASSERTION(nullptr != cbrs, "no containing block"); // in quirks mode, get the cb height using the special quirk method - if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { + if (!wm.IsVertical() && + eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { if (!IS_TABLE_CELL(fType)) { - aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs); - if (aContainingBlockHeight == NS_AUTOHEIGHT) { - heightUnit = eStyleUnit_Auto; + cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbrs); + if (cbSize.BSize(wm) == NS_AUTOHEIGHT) { + blockSizeUnit = eStyleUnit_Auto; } } else { - heightUnit = eStyleUnit_Auto; + blockSizeUnit = eStyleUnit_Auto; } } - // in standard mode, use the cb height. if it's "auto", as will be the case - // by default in BODY, use auto height as per CSS2 spec. - else + // in standard mode, use the cb block size. if it's "auto", + // as will be the case by default in BODY, use auto block size + // as per CSS2 spec. + else { - if (NS_AUTOHEIGHT != cbrs->ComputedHeight()) - aContainingBlockHeight = cbrs->ComputedHeight(); - else - heightUnit = eStyleUnit_Auto; + nscoord computedBSize = cbrs->ComputedSize(wm).BSize(wm); + if (NS_AUTOHEIGHT != computedBSize) { + cbSize.BSize(wm) = computedBSize; + } + else { + blockSizeUnit = eStyleUnit_Auto; + } } } else { - // default to interpreting the height like 'auto' - heightUnit = eStyleUnit_Auto; + // default to interpreting the blockSize like 'auto' + blockSizeUnit = eStyleUnit_Auto; } } } // Compute our offsets if the element is relatively positioned. We need - // the correct containing block width and height here, which is why we need + // the correct containing block width and blockSize here, which is why we need // to do it after all the quirks-n-such above. (If the element is sticky // positioned, we need to wait until the scroll container knows its size, // so we compute offsets from StickyScrollContainer::UpdatePositions.) if (mStyleDisplay->IsRelativelyPositioned(frame) && NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) { - uint8_t direction = NS_STYLE_DIRECTION_LTR; - if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { - direction = NS_STYLE_DIRECTION_RTL; - } - ComputeRelativeOffsets(direction, frame, aContainingBlockWidth, - aContainingBlockHeight, ComputedPhysicalOffsets()); + ComputeRelativeOffsets(wm, frame, cbSize, ComputedPhysicalOffsets()); } else { // Initialize offsets to 0 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); @@ -2095,62 +2131,61 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // Calculate the computed values for min and max properties. Note that // this MUST come after we've computed our border and padding. - ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs); + ComputeMinMaxValues(cbSize); - // Calculate the computed width and height. This varies by frame type + // Calculate the computed width and blockSize. This varies by frame type if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { // Internal table elements. The rules vary depending on the type. // Calculate the computed width bool rowOrRowGroup = false; - const nsStyleCoord &width = mStylePosition->mWidth; - nsStyleUnit widthUnit = width.GetUnit(); + const nsStyleCoord &inlineSize = mStylePosition->ISize(wm); + nsStyleUnit inlineSizeUnit = inlineSize.GetUnit(); if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) || (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) { - // 'width' property doesn't apply to table rows and row groups - widthUnit = eStyleUnit_Auto; + // 'inlineSize' property doesn't apply to table rows and row groups + inlineSizeUnit = eStyleUnit_Auto; rowOrRowGroup = true; } // calc() with percentages acts like auto on internal table elements - if (eStyleUnit_Auto == widthUnit || - (width.IsCalcUnit() && width.CalcHasPercent())) { - ComputedWidth() = AvailableWidth(); + if (eStyleUnit_Auto == inlineSizeUnit || + (inlineSize.IsCalcUnit() && inlineSize.CalcHasPercent())) { + ComputedISize() = AvailableISize(); - if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ + if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ // Internal table elements don't have margins. Only tables and // cells have border and padding - ComputedWidth() -= ComputedPhysicalBorderPadding().left + - ComputedPhysicalBorderPadding().right; - if (ComputedWidth() < 0) - ComputedWidth() = 0; + ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm); + if (ComputedISize() < 0) + ComputedISize() = 0; } - NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width"); - + NS_ASSERTION(ComputedISize() >= 0, "Bogus computed width"); + } else { - NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(), + NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(), "unexpected width unit change"); - ComputedWidth() = ComputeWidthValue(aContainingBlockWidth, - mStylePosition->mBoxSizing, - mStylePosition->mWidth); + ComputedISize() = ComputeISizeValue(cbSize.ISize(wm), + mStylePosition->mBoxSizing, + inlineSize); } - // Calculate the computed height + // Calculate the computed block size if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) || (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) { - // 'height' property doesn't apply to table columns and column groups - heightUnit = eStyleUnit_Auto; + // 'blockSize' property doesn't apply to table columns and column groups + blockSizeUnit = eStyleUnit_Auto; } // calc() with percentages acts like 'auto' on internal table elements - if (eStyleUnit_Auto == heightUnit || - (height.IsCalcUnit() && height.CalcHasPercent())) { - ComputedHeight() = NS_AUTOHEIGHT; + if (eStyleUnit_Auto == blockSizeUnit || + (blockSize.IsCalcUnit() && blockSize.CalcHasPercent())) { + ComputedBSize() = NS_AUTOHEIGHT; } else { - NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(), - "unexpected height unit change"); - ComputedHeight() = ComputeHeightValue(aContainingBlockHeight, - mStylePosition->mBoxSizing, - mStylePosition->mHeight); + NS_ASSERTION(blockSizeUnit == blockSize.GetUnit(), + "unexpected block size unit change"); + ComputedBSize() = ComputeBSizeValue(cbSize.BSize(wm), + mStylePosition->mBoxSizing, + blockSize); } // Doesn't apply to table elements @@ -2159,8 +2194,7 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) { // XXX not sure if this belongs here or somewhere else - cwk - InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth, - aContainingBlockHeight, aFrameType); + InitAbsoluteConstraints(aPresContext, cbrs, cbSize, aFrameType); } else { AutoMaybeDisableFontInflation an(frame); @@ -2200,8 +2234,6 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, "'mIsFlexContainerMeasuringHeight' shouldn't be set"); } - LogicalSize cbSize(wm, nsSize(aContainingBlockWidth, - aContainingBlockHeight)); if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { // For orthogonal flows, where we found a parent orthogonal-limit // for AvailableISize() in Init(), we'll use the same here as well. @@ -2209,8 +2241,7 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, } LogicalSize size = - frame->ComputeSize(rendContext, wm, cbSize, - AvailableISize(), + frame->ComputeSize(rendContext, wm, cbSize, AvailableISize(), ComputedLogicalMargin().Size(wm), ComputedLogicalBorderPadding().Size(wm) - ComputedLogicalPadding().Size(wm), @@ -2253,16 +2284,12 @@ UpdateProp(FrameProperties& aProps, } void -nsCSSOffsetState::InitOffsets(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis, +nsCSSOffsetState::InitOffsets(const LogicalSize& aPercentBasis, nsIAtom* aFrameType, const nsMargin *aBorder, const nsMargin *aPadding) { - DISPLAY_INIT_OFFSETS(frame, this, - aInlineDirPercentBasis, - aBlockDirPercentBasis, - aBorder, aPadding); + DISPLAY_INIT_OFFSETS(frame, this, aPercentBasis, aBorder, aPadding); // Since we are in reflow, we don't need to store these properties anymore // unless they are dependent on width, in which case we store the new value. @@ -2274,8 +2301,7 @@ nsCSSOffsetState::InitOffsets(nscoord aInlineDirPercentBasis, // become the default computed values, and may be adjusted below // XXX fix to provide 0,0 for the top&bottom margins for // inline-non-replaced elements - bool needMarginProp = ComputeMargin(aInlineDirPercentBasis, - aBlockDirPercentBasis); + bool needMarginProp = ComputeMargin(aPercentBasis); // XXX We need to include 'auto' horizontal margins in this too! // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin // to use it even when the margins are all zero (since sometimes @@ -2308,8 +2334,7 @@ nsCSSOffsetState::InitOffsets(nscoord aInlineDirPercentBasis, (frame->GetStateBits() & NS_FRAME_REFLOW_ROOT); } else { - needPaddingProp = ComputePadding(aInlineDirPercentBasis, - aBlockDirPercentBasis, aFrameType); + needPaddingProp = ComputePadding(aPercentBasis, aFrameType); } if (isThemed) { @@ -2346,7 +2371,8 @@ nsCSSOffsetState::InitOffsets(nscoord aInlineDirPercentBasis, // try to do anything like handling 'auto' widths, // 'box-sizing', or 'auto' margins. ComputedPhysicalPadding().SizeTo(0,0,0,0); - ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder(); + SetComputedLogicalBorderPadding( + tableFrame->GetIncludedOuterBCBorder(mWritingMode)); } // The margin is inherited to the outer table frame via @@ -2602,8 +2628,7 @@ nsHTMLReflowState::CalcLineHeight(nsIContent* aContent, } bool -nsCSSOffsetState::ComputeMargin(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis) +nsCSSOffsetState::ComputeMargin(const LogicalSize& aPercentBasis) { // SVG text frames have no margin. if (frame->IsSVGText()) { @@ -2612,29 +2637,25 @@ nsCSSOffsetState::ComputeMargin(nscoord aInlineDirPercentBasis, // If style style can provide us the margin directly, then use it. const nsStyleMargin *styleMargin = frame->StyleMargin(); + bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin()); if (isCBDependent) { // We have to compute the value - LogicalMargin m(mWritingMode); - nscoord horizontalPercentBasis = - mWritingMode.IsVertical() ? aBlockDirPercentBasis - : aInlineDirPercentBasis; - m.Left(mWritingMode) = nsLayoutUtils:: - ComputeCBDependentValue(horizontalPercentBasis, - styleMargin->mMargin.GetLeft()); - m.Right(mWritingMode) = nsLayoutUtils:: - ComputeCBDependentValue(horizontalPercentBasis, - styleMargin->mMargin.GetRight()); + WritingMode wm = GetWritingMode(); + LogicalMargin m(wm); + m.IStart(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.ISize(wm), + styleMargin->mMargin.GetIStart(wm)); + m.IEnd(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.ISize(wm), + styleMargin->mMargin.GetIEnd(wm)); - nscoord verticalPercentBasis = - mWritingMode.IsVertical() ? aInlineDirPercentBasis - : aBlockDirPercentBasis; - m.Top(mWritingMode) = nsLayoutUtils:: - ComputeCBDependentValue(verticalPercentBasis, - styleMargin->mMargin.GetTop()); - m.Bottom(mWritingMode) = nsLayoutUtils:: - ComputeCBDependentValue(verticalPercentBasis, - styleMargin->mMargin.GetBottom()); + m.BStart(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.BSize(wm), + styleMargin->mMargin.GetBStart(wm)); + m.BEnd(wm) = nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.BSize(wm), + styleMargin->mMargin.GetBEnd(wm)); SetComputedLogicalMargin(m); } @@ -2651,8 +2672,7 @@ nsCSSOffsetState::ComputeMargin(nscoord aInlineDirPercentBasis, } bool -nsCSSOffsetState::ComputePadding(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis, +nsCSSOffsetState::ComputePadding(const LogicalSize& aPercentBasis, nsIAtom* aFrameType) { // If style can provide us the padding directly, then use it. @@ -2669,26 +2689,21 @@ nsCSSOffsetState::ComputePadding(nscoord aInlineDirPercentBasis, else if (isCBDependent) { // We have to compute the value // clamp negative calc() results to 0 - LogicalMargin p(mWritingMode); - nscoord horizontalPercentBasis = - mWritingMode.IsVertical() ? aBlockDirPercentBasis - : aInlineDirPercentBasis; - p.Left(mWritingMode) = std::max(0, nsLayoutUtils:: - ComputeCBDependentValue(horizontalPercentBasis, - stylePadding->mPadding.GetLeft())); - p.Right(mWritingMode) = std::max(0, nsLayoutUtils:: - ComputeCBDependentValue(horizontalPercentBasis, - stylePadding->mPadding.GetRight())); + WritingMode wm = GetWritingMode(); + LogicalMargin p(wm); + p.IStart(wm) = std::max(0, nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.ISize(wm), + stylePadding->mPadding.GetIStart(wm))); + p.IEnd(wm) = std::max(0, nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.ISize(wm), + stylePadding->mPadding.GetIEnd(wm))); - nscoord verticalPercentBasis = - mWritingMode.IsVertical() ? aInlineDirPercentBasis - : aBlockDirPercentBasis; - p.Top(mWritingMode) = std::max(0, nsLayoutUtils:: - ComputeCBDependentValue(verticalPercentBasis, - stylePadding->mPadding.GetTop())); - p.Bottom(mWritingMode) = std::max(0, nsLayoutUtils:: - ComputeCBDependentValue(verticalPercentBasis, - stylePadding->mPadding.GetBottom())); + p.BStart(wm) = std::max(0, nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.BSize(wm), + stylePadding->mPadding.GetBStart(wm))); + p.BEnd(wm) = std::max(0, nsLayoutUtils:: + ComputeCBDependentValue(aPercentBasis.BSize(wm), + stylePadding->mPadding.GetBEnd(wm))); SetComputedLogicalPadding(p); } @@ -2696,34 +2711,39 @@ nsCSSOffsetState::ComputePadding(nscoord aInlineDirPercentBasis, } void -nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsHTMLReflowState* aContainingBlockRS) +nsHTMLReflowState::ComputeMinMaxValues(const LogicalSize&aCBSize) { + WritingMode wm = GetWritingMode(); + + const nsStyleCoord& minISize = mStylePosition->MinISize(wm); + const nsStyleCoord& maxISize = mStylePosition->MaxISize(wm); + const nsStyleCoord& minBSize = mStylePosition->MinBSize(wm); + const nsStyleCoord& maxBSize = mStylePosition->MaxBSize(wm); + // NOTE: min-width:auto resolves to 0, except on a flex item. (But // even there, it's supposed to be ignored (i.e. treated as 0) until // the flex container explicitly resolves & considers it.) - if (eStyleUnit_Auto == mStylePosition->mMinWidth.GetUnit()) { - ComputedMinWidth() = 0; + if (eStyleUnit_Auto == minISize.GetUnit()) { + ComputedMinISize() = 0; } else { - ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth, - mStylePosition->mBoxSizing, - mStylePosition->mMinWidth); + ComputedMinISize() = ComputeISizeValue(aCBSize.ISize(wm), + mStylePosition->mBoxSizing, + minISize); } - if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) { + if (eStyleUnit_None == maxISize.GetUnit()) { // Specified value of 'none' - ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE; // no limit + ComputedMaxISize() = NS_UNCONSTRAINEDSIZE; // no limit } else { - ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth, - mStylePosition->mBoxSizing, - mStylePosition->mMaxWidth); + ComputedMaxISize() = ComputeISizeValue(aCBSize.ISize(wm), + mStylePosition->mBoxSizing, + maxISize); } // If the computed value of 'min-width' is greater than the value of // 'max-width', 'max-width' is set to the value of 'min-width' - if (ComputedMinWidth() > ComputedMaxWidth()) { - ComputedMaxWidth() = ComputedMinWidth(); + if (ComputedMinISize() > ComputedMaxISize()) { + ComputedMaxISize() = ComputedMinISize(); } // Check for percentage based values and a containing block height that @@ -2736,24 +2756,22 @@ nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, // NOTE: min-height:auto resolves to 0, except on a flex item. (But // even there, it's supposed to be ignored (i.e. treated as 0) until // the flex container explicitly resolves & considers it.) - const nsStyleCoord &minHeight = mStylePosition->mMinHeight; - if (eStyleUnit_Auto == minHeight.GetUnit() || - (NS_AUTOHEIGHT == aContainingBlockHeight && - minHeight.HasPercent()) || + if (eStyleUnit_Auto == minBSize.GetUnit() || + (NS_AUTOHEIGHT == aCBSize.BSize(wm) && + minBSize.HasPercent()) || (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && - minHeight.IsCalcUnit() && minHeight.CalcHasPercent()) || + minBSize.IsCalcUnit() && minBSize.CalcHasPercent()) || mFlags.mIsFlexContainerMeasuringHeight) { - ComputedMinHeight() = 0; + ComputedMinBSize() = 0; } else { - ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight, - mStylePosition->mBoxSizing, - minHeight); + ComputedMinBSize() = ComputeBSizeValue(aCBSize.BSize(wm), + mStylePosition->mBoxSizing, + minBSize); } - const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight; - nsStyleUnit maxHeightUnit = maxHeight.GetUnit(); - if (eStyleUnit_None == maxHeightUnit) { + nsStyleUnit maxBSizeUnit = maxBSize.GetUnit(); + if (eStyleUnit_None == maxBSizeUnit) { // Specified value of 'none' - ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; // no limit + ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE; // no limit } else { // Check for percentage based values and a containing block height that // depends on the content height. Treat them like 'none' @@ -2761,23 +2779,23 @@ nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, // that's treated as 'auto' too. // Likewise, if we're a child of a flex container who's measuring our // intrinsic height, then we want to disregard our max-height. - if ((NS_AUTOHEIGHT == aContainingBlockHeight && - maxHeight.HasPercent()) || + if ((NS_AUTOHEIGHT == aCBSize.BSize(wm) && + maxBSize.HasPercent()) || (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && - maxHeight.IsCalcUnit() && maxHeight.CalcHasPercent()) || + maxBSize.IsCalcUnit() && maxBSize.CalcHasPercent()) || mFlags.mIsFlexContainerMeasuringHeight) { - ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; + ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE; } else { - ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight, - mStylePosition->mBoxSizing, - maxHeight); + ComputedMaxBSize() = ComputeBSizeValue(aCBSize.BSize(wm), + mStylePosition->mBoxSizing, + maxBSize); } } // If the computed value of 'min-height' is greater than the value of // 'max-height', 'max-height' is set to the value of 'min-height' - if (ComputedMinHeight() > ComputedMaxHeight()) { - ComputedMaxHeight() = ComputedMinHeight(); + if (ComputedMinBSize() > ComputedMaxBSize()) { + ComputedMaxBSize() = ComputedMinBSize(); } } diff --git a/layout/generic/nsHTMLReflowState.h b/layout/generic/nsHTMLReflowState.h index a93d989643..5169ac9e8f 100644 --- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -18,7 +18,7 @@ class nsPresContext; class nsRenderingContext; class nsFloatManager; class nsLineLayout; -class nsIPercentHeightObserver; +class nsIPercentBSizeObserver; struct nsHypotheticalBox; /** @@ -159,17 +159,17 @@ public: } nsCSSOffsetState(nsIFrame *aFrame, nsRenderingContext *aRenderingContext, - nscoord aContainingBlockWidth); + nscoord aContainingBlockISize); #ifdef DEBUG // Reflow trace methods. Defined in nsFrame.cpp so they have access // to the display-reflow infrastructure. - static void* DisplayInitOffsetsEnter(nsIFrame* aFrame, - nsCSSOffsetState* aState, - nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis, - const nsMargin* aBorder, - const nsMargin* aPadding); + static void* DisplayInitOffsetsEnter( + nsIFrame* aFrame, + nsCSSOffsetState* aState, + const mozilla::LogicalSize& aPercentBasis, + const nsMargin* aBorder, + const nsMargin* aPadding); static void DisplayInitOffsetsExit(nsIFrame* aFrame, nsCSSOffsetState* aState, void* aValue); @@ -180,63 +180,63 @@ private: * Computes margin values from the specified margin style information, and * fills in the mComputedMargin member. * - * @param aInlineDirPercentBasis - * Length to use for resolving percentage margin values in the inline - * axis. Usually the containing block inline-size (width if writing mode - * is horizontal, and height if vertical). - * @param aBlockDirPercentBasis - * Length to use for resolving percentage margin values in the block - * axis. Usually the containing block inline-size, per CSS21 sec 8.3 - * (read in conjunction with CSS Writing Modes sec 7.2), but may - * be the containing block block-size, e.g. in CSS3 Flexbox and Grid. + * @param aPercentBasis + * Logical size to use for resolving percentage margin values in + * the inline and block axes. + * The inline size is usually the containing block inline-size + * (width if writing mode is horizontal, and height if vertical). + * The block size is usually the containing block inline-size, per + * CSS21 sec 8.3 (read in conjunction with CSS Writing Modes sec + * 7.2), but may be the containing block block-size, e.g. in CSS3 + * Flexbox and Grid. * @return true if the margin is dependent on the containing block size. */ - bool ComputeMargin(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis); + bool ComputeMargin(const mozilla::LogicalSize& aPercentBasis); /** * Computes padding values from the specified padding style information, and * fills in the mComputedPadding member. * - * @param aInlineDirPercentBasis - * Length to use for resolving percentage padding values in the inline - * axis. Usually the containing block inline-size. - * @param aBlockDirPercentBasis - * Length to use for resolving percentage padding values in the block - * axis. Usually the containing block inline-size, per CSS21 sec 8.4, - * but may be the containing block block-size in e.g. CSS3 Flexbox and - * Grid. + * @param aPercentBasis + * Length to use for resolving percentage padding values in + * the inline and block axes. + * The inline size is usually the containing block inline-size + * (width if writing mode is horizontal, and height if vertical). + * The block size is usually the containing block inline-size, per + * CSS21 sec 8.3 (read in conjunction with CSS Writing Modes sec + * 7.2), but may be the containing block block-size, e.g. in CSS3 + * Flexbox and Grid. * @return true if the padding is dependent on the containing block size. */ - bool ComputePadding(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis, nsIAtom* aFrameType); + bool ComputePadding(const mozilla::LogicalSize& aPercentBasis, + nsIAtom* aFrameType); protected: - void InitOffsets(nscoord aInlineDirPercentBasis, - nscoord aBlockDirPercentBasis, + void InitOffsets(const mozilla::LogicalSize& aPercentBasis, nsIAtom* aFrameType, const nsMargin *aBorder = nullptr, const nsMargin *aPadding = nullptr); /* * Convert nsStyleCoord to nscoord when percentages depend on the - * containing block width, and enumerated values are for width, - * min-width, or max-width. Does not handle auto widths. + * inline size of the containing block, and enumerated values are for + * inline size, min-inline-size, or max-inline-size. Does not handle + * auto inline sizes. */ - inline nscoord ComputeWidthValue(nscoord aContainingBlockWidth, + inline nscoord ComputeISizeValue(nscoord aContainingBlockISize, nscoord aContentEdgeToBoxSizing, nscoord aBoxSizingToMarginEdge, const nsStyleCoord& aCoord); // same as previous, but using mComputedBorderPadding, mComputedPadding, // and mComputedMargin - nscoord ComputeWidthValue(nscoord aContainingBlockWidth, + nscoord ComputeISizeValue(nscoord aContainingBlockISize, uint8_t aBoxSizing, const nsStyleCoord& aCoord); - nscoord ComputeHeightValue(nscoord aContainingBlockHeight, - uint8_t aBoxSizing, - const nsStyleCoord& aCoord); + nscoord ComputeBSizeValue(nscoord aContainingBlockBSize, + uint8_t aBoxSizing, + const nsStyleCoord& aCoord); }; /** @@ -492,8 +492,8 @@ public: uint8_t GetDisplay() const; // a frame (e.g. nsTableCellFrame) which may need to generate a special - // reflow for percent height calculations - nsIPercentHeightObserver* mPercentHeightObserver; + // reflow for percent bsize calculations + nsIPercentBSizeObserver* mPercentBSizeObserver; // CSS margin collapsing sometimes requires us to reflow // optimistically assuming that margins collapse to see if clearance @@ -507,8 +507,8 @@ public: int16_t mReflowDepth; struct ReflowStateFlags { - uint16_t mSpecialHeightReflow:1; // used by tables to communicate special reflow (in process) to handle - // percent height frames inside cells which may not have computed heights + uint16_t mSpecialBSizeReflow:1; // used by tables to communicate special reflow (in process) to handle + // percent bsize frames inside cells which may not have computed bsizes uint16_t mNextInFlowUntouched:1; // nothing in the frame's next-in-flow (or its descendants) // is changing uint16_t mIsTopOfPage:1; // Is the current context at the top of a @@ -614,12 +614,9 @@ public: * @param aFrame The frame for whose reflow state is being constructed. * @param aAvailableSpace See comments for availableHeight and availableWidth * members. - * @param aContainingBlockWidth An optional width, in app units, that is used - * by absolute positioning code to override default containing block - * width. - * @param aContainingBlockHeight An optional height, in app units, that is - * used by absolute positioning code to override default containing - * block height. + * @param aContainingBlockSize An optional size, in app units, that + * is used by absolute positioning code to override default containing + * block sizes. * @param aFlags A set of flags used for additional boolean parameters (see * below). */ @@ -627,8 +624,7 @@ public: const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const mozilla::LogicalSize& aAvailableSpace, - nscoord aContainingBlockWidth = -1, - nscoord aContainingBlockHeight = -1, + const mozilla::LogicalSize* aContainingBlockSize = nullptr, uint32_t aFlags = 0); // Values for |aFlags| passed to constructor @@ -644,11 +640,10 @@ public: // This method initializes various data members. It is automatically // called by the various constructors - void Init(nsPresContext* aPresContext, - nscoord aContainingBlockISize = -1, - nscoord aContainingBlockBSize = -1, - const nsMargin* aBorder = nullptr, - const nsMargin* aPadding = nullptr); + void Init(nsPresContext* aPresContext, + const mozilla::LogicalSize* aContainingBlockSize = nullptr, + const nsMargin* aBorder = nullptr, + const nsMargin* aPadding = nullptr); /** * Find the content isize of our containing block for the given writing mode, @@ -664,7 +659,7 @@ public: /** * Same as CalcLineHeight() above, but doesn't need a reflow state. * - * @param aBlockHeight The computed height of the content rect of the block + * @param aBlockBSize The computed block size of the content rect of the block * that the line should fill. * Only used with line-height:-moz-block-height. * NS_AUTOHEIGHT results in a normal line-height for @@ -680,10 +675,9 @@ public: float aFontSizeInflation); - void ComputeContainingBlockRectangle(nsPresContext* aPresContext, - const nsHTMLReflowState* aContainingBlockRS, - nscoord& aContainingBlockWidth, - nscoord& aContainingBlockHeight); + mozilla::LogicalSize ComputeContainingBlockRectangle( + nsPresContext* aPresContext, + const nsHTMLReflowState* aContainingBlockRS); /** * Apply the mComputed(Min/Max)Width constraints to the content @@ -754,16 +748,16 @@ public: } bool ShouldReflowAllKids() const { - // Note that we could make a stronger optimization for mVResize if + // Note that we could make a stronger optimization for IsBResize if // we use it in a ShouldReflowChild test that replaces the current // checks of NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN, if it - // were tested there along with NS_FRAME_CONTAINS_RELATIVE_HEIGHT. + // were tested there along with NS_FRAME_CONTAINS_RELATIVE_BSIZE. // This would need to be combined with a slight change in which - // frames NS_FRAME_CONTAINS_RELATIVE_HEIGHT is marked on. + // frames NS_FRAME_CONTAINS_RELATIVE_BSIZE is marked on. return (frame->GetStateBits() & NS_FRAME_IS_DIRTY) || - IsHResize() || - (IsVResize() && - (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)); + IsIResize() || + (IsBResize() && + (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)); } // This method doesn't apply min/max computed widths to the value passed in. @@ -803,10 +797,9 @@ public: } // Compute the offsets for a relative position element - static void ComputeRelativeOffsets(uint8_t aCBDirection, + static void ComputeRelativeOffsets(mozilla::WritingMode aWM, nsIFrame* aFrame, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, + const mozilla::LogicalSize& aCBSize, nsMargin& aComputedOffsets); // If a relatively positioned element, adjust the position appropriately. @@ -851,8 +844,8 @@ public: // to the display-reflow infrastructure. static void* DisplayInitConstraintsEnter(nsIFrame* aFrame, nsHTMLReflowState* aState, - nscoord aCBWidth, - nscoord aCBHeight, + nscoord aCBISize, + nscoord aCBBSize, const nsMargin* aBorder, const nsMargin* aPadding); static void DisplayInitConstraintsExit(nsIFrame* aFrame, @@ -870,46 +863,43 @@ protected: void InitCBReflowState(); void InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType); - void InitConstraints(nsPresContext* aPresContext, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsMargin* aBorder, - const nsMargin* aPadding, - nsIAtom* aFrameType); + void InitConstraints(nsPresContext* aPresContext, + const mozilla::LogicalSize& aContainingBlockSize, + const nsMargin* aBorder, + const nsMargin* aPadding, + nsIAtom* aFrameType); // Returns the nearest containing block or block frame (whether or not // it is a containing block) for the specified frame. Also returns - // the left edge and width of the containing block's content area. + // the inline-start edge and inline size of the containing block's + // content area. // These are returned in the coordinate space of the containing block. nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame, - nscoord& aCBLeftEdge, - nscoord& aCBWidth); + nscoord& aCBIStartEdge, + nscoord& aCBISize); void CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, nsIFrame* aContainingBlock, - nscoord aBlockLeftContentEdge, - nscoord aBlockContentWidth, + nscoord aBlockIStartContentEdge, + nscoord aBlockContentISize, const nsHTMLReflowState* cbrs, nsHypotheticalBox& aHypotheticalBox, nsIAtom* aFrameType); void InitAbsoluteConstraints(nsPresContext* aPresContext, const nsHTMLReflowState* cbrs, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, + const mozilla::LogicalSize& aContainingBlockSize, nsIAtom* aFrameType); // Calculates the computed values for the 'min-Width', 'max-Width', // 'min-Height', and 'max-Height' properties, and stores them in the assorted // data members - void ComputeMinMaxValues(nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsHTMLReflowState* aContainingBlockRS); + void ComputeMinMaxValues(const mozilla::LogicalSize& aContainingBlockSize); - void CalculateHorizBorderPaddingMargin(nscoord aContainingBlockWidth, - nscoord* aInsideBoxSizing, - nscoord* aOutsideBoxSizing); + void CalculateInlineBorderPaddingMargin(nscoord aContainingBlockISize, + nscoord* aInsideBoxSizing, + nscoord* aOutsideBoxSizing); void CalculateBlockSideMargins(nsIAtom* aFrameType); }; diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index 523175be06..e35e985901 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -32,10 +32,18 @@ class nsDisplayListBuilder; namespace mozilla { struct ContainerLayerParameters; +class DisplayItemClip; namespace layers { class Layer; } -} + +struct FrameMetricsAndClip +{ + layers::FrameMetrics metrics; + const DisplayItemClip* clip; +}; + +} // namespace mozilla /** * Interface for frames that are scrollable. This interface exposes @@ -415,10 +423,10 @@ public: * aLayer's animated geometry root is this frame. If there needs to be a * FrameMetrics contributed by this frame, append it to aOutput. */ - virtual void ComputeFrameMetrics(mozilla::layers::Layer* aLayer, - nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - nsTArray* aOutput) const = 0; + virtual mozilla::Maybe ComputeFrameMetrics( + mozilla::layers::Layer* aLayer, + nsIFrame* aContainerReferenceFrame, + const ContainerLayerParameters& aParameters) const = 0; /** * If this scroll frame is ignoring viewporting clipping diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index ece44b364a..8d665fa9b5 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -316,12 +316,13 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); + LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). + ConvertTo(wm, aMetrics.GetWritingMode()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, - aMetrics.Width(), - aMetrics.Height()); + &cbSize); nsRect posterRenderRect; if (ShouldDisplayPoster()) { @@ -356,12 +357,13 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); + LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). + ConvertTo(wm, aMetrics.GetWritingMode()); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, - aMetrics.Width(), - aMetrics.Height()); + &cbSize); nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index 98c185b5d3..6efa77caac 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -183,9 +183,9 @@ ViewportFrame::Reflow(nsPresContext* aPresContext, // Initialize OUT parameters aStatus = NS_FRAME_COMPLETE; - // Because |Reflow| sets mComputedHeight on the child to - // availableHeight. - AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + // Because |Reflow| sets ComputedBSize() on the child to our + // ComputedBSize(). + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index b8ad788555..ab32b74a55 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -196,28 +196,9 @@ public: } } - virtual void HandleLongTapUp(const CSSPoint& aPoint, - Modifiers aModifiers, - const ScrollableLayerGuid& aGuid) override - { - if (MessageLoop::current() != mUILoop) { - // We have to send this message from the "UI thread" (main - // thread). - mUILoop->PostTask( - FROM_HERE, - NewRunnableMethod(this, &RemoteContentController::HandleLongTapUp, - aPoint, aModifiers, aGuid)); - return; - } - if (mRenderFrame) { - TabParent* browser = TabParent::GetFrom(mRenderFrame->Manager()); - browser->HandleLongTapUp(aPoint, aModifiers, aGuid); - } - } - void ClearRenderFrame() { mRenderFrame = nullptr; } - virtual void SendAsyncScrollDOMEvent(bool aIsRoot, + virtual void SendAsyncScrollDOMEvent(bool aIsRootContent, const CSSRect& aContentRect, const CSSSize& aContentSize) override { @@ -226,10 +207,10 @@ public: FROM_HERE, NewRunnableMethod(this, &RemoteContentController::SendAsyncScrollDOMEvent, - aIsRoot, aContentRect, aContentSize)); + aIsRootContent, aContentRect, aContentSize)); return; } - if (mRenderFrame && aIsRoot) { + if (mRenderFrame && aIsRootContent) { TabParent* browser = TabParent::GetFrom(mRenderFrame->Manager()); BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect, aContentSize); diff --git a/layout/mathml/nsMathMLParts.h b/layout/mathml/nsMathMLParts.h index d203f2d585..c3907e6696 100644 --- a/layout/mathml/nsMathMLParts.h +++ b/layout/mathml/nsMathMLParts.h @@ -9,6 +9,8 @@ #include "nscore.h" #include "nsISupports.h" +class nsTableFrame; + // Factory methods for creating MathML objects nsIFrame* NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); @@ -26,7 +28,7 @@ nsIFrame* NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContex nsContainerFrame* NS_NewMathMLmtableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsContainerFrame* NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsContainerFrame* NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); -nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); +nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsTableFrame* aTableFrame); nsContainerFrame* NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* NS_NewMathMLmsqrtFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsIFrame* NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); diff --git a/layout/mathml/nsMathMLmtableFrame.cpp b/layout/mathml/nsMathMLmtableFrame.cpp index abe272f68f..b3935a66f8 100644 --- a/layout/mathml/nsMathMLmtableFrame.cpp +++ b/layout/mathml/nsMathMLmtableFrame.cpp @@ -1107,9 +1107,10 @@ nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID, // implementation of nsMathMLmtdFrame nsContainerFrame* -NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + nsTableFrame* aTableFrame) { - return new (aPresShell) nsMathMLmtdFrame(aContext); + return new (aPresShell) nsMathMLmtdFrame(aContext, aTableFrame); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame) @@ -1238,13 +1239,12 @@ nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame, return NS_OK; } -nsMargin* -nsMathMLmtdFrame::GetBorderWidth(nsMargin& aBorder) const +LogicalMargin +nsMathMLmtdFrame::GetBorderWidth(WritingMode aWM) const { nsStyleBorder styleBorder = *StyleBorder(); ApplyBorderToStyle(this, styleBorder); - aBorder = styleBorder.GetComputedBorder(); - return &aBorder; + return LogicalMargin(aWM, styleBorder.GetComputedBorder()); } nsMargin diff --git a/layout/mathml/nsMathMLmtableFrame.h b/layout/mathml/nsMathMLmtableFrame.h index 2c6b444d8d..0e9d9ddaac 100644 --- a/layout/mathml/nsMathMLmtableFrame.h +++ b/layout/mathml/nsMathMLmtableFrame.h @@ -238,7 +238,8 @@ public: NS_DECL_FRAMEARENA_HELPERS friend nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext); + nsStyleContext* aContext, + nsTableFrame* aTableFrame); // overloaded nsTableCellFrame methods @@ -263,12 +264,13 @@ public: return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML)); } - virtual nsMargin* GetBorderWidth(nsMargin& aBorder) const override; + virtual LogicalMargin GetBorderWidth(WritingMode aWM) const override; virtual nsMargin GetBorderOverflow() override; protected: - explicit nsMathMLmtdFrame(nsStyleContext* aContext) : nsTableCellFrame(aContext) {} + nsMathMLmtdFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame) + : nsTableCellFrame(aContext, aTableFrame) {} virtual ~nsMathMLmtdFrame(); }; // class nsMathMLmtdFrame diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001-ref.html new file mode 100644 index 0000000000..e2d59662a7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001.html new file mode 100644 index 0000000000..b2fb6d3f1f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-001.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: horizontal-tb' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002-ref.html new file mode 100644 index 0000000000..4c3166171b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002.html new file mode 100644 index 0000000000..8e1c724ed7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-002.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-rl' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003-ref.html new file mode 100644 index 0000000000..2d89eea66b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003.html new file mode 100644 index 0000000000..9b4450487f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-003.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-lr' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004-ref.html new file mode 100644 index 0000000000..1176830b56 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004.html new file mode 100644 index 0000000000..38f4158680 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-004.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: horizontal-tb' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005-ref.html new file mode 100644 index 0000000000..a0894911bd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005.html new file mode 100644 index 0000000000..65826fc2e3 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-005.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-rl' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006-ref.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006-ref.html new file mode 100644 index 0000000000..8c045354bc --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006-ref.html @@ -0,0 +1,76 @@ + + + + + CSS Reftest Reference + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006.html b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006.html new file mode 100644 index 0000000000..3e8352a45a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-writing-mode-006.html @@ -0,0 +1,78 @@ + + + + + CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-lr' + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/flexbox/reftest.list b/layout/reftests/w3c-css/submitted/flexbox/reftest.list index 9a1ea8f6cf..ca546ce51a 100644 --- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list +++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list @@ -182,3 +182,11 @@ fails == flexbox-min-width-auto-002b.html flexbox-min-width-auto-002-ref.html # == flexbox-with-pseudo-elements-001.html flexbox-with-pseudo-elements-001-ref.html == flexbox-with-pseudo-elements-002.html flexbox-with-pseudo-elements-002-ref.html == flexbox-with-pseudo-elements-003.html flexbox-with-pseudo-elements-003-ref.html + +# Tests for combined influence of 'writing-mode' & 'direction' on flex axes +test-pref(layout.css.vertical-text.enabled,true) == flexbox-writing-mode-001.html flexbox-writing-mode-001-ref.html +test-pref(layout.css.vertical-text.enabled,true) == flexbox-writing-mode-002.html flexbox-writing-mode-002-ref.html +test-pref(layout.css.vertical-text.enabled,true) == flexbox-writing-mode-003.html flexbox-writing-mode-003-ref.html +test-pref(layout.css.vertical-text.enabled,true) == flexbox-writing-mode-004.html flexbox-writing-mode-004-ref.html +test-pref(layout.css.vertical-text.enabled,true) fails == flexbox-writing-mode-005.html flexbox-writing-mode-005-ref.html # bug 1131451 +test-pref(layout.css.vertical-text.enabled,true) fails == flexbox-writing-mode-006.html flexbox-writing-mode-006-ref.html # bug 1131451 diff --git a/layout/reftests/w3c-css/submitted/ui3/reftest.list b/layout/reftests/w3c-css/submitted/ui3/reftest.list index 082a4e562b..8f677c7577 100644 --- a/layout/reftests/w3c-css/submitted/ui3/reftest.list +++ b/layout/reftests/w3c-css/submitted/ui3/reftest.list @@ -8,6 +8,7 @@ == box-sizing-padding-box-001.xht box-sizing-padding-box-001-ref.xht == box-sizing-padding-box-002.xht box-sizing-padding-box-002-ref.xht == box-sizing-padding-box-003.xht box-sizing-padding-box-003-ref.xht -random-if(Android) skip-if(B2G&&browserIsRemote) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht #bug 982547 -fuzzy-if(Android,27,874) random-if(B2G&&browserIsRemote) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht # Bug 1128229 -fuzzy-if(Android,14,869) random-if(B2G&&browserIsRemote) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229 +random-if(Android) skip-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht #bug 982547 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(Android,27,874) random-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht # Bug 1128229 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(Android,14,869) random-if((B2G&&browserIsRemote)||Mulet) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229 # Initial mulet triage: parity with B2G/B2G Desktop + diff --git a/layout/reftests/writing-mode/ua-style-sheet-size-1-ref.html b/layout/reftests/writing-mode/ua-style-sheet-size-1-ref.html index ecbdf0c419..06f6d5700e 100644 --- a/layout/reftests/writing-mode/ua-style-sheet-size-1-ref.html +++ b/layout/reftests/writing-mode/ua-style-sheet-size-1-ref.html @@ -15,6 +15,8 @@ marquee { background-color: yellow; color: transparent; }
A
+ diff --git a/layout/reftests/writing-mode/ua-style-sheet-size-1.html b/layout/reftests/writing-mode/ua-style-sheet-size-1.html index f43296d976..7549085010 100644 --- a/layout/reftests/writing-mode/ua-style-sheet-size-1.html +++ b/layout/reftests/writing-mode/ua-style-sheet-size-1.html @@ -12,6 +12,8 @@ marquee { background-color: yellow; color: transparent; }
A
+ diff --git a/layout/reftests/writing-mode/ua-style-sheet-size-2-ref.html b/layout/reftests/writing-mode/ua-style-sheet-size-2-ref.html index 5c81a70c0f..3c53326ba7 100644 --- a/layout/reftests/writing-mode/ua-style-sheet-size-2-ref.html +++ b/layout/reftests/writing-mode/ua-style-sheet-size-2-ref.html @@ -15,6 +15,8 @@ marquee { background-color: yellow; color: transparent; }
A
+ diff --git a/layout/reftests/writing-mode/ua-style-sheet-size-2.html b/layout/reftests/writing-mode/ua-style-sheet-size-2.html index 169491a435..1c0be6f4b5 100644 --- a/layout/reftests/writing-mode/ua-style-sheet-size-2.html +++ b/layout/reftests/writing-mode/ua-style-sheet-size-2.html @@ -12,6 +12,8 @@ marquee { background-color: yellow; color: transparent; }
A
+ diff --git a/layout/tables/celldata.h b/layout/tables/celldata.h index 4a225928e2..255a472a02 100644 --- a/layout/tables/celldata.h +++ b/layout/tables/celldata.h @@ -165,19 +165,19 @@ typedef uint16_t BCPixelSize; // the actual value is computed when needed. #define MAX_BORDER_WIDTH nscoord((1u << (sizeof(BCPixelSize) * 8)) - 1) -static inline nscoord -BC_BORDER_TOP_HALF_COORD(int32_t p2t, uint16_t px) { return (px - px / 2) * p2t; } -static inline nscoord -BC_BORDER_RIGHT_HALF_COORD(int32_t p2t, uint16_t px) { return ( px / 2) * p2t; } -static inline nscoord -BC_BORDER_BOTTOM_HALF_COORD(int32_t p2t, uint16_t px) { return ( px / 2) * p2t; } -static inline nscoord -BC_BORDER_LEFT_HALF_COORD(int32_t p2t, uint16_t px) { return (px - px / 2) * p2t; } +// The half of border on inline/block-axis start side +static inline BCPixelSize +BC_BORDER_START_HALF(BCPixelSize px) { return px - px / 2; } +// The half of border on inline/block-axis end side +static inline BCPixelSize +BC_BORDER_END_HALF(BCPixelSize px) { return px / 2; } -#define BC_BORDER_TOP_HALF(px) ((px) - (px) / 2) -#define BC_BORDER_RIGHT_HALF(px) ((px) / 2) -#define BC_BORDER_BOTTOM_HALF(px) ((px) / 2) -#define BC_BORDER_LEFT_HALF(px) ((px) - (px) / 2) +static inline nscoord +BC_BORDER_START_HALF_COORD(int32_t p2t, BCPixelSize px) + { return BC_BORDER_START_HALF(px) * p2t; } +static inline nscoord +BC_BORDER_END_HALF_COORD(int32_t p2t, BCPixelSize px) + { return BC_BORDER_END_HALF(px) * p2t; } // BCData stores the top and left border info and the corner connecting the two. class BCData diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index 418f8bcd00..142561bbbb 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -42,15 +42,16 @@ using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::image; -nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) : - nsContainerFrame(aContext) - , mDesiredSize(GetWritingMode()) +nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext, + nsTableFrame* aTableFrame) + : nsContainerFrame(aContext) + , mDesiredSize(aTableFrame->GetWritingMode()) { mColIndex = 0; - mPriorAvailWidth = 0; + mPriorAvailISize = 0; SetContentEmpty(false); - SetHasPctOverHeight(false); + SetHasPctOverBSize(false); } nsTableCellFrame::~nsTableCellFrame() @@ -104,10 +105,10 @@ nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot) nsContainerFrame::DestroyFrom(aDestructRoot); } -// nsIPercentHeightObserver methods +// nsIPercentBSizeObserver methods void -nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState) +nsTableCellFrame::NotifyPercentBSize(const nsHTMLReflowState& aReflowState) { // nsHTMLReflowState ensures the mCBReflowState of blocks inside a // cell is the cell frame, not the inner-cell block, and that the @@ -120,28 +121,28 @@ nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState) const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState; if (cellRS && cellRS->frame == this && - (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE || - cellRS->ComputedHeight() == 0)) { // XXXldb Why 0? - // This is a percentage height on a frame whose percentage heights - // are based on the height of the cell, since its containing block + (cellRS->ComputedBSize() == NS_UNCONSTRAINEDSIZE || + cellRS->ComputedBSize() == 0)) { // XXXldb Why 0? + // This is a percentage bsize on a frame whose percentage bsizes + // are based on the bsize of the cell, since its containing block // is the inner cell frame. - // We'll only honor the percent height if sibling-cells/ancestors - // have specified/pct height. (Also, siblings only count for this if + // We'll only honor the percent bsize if sibling-cells/ancestors + // have specified/pct bsize. (Also, siblings only count for this if // both this cell and the sibling cell span exactly 1 row.) - if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) || + if (nsTableFrame::AncestorsHaveStyleBSize(*cellRS) || (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 && (cellRS->parentReflowState->frame->GetStateBits() & - NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) { + NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) { for (const nsHTMLReflowState *rs = aReflowState.parentReflowState; rs != cellRS; rs = rs->parentReflowState) { - rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } - nsTableFrame::RequestSpecialHeightReflow(*cellRS); + nsTableFrame::RequestSpecialBSizeReflow(*cellRS); } } } @@ -164,7 +165,7 @@ nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) return false; } - // We always need to let the percent height observer be propagated + // We always need to let the percent bsize observer be propagated // from an outer table frame to an inner table frame. nsIAtom *fType = aReflowState.frame->GetType(); if (fType == nsGkAtoms::tableFrame) { @@ -174,6 +175,9 @@ nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) // We need the observer to be propagated to all children of the cell // (i.e., children of the child block) in quirks mode, but only to // tables in standards mode. + // XXX This may not be true in the case of orthogonal flows within + // the cell (bug 1174711 comment 8); we may need to observe isizes + // instead of bsizes for orthogonal children. return rs->frame == this && (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks || fType == nsGkAtoms::tableOuterFrame); @@ -580,66 +584,68 @@ nsTableCellFrame::GetBorderOverflow() // Align the cell's child frame within the cell -void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent) +void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) { /* It's the 'border-collapse' on the table that matters */ - nsMargin borderPadding = GetUsedBorderAndPadding(); + LogicalMargin borderPadding = GetLogicalUsedBorderAndPadding(aWM); - nscoord topInset = borderPadding.top; - nscoord bottomInset = borderPadding.bottom; + nscoord bStartInset = borderPadding.BStart(aWM); + nscoord bEndInset = borderPadding.BEnd(aWM); uint8_t verticalAlignFlags = GetVerticalAlign(); - nscoord height = mRect.height; + nscoord bSize = BSize(aWM); nsIFrame* firstKid = mFrames.FirstChild(); - NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); - nsRect kidRect = firstKid->GetRect(); - nscoord childHeight = kidRect.height; + nscoord containerWidth = mRect.width; + NS_ASSERTION(firstKid, "Frame construction error, a table cell always has " + "an inner cell frame"); + LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerWidth); + nscoord childBSize = kidRect.BSize(aWM); // Vertically align the child - nscoord kidYTop = 0; + nscoord kidBStart = 0; switch (verticalAlignFlags) { case NS_STYLE_VERTICAL_ALIGN_BASELINE: // Align the baselines of the child frame with the baselines of // other children in the same row which have 'vertical-align: baseline' - kidYTop = topInset + aMaxAscent - GetCellBaseline(); + kidBStart = bStartInset + aMaxAscent - GetCellBaseline(); break; case NS_STYLE_VERTICAL_ALIGN_TOP: // Align the top of the child frame with the top of the content area, - kidYTop = topInset; + kidBStart = bStartInset; break; case NS_STYLE_VERTICAL_ALIGN_BOTTOM: // Align the bottom of the child frame with the bottom of the content area, - kidYTop = height - childHeight - bottomInset; + kidBStart = bSize - childBSize - bEndInset; break; default: case NS_STYLE_VERTICAL_ALIGN_MIDDLE: // Align the middle of the child frame with the middle of the content area, - kidYTop = (height - childHeight - bottomInset + topInset) / 2; + kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2; } - // if the content is larger than the cell height align from top - kidYTop = std::max(0, kidYTop); + // if the content is larger than the cell bsize, align from bstart + kidBStart = std::max(0, kidBStart); - if (kidYTop != kidRect.y) { + if (kidBStart != kidRect.BStart(aWM)) { // Invalidate at the old position first firstKid->InvalidateFrameSubtree(); } - firstKid->SetPosition(nsPoint(kidRect.x, kidYTop)); - WritingMode wm = GetWritingMode(); - nsHTMLReflowMetrics desiredSize(wm); - desiredSize.SetSize(wm, GetLogicalSize(wm)); + firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), + kidBStart), containerWidth); + nsHTMLReflowMetrics desiredSize(aWM); + desiredSize.SetSize(aWM, GetLogicalSize(aWM)); nsRect overflow(nsPoint(0,0), GetSize()); overflow.Inflate(GetBorderOverflow()); desiredSize.mOverflowAreas.SetAllTo(overflow); ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid); FinishAndStoreOverflow(&desiredSize); - if (kidYTop != kidRect.y) { + if (kidBStart != kidRect.BStart(aWM)) { // Make sure any child views are correctly positioned. We know the inner table // cell won't have a view nsContainerFrame::PositionChildViews(firstKid); @@ -795,9 +801,8 @@ nsTableCellFrame::IntrinsicISizeOffsets(nsRenderingContext* aRenderingContext) result.hMargin = 0; result.hPctMargin = 0; - nsMargin border; - GetBorderWidth(border); - result.hBorder = border.LeftRight(); + WritingMode wm = GetWritingMode(); + result.hBorder = GetBorderWidth(wm).IStartEnd(wm); return result; } @@ -806,8 +811,7 @@ nsTableCellFrame::IntrinsicISizeOffsets(nsRenderingContext* aRenderingContext) #define PROBABLY_TOO_LARGE 1000000 static void DebugCheckChildSize(nsIFrame* aChild, - nsHTMLReflowMetrics& aMet, - nsSize& aAvailSize) + nsHTMLReflowMetrics& aMet) { WritingMode wm = aMet.GetWritingMode(); if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) { @@ -817,14 +821,14 @@ void DebugCheckChildSize(nsIFrame* aChild, } #endif -// the computed height for the cell, which descendants use for percent height calculations -// it is the height (minus border, padding) of the cell's first in flow during its final -// reflow without an unconstrained height. +// the computed bsize for the cell, which descendants use for percent bsize calculations +// it is the bsize (minus border, padding) of the cell's first in flow during its final +// reflow without an unconstrained bsize. static nscoord -CalcUnpaginagedHeight(nsPresContext* aPresContext, - nsTableCellFrame& aCellFrame, - nsTableFrame& aTableFrame, - nscoord aVerticalBorderPadding) +CalcUnpaginatedBSize(nsPresContext* aPresContext, + nsTableCellFrame& aCellFrame, + nsTableFrame& aTableFrame, + nscoord aBlockDirBorderPadding) { const nsTableCellFrame* firstCellInFlow = static_cast(aCellFrame.FirstInFlow()); @@ -839,19 +843,19 @@ CalcUnpaginagedHeight(nsPresContext* aPresContext, firstCellInFlow->GetRowIndex(rowIndex); int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); - nscoord computedHeight = firstTableInFlow->GetRowSpacing(rowIndex, - rowIndex + rowSpan - 1); - computedHeight -= aVerticalBorderPadding; + nscoord computedBSize = firstTableInFlow->GetRowSpacing(rowIndex, + rowIndex + rowSpan - 1); + computedBSize -= aBlockDirBorderPadding; int32_t rowX; for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { if (rowX > rowIndex + rowSpan - 1) { break; } else if (rowX >= rowIndex) { - computedHeight += row->GetUnpaginatedHeight(aPresContext); + computedBSize += row->GetUnpaginatedBSize(aPresContext); } } - return computedHeight; + return computedBSize; } void @@ -864,92 +868,99 @@ nsTableCellFrame::Reflow(nsPresContext* aPresContext, DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); - if (aReflowState.mFlags.mSpecialHeightReflow) { + if (aReflowState.mFlags.mSpecialBSizeReflow) { FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); } - // see if a special height reflow needs to occur due to having a pct height - nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); + // see if a special bsize reflow needs to occur due to having a pct height + nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowState); aStatus = NS_FRAME_COMPLETE; - nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); + WritingMode wm = aReflowState.GetWritingMode(); + LogicalSize availSize(wm, aReflowState.AvailableISize(), + aReflowState.AvailableBSize()); - nsMargin borderPadding = aReflowState.ComputedPhysicalPadding(); - nsMargin border; - GetBorderWidth(border); + LogicalMargin borderPadding = aReflowState.ComputedLogicalPadding(); + LogicalMargin border = GetBorderWidth(wm); borderPadding += border; - nscoord topInset = borderPadding.top; - nscoord rightInset = borderPadding.right; - nscoord bottomInset = borderPadding.bottom; - nscoord leftInset = borderPadding.left; - // reduce available space by insets, if we're in a constrained situation - availSize.width -= leftInset + rightInset; - if (NS_UNCONSTRAINEDSIZE != availSize.height) - availSize.height -= topInset + bottomInset; + availSize.ISize(wm) -= borderPadding.IStartEnd(wm); + if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { + availSize.BSize(wm) -= borderPadding.BStartEnd(wm); + } // Try to reflow the child into the available space. It might not // fit or might need continuing. - if (availSize.height < 0) - availSize.height = 1; + if (availSize.BSize(wm) < 0) { + availSize.BSize(wm) = 1; + } - WritingMode wm = aReflowState.GetWritingMode(); nsHTMLReflowMetrics kidSize(wm, aDesiredSize.mFlags); kidSize.ClearSize(); - SetPriorAvailWidth(aReflowState.AvailableWidth()); + SetPriorAvailISize(aReflowState.AvailableISize()); nsIFrame* firstKid = mFrames.FirstChild(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); nsTableFrame* tableFrame = GetTableFrame(); - if (aReflowState.mFlags.mSpecialHeightReflow) { - const_cast(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset); + if (aReflowState.mFlags.mSpecialBSizeReflow) { + const_cast(aReflowState). + SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm)); DISPLAY_REFLOW_CHANGE(); } else if (aPresContext->IsPaginated()) { - nscoord computedUnpaginatedHeight = - CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this, - *tableFrame, topInset + bottomInset); - if (computedUnpaginatedHeight > 0) { - const_cast(aReflowState).SetComputedHeight(computedUnpaginatedHeight); + nscoord computedUnpaginatedBSize = + CalcUnpaginatedBSize(aPresContext, (nsTableCellFrame&)*this, + *tableFrame, borderPadding.BStartEnd(wm)); + if (computedUnpaginatedBSize > 0) { + const_cast(aReflowState).SetComputedBSize(computedUnpaginatedBSize); DISPLAY_REFLOW_CHANGE(); } } else { - SetHasPctOverHeight(false); + SetHasPctOverBSize(false); } + WritingMode kidWM = firstKid->GetWritingMode(); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, - LogicalSize(firstKid->GetWritingMode(), - availSize)); + availSize.ConvertTo(kidWM, wm)); // Don't be a percent height observer if we're in the middle of - // special-height reflow, in case we get an accidental NotifyPercentHeight() - // call (which we shouldn't honor during special-height reflow) - if (!aReflowState.mFlags.mSpecialHeightReflow) { - // mPercentHeightObserver is for children of cells in quirks mode, + // special-bsize reflow, in case we get an accidental NotifyPercentBSize() + // call (which we shouldn't honor during special-bsize reflow) + if (!aReflowState.mFlags.mSpecialBSizeReflow) { + // mPercentBSizeObserver is for children of cells in quirks mode, // but only those than are tables in standards mode. NeedsToObserve // will determine how far this is propagated to descendants. - kidReflowState.mPercentHeightObserver = this; + kidReflowState.mPercentBSizeObserver = this; } - // Don't propagate special height reflow state to our kids - kidReflowState.mFlags.mSpecialHeightReflow = false; + // Don't propagate special bsize reflow state to our kids + kidReflowState.mFlags.mSpecialBSizeReflow = false; - if (aReflowState.mFlags.mSpecialHeightReflow || + if (aReflowState.mFlags.mSpecialBSizeReflow || (FirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { - // We need to force the kid to have mVResize set if we've had a + // We need to force the kid to have mBResize set if we've had a // special reflow in the past, since the non-special reflow needs to - // resize back to what it was without the special height reflow. - kidReflowState.SetVResize(true); + // resize back to what it was without the special bsize reflow. + kidReflowState.SetBResize(true); } - nsPoint kidOrigin(leftInset, topInset); + nscoord containerWidth; + if (aReflowState.ComputedWidth() == NS_UNCONSTRAINEDSIZE) { + containerWidth = 0; // avoid passing unconstrained container width to + // ReflowChild; but position will not be valid + } else { + containerWidth = aReflowState.ComputedWidth() + + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); + } + LogicalPoint kidOrigin(wm, borderPadding.IStart(wm), + borderPadding.BStart(wm)); nsRect origRect = firstKid->GetRect(); nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); bool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, - kidOrigin.x, kidOrigin.y, 0, aStatus); + wm, kidOrigin, containerWidth, 0, aStatus); if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it //XXX should paginate overflow as overflow, but not in this patch (bug 379349) @@ -963,7 +974,7 @@ nsTableCellFrame::Reflow(nsPresContext* aPresContext, } #ifdef DEBUG - DebugCheckChildSize(firstKid, kidSize, availSize); + DebugCheckChildSize(firstKid, kidSize); #endif // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode @@ -979,42 +990,41 @@ nsTableCellFrame::Reflow(nsPresContext* aPresContext, // Place the child FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowState, - kidOrigin.x, kidOrigin.y, 0); + wm, kidOrigin, containerWidth, 0); nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, firstReflow); - // first, compute the height which can be set w/o being restricted by aMaxSize.height + // first, compute the bsize which can be set w/o being restricted by + // available bsize LogicalSize cellSize(wm); - LogicalMargin logicalInsets(wm, nsMargin(topInset, rightInset, - bottomInset, leftInset)); cellSize.BSize(wm) = kidSize.BSize(wm); if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) { - cellSize.BSize(wm) += logicalInsets.BStartEnd(wm); + cellSize.BSize(wm) += borderPadding.BStartEnd(wm); } - // next determine the cell's width + // next determine the cell's isize cellSize.ISize(wm) = kidSize.ISize(wm); // at this point, we've factored in the cell's style attributes // factor in border and padding if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) { - cellSize.ISize(wm) += logicalInsets.IStartEnd(wm); + cellSize.ISize(wm) += borderPadding.IStartEnd(wm); } // set the cell's desired size and max element size aDesiredSize.SetSize(wm, cellSize); - // the overflow area will be computed when the child will be vertically aligned + // the overflow area will be computed when BlockDirAlignChild() gets called - if (aReflowState.mFlags.mSpecialHeightReflow) { - if (aDesiredSize.Height() > mRect.height) { - // set a bit indicating that the pct height contents exceeded + if (aReflowState.mFlags.mSpecialBSizeReflow) { + if (aDesiredSize.BSize(wm) > BSize(wm)) { + // set a bit indicating that the pct bsize contents exceeded // the height that they could honor in the pass 2 reflow - SetHasPctOverHeight(true); + SetHasPctOverBSize(true); } - if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { - aDesiredSize.Height() = mRect.height; + if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableBSize()) { + aDesiredSize.BSize(wm) = BSize(wm); } } @@ -1041,7 +1051,7 @@ nsTableCellFrame::Reflow(nsPresContext* aPresContext, NS_QUERYFRAME_HEAD(nsTableCellFrame) NS_QUERYFRAME_ENTRY(nsTableCellFrame) NS_QUERYFRAME_ENTRY(nsITableCellLayout) - NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver) + NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) #ifdef ACCESSIBILITY @@ -1069,21 +1079,20 @@ nsTableCellFrame::GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex) nsTableCellFrame* NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, - bool aIsBorderCollapse) + nsTableFrame* aTableFrame) { - if (aIsBorderCollapse) - return new (aPresShell) nsBCTableCellFrame(aContext); + if (aTableFrame->IsBorderCollapse()) + return new (aPresShell) nsBCTableCellFrame(aContext, aTableFrame); else - return new (aPresShell) nsTableCellFrame(aContext); + return new (aPresShell) nsTableCellFrame(aContext, aTableFrame); } NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame) -nsMargin* -nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const +LogicalMargin +nsTableCellFrame::GetBorderWidth(WritingMode aWM) const { - aBorder = StyleBorder()->GetComputedBorder(); - return &aBorder; + return LogicalMargin(aWM, StyleBorder()->GetComputedBorder()); } nsIAtom* @@ -1102,10 +1111,11 @@ nsTableCellFrame::GetFrameName(nsAString& aResult) const // nsBCTableCellFrame -nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext) -:nsTableCellFrame(aContext) +nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext, + nsTableFrame* aTableFrame) + : nsTableCellFrame(aContext, aTableFrame) { - mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0; + mBStartBorder = mIEndBorder = mBEndBorder = mIStartBorder = 0; } nsBCTableCellFrame::~nsBCTableCellFrame() @@ -1121,9 +1131,8 @@ nsBCTableCellFrame::GetType() const /* virtual */ nsMargin nsBCTableCellFrame::GetUsedBorder() const { - nsMargin result; - GetBorderWidth(result); - return result; + WritingMode wm = GetWritingMode(); + return GetBorderWidth(wm).GetPhysicalMargin(wm); } /* virtual */ bool @@ -1146,61 +1155,61 @@ nsBCTableCellFrame::GetFrameName(nsAString& aResult) const } #endif -nsMargin* -nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const +LogicalMargin +nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const { - int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder); - aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder); - aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder); - aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder); - return &aBorder; + int32_t pixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); + return LogicalMargin(aWM, + BC_BORDER_END_HALF_COORD(pixelsToTwips, mBStartBorder), + BC_BORDER_START_HALF_COORD(pixelsToTwips, mIEndBorder), + BC_BORDER_START_HALF_COORD(pixelsToTwips, mBEndBorder), + BC_BORDER_END_HALF_COORD(pixelsToTwips, mIStartBorder)); } BCPixelSize -nsBCTableCellFrame::GetBorderWidth(mozilla::css::Side aSide) const +nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const { switch(aSide) { - case NS_SIDE_TOP: - return BC_BORDER_BOTTOM_HALF(mTopBorder); - case NS_SIDE_RIGHT: - return BC_BORDER_LEFT_HALF(mRightBorder); - case NS_SIDE_BOTTOM: - return BC_BORDER_TOP_HALF(mBottomBorder); + case eLogicalSideBStart: + return BC_BORDER_END_HALF(mBStartBorder); + case eLogicalSideIEnd: + return BC_BORDER_START_HALF(mIEndBorder); + case eLogicalSideBEnd: + return BC_BORDER_START_HALF(mBEndBorder); default: - return BC_BORDER_RIGHT_HALF(mLeftBorder); + return BC_BORDER_END_HALF(mIStartBorder); } } void -nsBCTableCellFrame::SetBorderWidth(mozilla::css::Side aSide, - BCPixelSize aValue) +nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) { switch(aSide) { - case NS_SIDE_TOP: - mTopBorder = aValue; + case eLogicalSideBStart: + mBStartBorder = aValue; break; - case NS_SIDE_RIGHT: - mRightBorder = aValue; + case eLogicalSideIEnd: + mIEndBorder = aValue; break; - case NS_SIDE_BOTTOM: - mBottomBorder = aValue; + case eLogicalSideBEnd: + mBEndBorder = aValue; break; default: - mLeftBorder = aValue; + mIStartBorder = aValue; } } /* virtual */ nsMargin nsBCTableCellFrame::GetBorderOverflow() { - nsMargin halfBorder; + WritingMode wm = GetWritingMode(); int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); - halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder); - halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder); - halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder); - halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder); - return halfBorder; + LogicalMargin halfBorder(wm, + BC_BORDER_START_HALF_COORD(p2t, mBStartBorder), + BC_BORDER_END_HALF_COORD(p2t, mIEndBorder), + BC_BORDER_END_HALF_COORD(p2t, mBEndBorder), + BC_BORDER_START_HALF_COORD(p2t, mIStartBorder)); + return halfBorder.GetPhysicalMargin(wm); } @@ -1212,8 +1221,8 @@ nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, { // make border-width reflect the half of the border-collapse // assigned border that's inside the cell - nsMargin borderWidth; - GetBorderWidth(borderWidth); + WritingMode wm = GetWritingMode(); + nsMargin borderWidth = GetBorderWidth(wm).GetPhysicalMargin(wm); nsStyleBorder myBorder(*StyleBorder()); diff --git a/layout/tables/nsTableCellFrame.h b/layout/tables/nsTableCellFrame.h index a9c504b723..d454546e9c 100644 --- a/layout/tables/nsTableCellFrame.h +++ b/layout/tables/nsTableCellFrame.h @@ -12,11 +12,12 @@ #include "nscore.h" #include "nsContainerFrame.h" #include "nsStyleContext.h" -#include "nsIPercentHeightObserver.h" +#include "nsIPercentBSizeObserver.h" #include "nsGkAtoms.h" #include "nsLayoutUtils.h" #include "nsTArray.h" #include "nsTableRowFrame.h" +#include "mozilla/WritingModes.h" /** * nsTableCellFrame @@ -31,10 +32,15 @@ */ class nsTableCellFrame : public nsContainerFrame, public nsITableCellLayout, - public nsIPercentHeightObserver + public nsIPercentBSizeObserver { typedef mozilla::image::DrawResult DrawResult; +protected: + typedef mozilla::WritingMode WritingMode; + typedef mozilla::LogicalSide LogicalSide; + typedef mozilla::LogicalMargin LogicalMargin; + public: NS_DECL_QUERYFRAME_TARGET(nsTableCellFrame) NS_DECL_QUERYFRAME @@ -42,7 +48,7 @@ public: // default constructor supplied by the compiler - explicit nsTableCellFrame(nsStyleContext* aContext); + nsTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame); ~nsTableCellFrame(); nsTableRowFrame* GetTableRowFrame() const @@ -92,17 +98,10 @@ public: virtual nsMargin GetUsedMargin() const override; - virtual void NotifyPercentHeight(const nsHTMLReflowState& aReflowState) override; + virtual void NotifyPercentBSize(const nsHTMLReflowState& aReflowState) override; virtual bool NeedsToObserve(const nsHTMLReflowState& aReflowState) override; - /** instantiate a new instance of nsTableRowFrame. - * @param aPresShell the pres shell for this frame - * - * @return the frame that was created - */ - friend nsIFrame* NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; @@ -137,7 +136,15 @@ public: virtual nsresult GetFrameName(nsAString& aResult) const override; #endif - void VerticallyAlignChild(nscoord aMaxAscent); + // Although the spec doesn't say that writing-mode is not applied to + // table-cells, we still override this method here because we want to + // make effective writing mode of table structure frames consistent + // within a table. The content inside table cells is reflowed by an + // anonymous block, hence their writing mode is not affected. + virtual mozilla::WritingMode GetWritingMode() const override + { return GetTableFrame()->GetWritingMode(); } + + void BlockDirAlignChild(mozilla::WritingMode aWM, nscoord aMaxAscent); /* * Get the value of vertical-align adjusted for CSS 2's rules for a @@ -150,12 +157,12 @@ public: return GetVerticalAlign() == NS_STYLE_VERTICAL_ALIGN_BASELINE; } - bool CellHasVisibleContent(nscoord height, - nsTableFrame* tableFrame, - nsIFrame* kidFrame); + bool CellHasVisibleContent(nscoord aBSize, + nsTableFrame* tableFrame, + nsIFrame* kidFrame); /** - * Get the first-line baseline of the cell relative to its top border + * Get the first-line baseline of the cell relative to its block-start border * edge, as if the cell were vertically aligned to the top of the row. */ nscoord GetCellBaseline() const; @@ -194,11 +201,11 @@ public: virtual nsresult GetColIndex(int32_t &aColIndex) const override; void SetColIndex(int32_t aColIndex); - /** return the available width given to this frame during its last reflow */ - inline nscoord GetPriorAvailWidth(); + /** return the available isize given to this frame during its last reflow */ + inline nscoord GetPriorAvailISize(); - /** set the available width given to this frame during its last reflow */ - inline void SetPriorAvailWidth(nscoord aPriorAvailWidth); + /** set the available isize given to this frame during its last reflow */ + inline void SetPriorAvailISize(nscoord aPriorAvailISize); /** return the desired size returned by this frame during its last reflow */ inline mozilla::LogicalSize GetDesiredSize(); @@ -209,12 +216,12 @@ public: bool GetContentEmpty(); void SetContentEmpty(bool aContentEmpty); - bool HasPctOverHeight(); - void SetHasPctOverHeight(bool aValue); + bool HasPctOverBSize(); + void SetHasPctOverBSize(bool aValue); nsTableCellFrame* GetNextCell() const; - virtual nsMargin* GetBorderWidth(nsMargin& aBorder) const; + virtual LogicalMargin GetBorderWidth(WritingMode aWM) const; virtual DrawResult PaintBackground(nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, @@ -236,7 +243,8 @@ public: virtual void InvalidateFrameForRemoval() override { InvalidateFrameSubtree(); } protected: - virtual LogicalSides GetLogicalSkipSides(const nsHTMLReflowState* aReflowState= nullptr) const override; + virtual LogicalSides + GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const override; /** * GetBorderOverflow says how far the cell's own borders extend @@ -251,15 +259,15 @@ protected: uint32_t mColIndex; // the starting column for this cell - nscoord mPriorAvailWidth; // the avail width during the last reflow + nscoord mPriorAvailISize; // the avail isize during the last reflow mozilla::LogicalSize mDesiredSize; // the last desired inline and block size }; -inline nscoord nsTableCellFrame::GetPriorAvailWidth() -{ return mPriorAvailWidth;} +inline nscoord nsTableCellFrame::GetPriorAvailISize() +{ return mPriorAvailISize; } -inline void nsTableCellFrame::SetPriorAvailWidth(nscoord aPriorAvailWidth) -{ mPriorAvailWidth = aPriorAvailWidth;} +inline void nsTableCellFrame::SetPriorAvailISize(nscoord aPriorAvailISize) +{ mPriorAvailISize = aPriorAvailISize; } inline mozilla::LogicalSize nsTableCellFrame::GetDesiredSize() { return mDesiredSize; } @@ -285,18 +293,18 @@ inline void nsTableCellFrame::SetContentEmpty(bool aContentEmpty) } } -inline bool nsTableCellFrame::HasPctOverHeight() +inline bool nsTableCellFrame::HasPctOverBSize() { - return (mState & NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT) == - NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT; + return (mState & NS_TABLE_CELL_HAS_PCT_OVER_BSIZE) == + NS_TABLE_CELL_HAS_PCT_OVER_BSIZE; } -inline void nsTableCellFrame::SetHasPctOverHeight(bool aValue) +inline void nsTableCellFrame::SetHasPctOverBSize(bool aValue) { if (aValue) { - mState |= NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT; + mState |= NS_TABLE_CELL_HAS_PCT_OVER_BSIZE; } else { - mState &= ~NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT; + mState &= ~NS_TABLE_CELL_HAS_PCT_OVER_BSIZE; } } @@ -307,7 +315,7 @@ class nsBCTableCellFrame final : public nsTableCellFrame public: NS_DECL_FRAMEARENA_HELPERS - explicit nsBCTableCellFrame(nsStyleContext* aContext); + nsBCTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame); ~nsBCTableCellFrame(); @@ -320,13 +328,13 @@ public: nscoord aRadii[8]) const override; // Get the *inner half of the border only*, in twips. - virtual nsMargin* GetBorderWidth(nsMargin& aBorder) const override; + virtual LogicalMargin GetBorderWidth(WritingMode aWM) const override; // Get the *inner half of the border only*, in pixels. - BCPixelSize GetBorderWidth(mozilla::css::Side aSide) const; + BCPixelSize GetBorderWidth(LogicalSide aSide) const; // Set the full (both halves) width of the border - void SetBorderWidth(mozilla::css::Side aSide, BCPixelSize aPixelValue); + void SetBorderWidth(LogicalSide aSide, BCPixelSize aPixelValue); virtual nsMargin GetBorderOverflow() override; @@ -343,10 +351,10 @@ private: // These are the entire width of the border (the cell edge contains only // the inner half, per the macros in nsTablePainter.h). - BCPixelSize mTopBorder; - BCPixelSize mRightBorder; - BCPixelSize mBottomBorder; - BCPixelSize mLeftBorder; + BCPixelSize mBStartBorder; + BCPixelSize mIEndBorder; + BCPixelSize mBEndBorder; + BCPixelSize mIStartBorder; }; #endif diff --git a/layout/tables/nsTableColFrame.cpp b/layout/tables/nsTableColFrame.cpp index 0a7d47a929..69d64a32aa 100644 --- a/layout/tables/nsTableColFrame.cpp +++ b/layout/tables/nsTableColFrame.cpp @@ -13,6 +13,8 @@ #include "nsCSSRendering.h" #include "nsIContent.h" +using namespace mozilla; + #define COL_TYPE_BITS (NS_FRAME_STATE_BIT(28) | \ NS_FRAME_STATE_BIT(29) | \ NS_FRAME_STATE_BIT(30) | \ @@ -69,18 +71,18 @@ nsTableColFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) } } -void nsTableColFrame::SetContinuousBCBorderWidth(uint8_t aForSide, +void nsTableColFrame::SetContinuousBCBorderWidth(LogicalSide aForSide, BCPixelSize aPixelValue) { switch (aForSide) { - case NS_SIDE_TOP: - mTopContBorderWidth = aPixelValue; + case eLogicalSideBStart: + mBStartContBorderWidth = aPixelValue; return; - case NS_SIDE_RIGHT: - mRightContBorderWidth = aPixelValue; + case eLogicalSideIEnd: + mIEndContBorderWidth = aPixelValue; return; - case NS_SIDE_BOTTOM: - mBottomContBorderWidth = aPixelValue; + case eLogicalSideBEnd: + mBEndContBorderWidth = aPixelValue; return; default: NS_ERROR("invalid side arg"); diff --git a/layout/tables/nsTableColFrame.h b/layout/tables/nsTableColFrame.h index d5abfceee2..5646cb21de 100644 --- a/layout/tables/nsTableColFrame.h +++ b/layout/tables/nsTableColFrame.h @@ -11,6 +11,7 @@ #include "nsContainerFrame.h" #include "nsTArray.h" #include "nsTableColGroupFrame.h" +#include "mozilla/WritingModes.h" class nsTableColFrame : public nsSplittableFrame { public: @@ -78,16 +79,19 @@ public: virtual nsSplittableType GetSplittableType() const override; + virtual mozilla::WritingMode GetWritingMode() const override + { return GetTableFrame()->GetWritingMode(); } + /** return the number of the columns the col represents. always >= 1 */ int32_t GetSpan(); /** convenience method, calls into cellmap */ int32_t Count() const; - nscoord GetLeftBorderWidth(); - void SetLeftBorderWidth(BCPixelSize aWidth); - nscoord GetRightBorderWidth(); - void SetRightBorderWidth(BCPixelSize aWidth); + nscoord GetIStartBorderWidth() const { return mIStartBorderWidth; } + nscoord GetIEndBorderWidth() const { return mIEndBorderWidth; } + void SetIStartBorderWidth(BCPixelSize aWidth) { mIStartBorderWidth = aWidth; } + void SetIEndBorderWidth(BCPixelSize aWidth) { mIEndBorderWidth = aWidth; } /** * Gets inner border widths before collapsing with cell borders @@ -100,9 +104,9 @@ public: nscoord GetContinuousBCBorderWidth(nsMargin& aBorder); /** * Set full border widths before collapsing with cell borders - * @param aForSide - side to set; only valid for top, right, and bottom + * @param aForSide - side to set; only valid for bstart, iend, and bend */ - void SetContinuousBCBorderWidth(uint8_t aForSide, + void SetContinuousBCBorderWidth(mozilla::LogicalSide aForSide, BCPixelSize aPixelValue); #ifdef DEBUG void Dump(int32_t aIndent); @@ -301,11 +305,11 @@ protected: uint32_t mColIndex; // border width in pixels of the inner half of the border only - BCPixelSize mLeftBorderWidth; - BCPixelSize mRightBorderWidth; - BCPixelSize mTopContBorderWidth; - BCPixelSize mRightContBorderWidth; - BCPixelSize mBottomContBorderWidth; + BCPixelSize mIStartBorderWidth; + BCPixelSize mIEndBorderWidth; + BCPixelSize mBStartContBorderWidth; + BCPixelSize mIEndContBorderWidth; + BCPixelSize mBEndContBorderWidth; bool mHasSpecifiedCoord; }; @@ -320,37 +324,20 @@ inline void nsTableColFrame::SetColIndex (int32_t aColIndex) mColIndex = aColIndex; } -inline nscoord nsTableColFrame::GetLeftBorderWidth() -{ - return mLeftBorderWidth; -} - -inline void nsTableColFrame::SetLeftBorderWidth(BCPixelSize aWidth) -{ - mLeftBorderWidth = aWidth; -} - -inline nscoord nsTableColFrame::GetRightBorderWidth() -{ - return mRightBorderWidth; -} - -inline void nsTableColFrame::SetRightBorderWidth(BCPixelSize aWidth) -{ - mRightBorderWidth = aWidth; -} - inline nscoord nsTableColFrame::GetContinuousBCBorderWidth(nsMargin& aBorder) { int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, - mTopContBorderWidth); - aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, - mRightContBorderWidth); - aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, - mBottomContBorderWidth); - return BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mRightContBorderWidth); + mozilla::WritingMode wm = GetWritingMode(); + mozilla::LogicalMargin border(wm, aBorder); + border.BStart(wm) = BC_BORDER_END_HALF_COORD(aPixelsToTwips, + mBStartContBorderWidth); + border.IEnd(wm) = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mIEndContBorderWidth); + border.BEnd(wm) = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mBEndContBorderWidth); + aBorder = border.GetPhysicalMargin(wm); + return BC_BORDER_END_HALF_COORD(aPixelsToTwips, mIEndContBorderWidth); } #endif diff --git a/layout/tables/nsTableColGroupFrame.cpp b/layout/tables/nsTableColGroupFrame.cpp index 95e9f0652c..bc761c63bb 100644 --- a/layout/tables/nsTableColGroupFrame.cpp +++ b/layout/tables/nsTableColGroupFrame.cpp @@ -437,10 +437,10 @@ void nsTableColGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder) nsTableColFrame* col = GetTableFrame()-> GetColFrame(mStartColIndex + mColCount - 1); col->GetContinuousBCBorderWidth(aBorder); - aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, - mTopContBorderWidth); - aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, - mBottomContBorderWidth); + aBorder.top = BC_BORDER_END_HALF_COORD(aPixelsToTwips, + mTopContBorderWidth); + aBorder.bottom = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mBottomContBorderWidth); } /* ----- global methods ----- */ diff --git a/layout/tables/nsTableColGroupFrame.h b/layout/tables/nsTableColGroupFrame.h index ee49250c1e..e387ee9370 100644 --- a/layout/tables/nsTableColGroupFrame.h +++ b/layout/tables/nsTableColGroupFrame.h @@ -9,6 +9,7 @@ #include "nscore.h" #include "nsContainerFrame.h" #include "nsTableFrame.h" +#include "mozilla/WritingModes.h" class nsTableColFrame; @@ -37,6 +38,8 @@ public: { nsIFrame* parent = GetParent(); MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame); + MOZ_ASSERT(!parent->GetPrevInFlow(), + "Col group should always be in a first-in-flow table frame"); return static_cast(parent); } @@ -113,6 +116,9 @@ public: */ virtual nsIAtom* GetType() const override; + virtual mozilla::WritingMode GetWritingMode() const override + { return GetTableFrame()->GetWritingMode(); } + /** Add column frames to the table storages: colframe cache and cellmap * this doesn't change the mFrames of the colgroup frame. * @param aFirstColIndex - the index at which aFirstFrame should be inserted diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index cc85a53f49..0557f37ba4 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -7,6 +7,7 @@ #include "mozilla/Likely.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/IntegerRange.h" +#include "mozilla/WritingModes.h" #include "nsCOMPtr.h" #include "nsTableFrame.h" @@ -60,62 +61,46 @@ struct nsTableReflowState { // the real reflow state const nsHTMLReflowState& reflowState; - // The table's available size - nsSize availSize; + // The table's available size (in reflowState's writing mode) + LogicalSize availSize; - // Stationary x-offset - nscoord x; + // Stationary inline-offset + nscoord iCoord; - // Running y-offset - nscoord y; + // Running block-offset + nscoord bCoord; - nsTableReflowState(nsPresContext& aPresContext, - const nsHTMLReflowState& aReflowState, - nsTableFrame& aTableFrame, - nscoord aAvailWidth, - nscoord aAvailHeight) + nsTableReflowState(const nsHTMLReflowState& aReflowState, + const LogicalSize& aAvailSize) : reflowState(aReflowState) + , availSize(aAvailSize) { - Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight); - } + MOZ_ASSERT(reflowState.frame->GetType() == nsGkAtoms::tableFrame, + "nsTableReflowState should only be created for nsTableFrame"); + nsTableFrame* table = + static_cast(reflowState.frame->FirstInFlow()); + WritingMode wm = aReflowState.GetWritingMode(); + LogicalMargin borderPadding = table->GetChildAreaOffset(wm, &reflowState); - void Init(nsPresContext& aPresContext, - nsTableFrame& aTableFrame, - nscoord aAvailWidth, - nscoord aAvailHeight) - { - nsTableFrame* table = static_cast(aTableFrame.FirstInFlow()); - nsMargin borderPadding = table->GetChildAreaOffset(&reflowState); + iCoord = borderPadding.IStart(wm) + table->GetColSpacing(-1); + bCoord = borderPadding.BStart(wm); //cellspacing added during reflow - x = borderPadding.left + table->GetColSpacing(-1); - y = borderPadding.top; //cellspacing added during reflow - - availSize.width = aAvailWidth; - if (NS_UNCONSTRAINEDSIZE != availSize.width) { + // XXX do we actually need to check for unconstrained inline-size here? + if (NS_UNCONSTRAINEDSIZE != availSize.ISize(wm)) { int32_t colCount = table->GetColCount(); - availSize.width -= borderPadding.left + borderPadding.right - + table->GetColSpacing(-1) - + table->GetColSpacing(colCount); - availSize.width = std::max(0, availSize.width); + availSize.ISize(wm) -= borderPadding.IStartEnd(wm) + + table->GetColSpacing(-1) + + table->GetColSpacing(colCount); + availSize.ISize(wm) = std::max(0, availSize.ISize(wm)); } - availSize.height = aAvailHeight; - if (NS_UNCONSTRAINEDSIZE != availSize.height) { - availSize.height -= borderPadding.top + borderPadding.bottom - + table->GetRowSpacing(-1) - + table->GetRowSpacing(table->GetRowCount()); - availSize.height = std::max(0, availSize.height); + if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { + availSize.BSize(wm) -= borderPadding.BStartEnd(wm) + + table->GetRowSpacing(-1) + + table->GetRowSpacing(table->GetRowCount()); + availSize.BSize(wm) = std::max(0, availSize.BSize(wm)); } } - - nsTableReflowState(nsPresContext& aPresContext, - const nsHTMLReflowState& aReflowState, - nsTableFrame& aTableFrame) - : reflowState(aReflowState) - { - Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); - } - }; /******************************************************************************** @@ -174,6 +159,7 @@ nsTableFrame::Init(nsIContent* aContent, nsIFrame* aPrevInFlow) { NS_PRECONDITION(!mCellMap, "Init called twice"); + NS_PRECONDITION(!mTableLayoutStrategy, "Init called twice"); NS_PRECONDITION(!aPrevInFlow || aPrevInFlow->GetType() == nsGkAtoms::tableFrame, "prev-in-flow must be of same type"); @@ -186,23 +172,20 @@ nsTableFrame::Init(nsIContent* aContent, bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); SetBorderCollapse(borderCollapse); - // Create the cell map if this frame is the first-in-flow. if (!aPrevInFlow) { + // If we're the first-in-flow, we manage the cell map & layout strategy that + // get used by our continuation chain: mCellMap = new nsTableCellMap(*this, borderCollapse); - } - - if (aPrevInFlow) { - // set my width, because all frames in a table flow are the same width and - // code in nsTableOuterFrame depends on this being set - mRect.width = aPrevInFlow->GetSize().width; - } - else { - NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called"); - // create the strategy - if (IsAutoLayout()) + if (IsAutoLayout()) { mTableLayoutStrategy = new BasicTableLayoutStrategy(this); - else + } else { mTableLayoutStrategy = new FixedTableLayoutStrategy(this); + } + } else { + // Set my isize, because all frames in a table flow are the same isize and + // code in nsTableOuterFrame depends on this being set. + WritingMode wm = GetWritingMode(); + SetSize(LogicalSize(wm, aPrevInFlow->ISize(wm), BSize(wm))); } } @@ -320,8 +303,7 @@ nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame, static_cast(props.Get(PositionedTablePartArray())); // Remove the frame. - MOZ_ASSERT(positionedParts && - positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex, + MOZ_ASSERT(positionedParts && positionedParts->Contains(aFrame), "Asked to unregister a positioned table part that wasn't registered"); if (positionedParts) { positionedParts->RemoveElement(aFrame); @@ -370,9 +352,10 @@ nsTableFrame::SetInitialChildList(ChildListID aListID, } } -void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, - nsIContent* aContent, - nsIAtom* aAttribute) +void +nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, + nsIContent* aContent, + nsIAtom* aAttribute) { nsTableCellFrame *cellFrame = do_QueryFrame(aFrame); if (cellFrame) { @@ -402,7 +385,8 @@ void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, /* ****** CellMap methods ******* */ /* return the effective col count */ -int32_t nsTableFrame::GetEffectiveColCount() const +int32_t +nsTableFrame::GetEffectiveColCount() const { int32_t colCount = GetColCount(); if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) { @@ -421,7 +405,8 @@ int32_t nsTableFrame::GetEffectiveColCount() const return colCount; } -int32_t nsTableFrame::GetIndexOfLastRealCol() +int32_t +nsTableFrame::GetIndexOfLastRealCol() { int32_t numCols = mColFrames.Length(); if (numCols > 0) { @@ -451,8 +436,9 @@ nsTableFrame::GetColFrame(int32_t aColIndex) const } } -int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, - const nsTableCellFrame& aCell) const +int32_t +nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, + const nsTableCellFrame& aCell) const { nsTableCellMap* cellMap = GetCellMap(); NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated."); @@ -462,8 +448,9 @@ int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex); } -int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, - nsCellMap* aCellMap) +int32_t +nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, + nsCellMap* aCellMap) { nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); @@ -477,8 +464,9 @@ int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex); } -int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, - nsCellMap* aCellMap) const +int32_t +nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, + nsCellMap* aCellMap) const { nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); @@ -493,14 +481,16 @@ int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex); } -bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const +bool +nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const { nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); return tableCellMap->HasMoreThanOneCell(aRowIndex); } -void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, - int32_t aAdjustment) +void +nsTableFrame::AdjustRowIndices(int32_t aRowIndex, + int32_t aAdjustment) { // Iterate over the row groups and adjust the row indices of all rows // whose index is >= aRowIndex. @@ -513,7 +503,8 @@ void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, } -void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude) +void +nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude) { // Iterate over the row groups and adjust the row indices of all rows // omit the rowgroups that will be inserted later @@ -541,8 +532,9 @@ void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude } } } -void nsTableFrame::InsertColGroups(int32_t aStartColIndex, - const nsFrameList::Slice& aColGroups) +void +nsTableFrame::InsertColGroups(int32_t aStartColIndex, + const nsFrameList::Slice& aColGroups) { int32_t colIndex = aStartColIndex; nsFrameList::Enumerator colGroups(aColGroups); @@ -573,8 +565,9 @@ void nsTableFrame::InsertColGroups(int32_t aStartColIndex, } } -void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, - int32_t aColIndex) +void +nsTableFrame::InsertCol(nsTableColFrame& aColFrame, + int32_t aColIndex) { mColFrames.InsertElementAt(aColIndex, &aColFrame); nsTableColType insertedColType = aColFrame.GetColType(); @@ -617,10 +610,11 @@ void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, } } -void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, - int32_t aColIndex, - bool aRemoveFromCache, - bool aRemoveFromCellMap) +void +nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, + int32_t aColIndex, + bool aRemoveFromCache, + bool aRemoveFromCellMap) { if (aRemoveFromCache) { mColFrames.RemoveElementAt(aColIndex); @@ -810,9 +804,10 @@ nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame, } } -void nsTableFrame::InsertCells(nsTArray& aCellFrames, - int32_t aRowIndex, - int32_t aColIndexBefore) +void +nsTableFrame::InsertCells(nsTArray& aCellFrames, + int32_t aRowIndex, + int32_t aColIndexBefore) { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { @@ -851,8 +846,9 @@ nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames) return (aNumFrames - numColsRemoved); } -void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, - int32_t aRowIndex) +void +nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, + int32_t aRowIndex) { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { @@ -884,9 +880,10 @@ nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame) } // this cannot extend beyond a single row group -void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame, - int32_t aRowIndex, - nsTArray& aRowFrames) +void +nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame, + int32_t aRowIndex, + nsTArray& aRowFrames) { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { @@ -937,7 +934,8 @@ nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame, } // this cannot extend beyond a single row group -void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame, +void +nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame, int32_t aNumRowsToRemove, bool aConsiderSpans) { @@ -1173,7 +1171,8 @@ nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, nsDisplayTableItemGeometry::UpdateDrawResult(this, result); } -static int32_t GetTablePartRank(nsDisplayItem* aItem) +static int32_t +GetTablePartRank(nsDisplayItem* aItem) { nsIAtom* type = aItem->Frame()->GetType(); if (type == nsGkAtoms::tableFrame) @@ -1352,7 +1351,8 @@ nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const !IsBorderCollapse()) return nsMargin(0,0,0,0); - return GetOuterBCBorder(); + WritingMode wm = GetWritingMode(); + return GetOuterBCBorder(wm).GetPhysicalMargin(wm); } // XXX We don't put the borders and backgrounds in tree order like we should. @@ -1420,58 +1420,73 @@ nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const } void -nsTableFrame::SetColumnDimensions(nscoord aHeight, - const nsMargin& aBorderPadding) +nsTableFrame::SetColumnDimensions(nscoord aBSize, WritingMode aWM, + const LogicalMargin& aBorderPadding, + nscoord aContainerWidth) { - nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom + - GetRowSpacing(-1) + - GetRowSpacing(GetRowCount()); - + const nscoord colBSize = aBSize - (aBorderPadding.BStartEnd(aWM) + + GetRowSpacing(-1) + GetRowSpacing(GetRowCount())); + int32_t colIdx = 0; + LogicalPoint colGroupOrigin(aWM, + aBorderPadding.IStart(aWM) + GetColSpacing(-1), + aBorderPadding.BStart(aWM) + GetRowSpacing(-1)); nsTableIterator iter(mColGroups); - nsIFrame* colGroupFrame = iter.First(); - bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; - int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1); - nscoord cellSpacingX = GetColSpacing(colX); - int32_t tableColIncr = tableIsLTR ? 1 : -1; - nsPoint colGroupOrigin(aBorderPadding.left + GetColSpacing(-1), - aBorderPadding.top + GetRowSpacing(-1)); - while (colGroupFrame) { + nsTableFrame* fif = static_cast(FirstInFlow()); + for (nsIFrame* colGroupFrame = iter.First(); colGroupFrame; + colGroupFrame = iter.Next()) { MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame); - nscoord colGroupWidth = 0; + // first we need to figure out the size of the colgroup + int32_t groupFirstCol = colIdx; + nscoord colGroupISize = 0; + nscoord cellSpacingI = 0; nsTableIterator iterCol(*colGroupFrame); - nsIFrame* colFrame = iterCol.First(); - nsPoint colOrigin(0,0); - while (colFrame) { + for (nsIFrame* colFrame = iterCol.First(); colFrame; + colFrame = iterCol.Next()) { if (NS_STYLE_DISPLAY_TABLE_COLUMN == colFrame->StyleDisplay()->mDisplay) { - NS_ASSERTION(colX < GetColCount(), "invalid number of columns"); - nscoord colWidth = GetColumnISize(colX); - nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight); - colFrame->SetRect(colRect); - cellSpacingX = GetColSpacing(colX); - colOrigin.x += colWidth + cellSpacingX; - colGroupWidth += colWidth + cellSpacingX; - colX += tableColIncr; + NS_ASSERTION(colIdx < GetColCount(), "invalid number of columns"); + cellSpacingI = GetColSpacing(colIdx); + colGroupISize += fif->GetColumnISizeFromFirstInFlow(colIdx) + + cellSpacingI; + ++colIdx; } - colFrame = iterCol.Next(); } - if (colGroupWidth) { - colGroupWidth -= cellSpacingX; + if (colGroupISize) { + colGroupISize -= cellSpacingI; } - nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight); - colGroupFrame->SetRect(colGroupRect); - colGroupFrame = iter.Next(); - colGroupOrigin.x += colGroupWidth + cellSpacingX; + LogicalRect colGroupRect(aWM, colGroupOrigin.I(aWM), colGroupOrigin.B(aWM), + colGroupISize, colBSize); + colGroupFrame->SetRect(aWM, colGroupRect, aContainerWidth); + nscoord colGroupWidth = colGroupFrame->GetSize().width; + + // then we can place the columns correctly within the group + colIdx = groupFirstCol; + LogicalPoint colOrigin(aWM); + for (nsIFrame* colFrame = iterCol.First(); colFrame; + colFrame = iterCol.Next()) { + if (NS_STYLE_DISPLAY_TABLE_COLUMN == + colFrame->StyleDisplay()->mDisplay) { + nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx); + LogicalRect colRect(aWM, colOrigin.I(aWM), colOrigin.B(aWM), + colISize, colBSize); + colFrame->SetRect(aWM, colRect, colGroupWidth); + cellSpacingI = GetColSpacing(colIdx); + colOrigin.I(aWM) += colISize + cellSpacingI; + ++colIdx; + } + } + + colGroupOrigin.I(aWM) += colGroupISize + cellSpacingI; } } // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages. // XXX this could be made more general to handle row modifications that change the -// table height, but first we need to scrutinize every Invalidate +// table bsize, but first we need to scrutinize every Invalidate void -nsTableFrame::ProcessRowInserted(nscoord aNewHeight) +nsTableFrame::ProcessRowInserted(nscoord aNewBSize) { SetRowInserted(false); // reset the bit that got us here nsTableFrame::RowGroupArray rowGroups; @@ -1554,8 +1569,9 @@ nsTableFrame::IntrinsicISizeOffsets(nsRenderingContext* aRenderingContext) result.hPadding = 0; result.hPctPadding = 0; - nsMargin outerBC = GetIncludedOuterBCBorder(); - result.hBorder = outerBC.LeftRight(); + WritingMode wm = GetWritingMode(); + LogicalMargin outerBC = GetIncludedOuterBCBorder(wm); + result.hBorder = outerBC.IStartEnd(wm); } return result; @@ -1598,31 +1614,31 @@ nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext, } nscoord -nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, - nscoord aWidthInCB) +nsTableFrame::TableShrinkISizeToFit(nsRenderingContext *aRenderingContext, + nscoord aISizeInCB) { // If we're a container for font size inflation, then shrink // wrapping inside of us should not apply font size inflation. AutoMaybeDisableFontInflation an(this); nscoord result; - nscoord minWidth = GetMinISize(aRenderingContext); - if (minWidth > aWidthInCB) { - result = minWidth; + nscoord minISize = GetMinISize(aRenderingContext); + if (minISize > aISizeInCB) { + result = minISize; } else { - // Tables shrink width to fit with a slightly different algorithm - // from the one they use for their intrinsic widths (the difference - // relates to handling of percentage widths on columns). So this + // Tables shrink inline-size to fit with a slightly different algorithm + // from the one they use for their intrinsic isize (the difference + // relates to handling of percentage isizes on columns). So this // function differs from nsFrame::ShrinkWidthToFit by only the // following line. // Since we've already called GetMinISize, we don't need to do any // of the other stuff GetPrefISize does. - nscoord prefWidth = + nscoord prefISize = LayoutStrategy()->GetPrefISize(aRenderingContext, true); - if (prefWidth > aWidthInCB) { - result = aWidthInCB; + if (prefISize > aISizeInCB) { + result = aISizeInCB; } else { - result = prefWidth; + result = prefISize; } } return result; @@ -1642,61 +1658,64 @@ nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, // Tables always shrink-wrap. nscoord cbBased = aAvailableISize - aMargin.ISize(aWM) - aBorder.ISize(aWM) - aPadding.ISize(aWM); - return LogicalSize(aWM, TableShrinkWidthToFit(aRenderingContext, cbBased), + return LogicalSize(aWM, TableShrinkISizeToFit(aRenderingContext, cbBased), NS_UNCONSTRAINEDSIZE); } // Return true if aParentReflowState.frame or any of its ancestors within -// the containing table have non-auto height. (e.g. pct or fixed height) +// the containing table have non-auto bsize. (e.g. pct or fixed bsize) bool -nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState) +nsTableFrame::AncestorsHaveStyleBSize(const nsHTMLReflowState& aParentReflowState) { + WritingMode wm = aParentReflowState.GetWritingMode(); for (const nsHTMLReflowState* rs = &aParentReflowState; rs && rs->frame; rs = rs->parentReflowState) { nsIAtom* frameType = rs->frame->GetType(); if (IS_TABLE_CELL(frameType) || (nsGkAtoms::tableRowFrame == frameType) || (nsGkAtoms::tableRowGroupFrame == frameType)) { - const nsStyleCoord &height = rs->mStylePosition->mHeight; + const nsStyleCoord &bsize = rs->mStylePosition->BSize(wm); // calc() with percentages treated like 'auto' on internal table elements - if (height.GetUnit() != eStyleUnit_Auto && - (!height.IsCalcUnit() || !height.HasPercent())) { + if (bsize.GetUnit() != eStyleUnit_Auto && + (!bsize.IsCalcUnit() || !bsize.HasPercent())) { return true; } } else if (nsGkAtoms::tableFrame == frameType) { // we reached the containing table, so always return - return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto; + return rs->mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto; } } return false; } -// See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow +// See if a special block-size reflow needs to occur and if so, +// call RequestSpecialBSizeReflow void -nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) +nsTableFrame::CheckRequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowState) { NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) || aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame || aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame || aReflowState.frame->GetType() == nsGkAtoms::tableFrame, "unexpected frame type"); + WritingMode wm = aReflowState.GetWritingMode(); if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow - (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height - 0 == aReflowState.ComputedHeight()) && - eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height - nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) { - nsTableFrame::RequestSpecialHeightReflow(aReflowState); + (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedBSize() || // no computed bsize + 0 == aReflowState.ComputedBSize()) && + eStyleUnit_Percent == aReflowState.mStylePosition->BSize(wm).GetUnit() && // pct bsize + nsTableFrame::AncestorsHaveStyleBSize(*aReflowState.parentReflowState)) { + nsTableFrame::RequestSpecialBSizeReflow(aReflowState); } } // Notify the frame and its ancestors (up to the containing table) that a special -// height reflow will occur. During a special height reflow, a table, row group, +// bsize reflow will occur. During a special bsize reflow, a table, row group, // row, or cell returns the last size it was reflowed at. However, the table may -// change the height of row groups, rows, cells in DistributeHeightToRows after. -// And the row group can change the height of rows, cells in CalculateRowHeights. +// change the bsize of row groups, rows, cells in DistributeBSizeToRows after. +// And the row group can change the bsize of rows, cells in CalculateRowBSizes. void -nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) +nsTableFrame::RequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowState) { // notify the frame and its ancestors of the special reflow, stopping at the containing table for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) { @@ -1707,10 +1726,10 @@ nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) nsGkAtoms::tableFrame == frameType, "unexpected frame type"); - rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); + rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); if (nsGkAtoms::tableFrame == frameType) { NS_ASSERTION(rs != &aReflowState, - "should not request special height reflow for table"); + "should not request special bsize reflow for table"); // always stop when we reach a table break; } @@ -1718,41 +1737,43 @@ nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) } /****************************************************************************************** - * Before reflow, intrinsic width calculation is done using GetMinISize + * Before reflow, intrinsic inline-size calculation is done using GetMinISize * and GetPrefISize. This used to be known as pass 1 reflow. * - * After the intrinsic width calculation, the table determines the - * column widths using BalanceColumnWidths() and - * then reflows each child again with a constrained avail width. This reflow is referred to + * After the intrinsic isize calculation, the table determines the + * column widths using BalanceColumnISizes() and + * then reflows each child again with a constrained avail isize. This reflow is referred to * as the pass 2 reflow. * - * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow - * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed - * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables - * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is + * A special bsize reflow (pass 3 reflow) can occur during an initial or resize reflow + * if (a) a row group, row, cell, or a frame inside a cell has a percent bsize but no computed + * bsize or (b) in paginated mode, a table has a bsize. (a) supports percent nested tables + * contained inside cells whose bsizes aren't known until after the pass 2 reflow. (b) is * necessary because the table cannot split until after the pass 2 reflow. The mechanics of - * the special height reflow (variety a) are as follows: + * the special bsize reflow (variety a) are as follows: * * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow() - * to indicate that it should get the reflow. It does this when it has a percent height but - * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls - * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until + * to indicate that it should get the reflow. It does this when it has a percent bsize but + * no computed bsize by calling CheckRequestSpecialBSizeReflow(). This method calls + * RequestSpecialBSizeReflow() which calls SetNeedSpecialReflow() on its ancestors until * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For - * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight() - * is called (the cell is the reflow state's mPercentHeightObserver in this case). - * NotifyPercentHeight() calls RequestSpecialHeightReflow(). + * percent bsize frames inside cells, during DidReflow(), the cell's NotifyPercentBSize() + * is called (the cell is the reflow state's mPercentBSizeObserver in this case). + * NotifyPercentBSize() calls RequestSpecialBSizeReflow(). * + * XXX (jfkthame) This comment appears to be out of date; it refers to methods/flags + * that are no longer present in the code. * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it - * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow + * will do the special bsize reflow, setting the reflow state's mFlags.mSpecialBSizeReflow * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow() - * returns true because in that case another special height reflow will be coming along with the + * returns true because in that case another special bsize reflow will be coming along with the * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then - * appropriate heights will not be known. + * appropriate bsizes will not be known. * - * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2 - * reflow, they return their last desired sizes during the special height reflow. The reflow only - * permits percent height frames inside the cells to resize based on the cells height and that height + * 3) Since the bsizes of the table, row groups, rows, and cells was determined during the pass 2 + * reflow, they return their last desired sizes during the special bsize reflow. The reflow only + * permits percent bsize frames inside the cells to resize based on the cells bsize and that bsize * was determined during the pass 2 reflow. * * So, in the case of deeply nested tables, all of the tables that were told to initiate a special @@ -1763,16 +1784,16 @@ nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) * The type of special reflow that occurs during printing (variety b) follows the same mechanism except * that all frames will receive the reflow even if they don't really need them. * - * Open issues with the special height reflow: + * Open issues with the special bsize reflow: * - * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are + * 1) At some point there should be 2 kinds of special bsize reflows because (a) and (b) above are * really quite different. This would avoid unnecessary reflows during printing. - * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245). - * However, this can also occur if a cell has a fixed height and there is no special height reflow. + * 2) When a cell contains frames whose percent bsizes > 100%, there is data loss (see bug 115245). + * However, this can also occur if a cell has a fixed bsize and there is no special bsize reflow. * - * XXXldb Special height reflow should really be its own method, not + * XXXldb Special bsize reflow should really be its own method, not * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on - * the contents of the cells to do the necessary vertical resizing. + * the contents of the cells to do the necessary block-axis resizing. * ******************************************************************************************/ @@ -1787,10 +1808,11 @@ nsTableFrame::Reflow(nsPresContext* aPresContext, DO_GLOBAL_REFLOW_COUNT("nsTableFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); bool isPaginated = aPresContext->IsPaginated(); + WritingMode wm = aReflowState.GetWritingMode(); aStatus = NS_FRAME_COMPLETE; if (!GetPrevInFlow() && !mTableLayoutStrategy) { - NS_ASSERTION(false, "strategy should have been created in Init"); + NS_ERROR("strategy should have been created in Init"); return; } @@ -1799,116 +1821,122 @@ nsTableFrame::Reflow(nsPresContext* aPresContext, CalcBCBorders(); } - aDesiredSize.Width() = aReflowState.AvailableWidth(); + aDesiredSize.ISize(wm) = aReflowState.AvailableISize(); // Check for an overflow list, and append any row group frames being pushed MoveOverflowToChildList(); - bool haveDesiredHeight = false; + bool haveDesiredBSize = false; SetHaveReflowedColGroups(false); // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a // constrained initial reflow and other reflows which require either a strategy init or balance. // This isn't done during an unconstrained reflow, because it will occur later when the parent - // reflows with a constrained width. + // reflows with a constrained isize. if (NS_SUBTREE_DIRTY(this) || aReflowState.ShouldReflowAllKids() || IsGeometryDirty() || - aReflowState.IsVResize()) { + aReflowState.IsBResize()) { - if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE || - // Also check mVResize, to handle the first Reflow preceding a - // special height Reflow, when we've already had a special height - // Reflow (where mComputedHeight would not be + if (aReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE || + // Also check IsBResize(), to handle the first Reflow preceding a + // special bsize Reflow, when we've already had a special bsize + // Reflow (where ComputedBSize() would not be // NS_UNCONSTRAINEDSIZE, but without a style change in between). - aReflowState.IsVResize()) { - // XXX Eventually, we should modify DistributeHeightToRows to use - // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height. + aReflowState.IsBResize()) { + // XXX Eventually, we should modify DistributeBSizeToRows to use + // nsTableRowFrame::GetBSize instead of nsIFrame::BSize(). // That way, it will make its calculations based on internal table - // frame heights as they are before they ever had any extra height + // frame bsizes as they are before they ever had any extra bsize // distributed to them. In the meantime, this reflows all the // internal table frames, which restores them to their state before - // DistributeHeightToRows was called. + // DistributeBSizeToRows was called. SetGeometryDirty(); } bool needToInitiateSpecialReflow = - !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT); - // see if an extra reflow will be necessary in pagination mode when there is a specified table height - if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); - if ((tableSpecifiedHeight > 0) && - (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { + !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE); + // see if an extra reflow will be necessary in pagination mode + // when there is a specified table bsize + if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize())) { + nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowState); + if ((tableSpecifiedBSize > 0) && + (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE)) { needToInitiateSpecialReflow = true; } } nsIFrame* lastChildReflowed = nullptr; - NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow, - "Shouldn't be in special height reflow here!"); + NS_ASSERTION(!aReflowState.mFlags.mSpecialBSizeReflow, + "Shouldn't be in special bsize reflow here!"); - // do the pass 2 reflow unless this is a special height reflow and we will be - // initiating a special height reflow + // do the pass 2 reflow unless this is a special bsize reflow and we will be + // initiating a special bsize reflow // XXXldb I changed this. Should I change it back? - // if we need to initiate a special height reflow, then don't constrain the - // height of the reflow before that - nscoord availHeight = needToInitiateSpecialReflow - ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight(); + // if we need to initiate a special bsize reflow, then don't constrain the + // bsize of the reflow before that + nscoord availBSize = needToInitiateSpecialReflow + ? NS_UNCONSTRAINEDSIZE + : aReflowState.AvailableBSize(); - ReflowTable(aDesiredSize, aReflowState, availHeight, + ReflowTable(aDesiredSize, aReflowState, availBSize, lastChildReflowed, aStatus); - // reevaluate special height reflow conditions - if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) + // reevaluate special bsize reflow conditions + if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) { needToInitiateSpecialReflow = true; + } // XXXldb Are all these conditions correct? if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) { - // XXXldb Do we need to set the mVResize flag on any reflow states? + // XXXldb Do we need to set the IsBResize flag on any reflow states? nsHTMLReflowState &mutable_rs = const_cast(aReflowState); - // distribute extra vertical space to rows - CalcDesiredHeight(aReflowState, aDesiredSize); - mutable_rs.mFlags.mSpecialHeightReflow = true; + // distribute extra block-direction space to rows + CalcDesiredBSize(aReflowState, aDesiredSize); + mutable_rs.mFlags.mSpecialBSizeReflow = true; - ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(), + ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableBSize(), lastChildReflowed, aStatus); if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) { - // if there is an incomplete child, then set the desired height to include it but not the next one - nsMargin borderPadding = GetChildAreaOffset(&aReflowState); - aDesiredSize.Height() = borderPadding.bottom + GetRowSpacing(GetRowCount()) + - lastChildReflowed->GetNormalRect().YMost(); + // if there is an incomplete child, then set the desired bsize + // to include it but not the next one + LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState); + aDesiredSize.BSize(wm) = + borderPadding.BEnd(wm) + GetRowSpacing(GetRowCount()) + + lastChildReflowed->GetNormalRect().YMost(); // XXX YMost should be B-flavored } - haveDesiredHeight = true; + haveDesiredBSize = true; - mutable_rs.mFlags.mSpecialHeightReflow = false; + mutable_rs.mFlags.mSpecialBSizeReflow = false; } } else { // Calculate the overflow area contribution from our children. - for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { + for (nsIFrame* kid : mFrames) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid); } } - aDesiredSize.Width() = aReflowState.ComputedWidth() + - aReflowState.ComputedPhysicalBorderPadding().LeftRight(); - if (!haveDesiredHeight) { - CalcDesiredHeight(aReflowState, aDesiredSize); + aDesiredSize.ISize(wm) = aReflowState.ComputedISize() + + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm); + if (!haveDesiredBSize) { + CalcDesiredBSize(aReflowState, aDesiredSize); } if (IsRowInserted()) { - ProcessRowInserted(aDesiredSize.Height()); + ProcessRowInserted(aDesiredSize.BSize(wm)); } - nsMargin borderPadding = GetChildAreaOffset(&aReflowState); - SetColumnDimensions(aDesiredSize.Height(), borderPadding); + LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState); + SetColumnDimensions(aDesiredSize.BSize(wm), wm, borderPadding, + aDesiredSize.Width()); if (NeedToCollapse() && - (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) { - AdjustForCollapsingRowsCols(aDesiredSize, borderPadding); + (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableISize())) { + AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding); } // If there are any relatively-positioned table parts, we need to reflow their @@ -1920,8 +1948,8 @@ nsTableFrame::Reflow(nsPresContext* aPresContext, if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) { // collapsed border may leak out - nsMargin bcMargin = GetExcludedOuterBCBorder(); - tableRect.Inflate(bcMargin); + LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm); + tableRect.Inflate(bcMargin.GetPhysicalMargin(wm)); } aDesiredSize.mOverflowAreas.UnionAllWith(tableRect); @@ -1972,7 +2000,7 @@ nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext, nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; // Reflow absolutely-positioned descendants of the positioned part. - // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and + // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the bsize and // ignoring any change to the reflow status aren't correct. We'll never // paginate absolutely positioned frames. nsFrame* positionedFrame = static_cast(positionedPart); @@ -2009,8 +2037,9 @@ nsTableFrame::UpdateOverflow() // As above in Reflow, make sure the table overflow area includes the table // rect, and check for collapsed borders leaking out. if (!ShouldApplyOverflowClipping(this, StyleDisplay())) { - nsMargin bcMargin = GetExcludedOuterBCBorder(); - bounds.Inflate(bcMargin); + WritingMode wm = GetWritingMode(); + LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm); + bounds.Inflate(bcMargin.GetPhysicalMargin(wm)); } nsOverflowAreas overflowAreas(bounds, bounds); @@ -2022,7 +2051,7 @@ nsTableFrame::UpdateOverflow() void nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nscoord aAvailHeight, + nscoord aAvailBSize, nsIFrame*& aLastChildReflowed, nsReflowStatus& aStatus) { @@ -2031,12 +2060,14 @@ nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize, if (!GetPrevInFlow()) { mTableLayoutStrategy->ComputeColumnISizes(aReflowState); } - // Constrain our reflow width to the computed table width (of the 1st in flow). - // and our reflow height to our avail height minus border, padding, cellspacing - aDesiredSize.Width() = aReflowState.ComputedWidth() + - aReflowState.ComputedPhysicalBorderPadding().LeftRight(); - nsTableReflowState reflowState(*PresContext(), aReflowState, *this, - aDesiredSize.Width(), aAvailHeight); + // Constrain our reflow isize to the computed table isize (of the 1st in flow). + // and our reflow bsize to our avail bsize minus border, padding, cellspacing + WritingMode wm = aReflowState.GetWritingMode(); + aDesiredSize.ISize(wm) = aReflowState.ComputedISize() + + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm); + nsTableReflowState reflowState(aReflowState, + LogicalSize(wm, aDesiredSize.ISize(wm), + aAvailBSize)); ReflowChildren(reflowState, aStatus, aLastChildReflowed, aDesiredSize.mOverflowAreas); @@ -2049,7 +2080,7 @@ nsTableFrame::GetFirstBodyRowGroupFrame() nsIFrame* headerFrame = nullptr; nsIFrame* footerFrame = nullptr; - for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) { + for (nsIFrame* kidFrame : mFrames) { const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay(); // We expect the header and footer row group frames to be first, and we only @@ -2073,9 +2104,6 @@ nsTableFrame::GetFirstBodyRowGroupFrame() } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) { return kidFrame; } - - // Get the next child - kidFrame = kidFrame->GetNextSibling(); } return nullptr; @@ -2128,9 +2156,10 @@ nsTableFrame::PushChildren(const RowGroupArray& aRowGroups, // reflow so that it has no effect on the calculations of reflow. void nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, - nsMargin aBorderPadding) + const WritingMode aWM, + const LogicalMargin& aBorderPadding) { - nscoord yTotalOffset = 0; // total offset among all rows in all row groups + nscoord bTotalOffset = 0; // total offset among all rows in all row groups // reset the bit, it will be set again if row/rowgroup or col/colgroup are // collapsed @@ -2142,20 +2171,21 @@ nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, OrderRowGroups(rowGroups); nsTableFrame* firstInFlow = static_cast(FirstInFlow()); - nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding); - nscoord rgWidth = width - GetColSpacing(-1) - + nscoord iSize = firstInFlow->GetCollapsedISize(aWM, aBorderPadding); + nscoord rgISize = iSize - GetColSpacing(-1) - GetColSpacing(GetColCount()); nsOverflowAreas overflow; // Walk the list of children for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { nsTableRowGroupFrame* rgFrame = rowGroups[childX]; NS_ASSERTION(rgFrame, "Must have row group frame here"); - yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth); + bTotalOffset += rgFrame->CollapseRowGroupIfNecessary(bTotalOffset, rgISize, + aWM); ConsiderChildOverflow(overflow, rgFrame); } - aDesiredSize.Height() -= yTotalOffset; - aDesiredSize.Width() = width; + aDesiredSize.BSize(aWM) -= bTotalOffset; + aDesiredSize.ISize(aWM) = iSize; overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height())); FinishAndStoreOverflow(overflow, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); @@ -2163,13 +2193,14 @@ nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, nscoord -nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) +nsTableFrame::GetCollapsedISize(const WritingMode aWM, + const LogicalMargin& aBorderPadding) { - NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow"); - nscoord width = GetColSpacing(GetColCount()); - width += aBorderPadding.left + aBorderPadding.right; - for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame; - groupFrame = groupFrame->GetNextSibling()) { + NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedISize called on next in flow"); + nscoord iSize = GetColSpacing(GetColCount()); + iSize += aBorderPadding.IStartEnd(aWM); + nsTableFrame* fif = static_cast(FirstInFlow()); + for (nsIFrame* groupFrame : mColGroups) { const nsStyleVisibility* groupVis = groupFrame->StyleVisibility(); bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame; @@ -2180,11 +2211,11 @@ nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) { const nsStyleVisibility* colVis = colFrame->StyleVisibility(); bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); - int32_t colWidth = GetColumnISize(colX); + nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colX); if (!collapseGroup && !collapseCol) { - width += colWidth; + iSize += colISize; if (ColumnHasCellSpacingBefore(colX)) - width += GetColSpacing(colX-1); + iSize += GetColSpacing(colX-1); } else { SetNeedToCollapse(true); @@ -2192,7 +2223,7 @@ nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) } } } - return width; + return iSize; } /* virtual */ void @@ -2248,11 +2279,17 @@ nsTableFrame::AppendFrames(ChildListID aListID, const nsStyleDisplay* display = f->StyleDisplay(); if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { + if (MOZ_UNLIKELY(GetPrevInFlow())) { + nsFrameList colgroupFrame(f, f); + auto firstInFlow = static_cast(FirstInFlow()); + firstInFlow->AppendFrames(aListID, colgroupFrame); + continue; + } nsTableColGroupFrame* lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this); int32_t startColIndex = (lastColGroup) ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0; - mColGroups.InsertFrame(nullptr, lastColGroup, f); + mColGroups.InsertFrame(this, lastColGroup, f); // Insert the colgroup and its cols into the table InsertColGroups(startColIndex, nsFrameList::Slice(mColGroups, f, f->GetNextSibling())); @@ -2346,16 +2383,22 @@ nsTableFrame::HomogenousInsertFrames(ChildListID aListID, { // See what kind of frame we have const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); + bool isColGroup = NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay; #ifdef DEBUG // Verify that either all siblings have display:table-column-group, or they // all have display values different from table-column-group. - for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { - const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay(); - MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) == - (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP), + for (nsIFrame* frame : aFrameList) { + auto nextDisplay = frame->StyleDisplay()->mDisplay; + MOZ_ASSERT(isColGroup == + (nextDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP), "heterogenous childlist"); } #endif + if (MOZ_UNLIKELY(isColGroup && GetPrevInFlow())) { + auto firstInFlow = static_cast(FirstInFlow()); + firstInFlow->AppendFrames(aListID, aFrameList); + return; + } if (aPrevFrame) { const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay(); // Make sure they belong on the same frame list @@ -2376,8 +2419,6 @@ nsTableFrame::HomogenousInsertFrames(ChildListID aListID, if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823. int32_t newIndex = container->IndexOf(content); nsIFrame* kidFrame; - bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == - display->mDisplay); nsTableColGroupFrame* lastColGroup; if (isColGroup) { kidFrame = mColGroups.FirstChild(); @@ -2414,7 +2455,7 @@ nsTableFrame::HomogenousInsertFrames(ChildListID aListID, NS_ASSERTION(aListID == kColGroupList, "unexpected child list"); // Insert the column group frames const nsFrameList::Slice& newColgroups = - mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList); + mColGroups.InsertFrames(this, aPrevFrame, aFrameList); // find the starting col index for the first new col group int32_t startColIndex = 0; if (aPrevFrame) { @@ -2546,7 +2587,8 @@ nsTableFrame::GetUsedBorder() const if (!IsBorderCollapse()) return nsContainerFrame::GetUsedBorder(); - return GetIncludedOuterBCBorder(); + WritingMode wm = GetWritingMode(); + return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm); } /* virtual */ nsMargin @@ -2591,8 +2633,8 @@ DivideBCBorderSize(BCPixelSize aPixelSize, aLargeHalf = aPixelSize - aSmallHalf; } -nsMargin -nsTableFrame::GetOuterBCBorder() const +LogicalMargin +nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const { if (NeedToCalcBCBorders()) const_cast(this)->CalcBCBorders(); @@ -2601,64 +2643,63 @@ nsTableFrame::GetOuterBCBorder() const int32_t d2a = PresContext()->AppUnitsPerDevPixel(); BCPropertyData* propData = GetBCProperty(); if (propData) { - border.top = BC_BORDER_TOP_HALF_COORD(d2a, propData->mTopBorderWidth); - border.right = BC_BORDER_RIGHT_HALF_COORD(d2a, propData->mRightBorderWidth); - border.bottom = BC_BORDER_BOTTOM_HALF_COORD(d2a, propData->mBottomBorderWidth); - border.left = BC_BORDER_LEFT_HALF_COORD(d2a, propData->mLeftBorderWidth); + return LogicalMargin( + aWM, + BC_BORDER_START_HALF_COORD(d2a, propData->mTopBorderWidth), + BC_BORDER_END_HALF_COORD(d2a, propData->mRightBorderWidth), + BC_BORDER_END_HALF_COORD(d2a, propData->mBottomBorderWidth), + BC_BORDER_START_HALF_COORD(d2a, propData->mLeftBorderWidth)); } - return border; + return LogicalMargin(GetWritingMode()); } -nsMargin -nsTableFrame::GetIncludedOuterBCBorder() const +LogicalMargin +nsTableFrame::GetIncludedOuterBCBorder(const WritingMode aWM) const { if (NeedToCalcBCBorders()) const_cast(this)->CalcBCBorders(); - nsMargin border(0, 0, 0, 0); - int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); + int32_t d2a = PresContext()->AppUnitsPerDevPixel(); BCPropertyData* propData = GetBCProperty(); if (propData) { - border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); - border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth); - border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); - border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth); + return LogicalMargin( + aWM, + BC_BORDER_START_HALF_COORD(d2a, propData->mTopBorderWidth), + BC_BORDER_END_HALF_COORD(d2a, propData->mRightCellBorderWidth), + BC_BORDER_END_HALF_COORD(d2a, propData->mBottomBorderWidth), + BC_BORDER_START_HALF_COORD(d2a, propData->mLeftCellBorderWidth)); } - return border; + return LogicalMargin(GetWritingMode()); } -nsMargin -nsTableFrame::GetExcludedOuterBCBorder() const +LogicalMargin +nsTableFrame::GetExcludedOuterBCBorder(const WritingMode aWM) const { - return GetOuterBCBorder() - GetIncludedOuterBCBorder(); + return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM); } -static -void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState, - nsStyleContext& aStyleContext, - nsMargin& aBorderPadding) +static LogicalMargin +GetSeparateModelBorderPadding(const WritingMode aWM, + const nsHTMLReflowState* aReflowState, + nsStyleContext* aStyleContext) { // XXXbz Either we _do_ have a reflow state and then we can use its // mComputedBorderPadding or we don't and then we get the padding // wrong! - const nsStyleBorder* border = aStyleContext.StyleBorder(); - aBorderPadding = border->GetComputedBorder(); + const nsStyleBorder* border = aStyleContext->StyleBorder(); + LogicalMargin borderPadding(aWM, border->GetComputedBorder()); if (aReflowState) { - aBorderPadding += aReflowState->ComputedPhysicalPadding(); + borderPadding += aReflowState->ComputedLogicalPadding(); } + return borderPadding; } -nsMargin -nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const +LogicalMargin +nsTableFrame::GetChildAreaOffset(const WritingMode aWM, + const nsHTMLReflowState* aReflowState) const { - nsMargin offset(0,0,0,0); - if (IsBorderCollapse()) { - offset = GetIncludedOuterBCBorder(); - } - else { - GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset); - } - return offset; + return IsBorderCollapse() ? GetIncludedOuterBCBorder(aWM) : + GetSeparateModelBorderPadding(aWM, aReflowState, mStyleContext); } void @@ -2671,13 +2712,16 @@ nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) if (IsBorderCollapse()) { nsTableRowGroupFrame* rgFrame = static_cast(aReflowState.frame); - pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder); + WritingMode wm = GetWritingMode(); + LogicalMargin border = rgFrame->GetBCBorderWidth(wm); + collapseBorder = border.GetPhysicalMargin(wm); + pCollapseBorder = &collapseBorder; } - aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding); + aReflowState.Init(presContext, nullptr, pCollapseBorder, &padding); NS_ASSERTION(!mBits.mResizedColumns || - !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow, - "should not resize columns on special height reflow"); + !aReflowState.parentReflowState->mFlags.mSpecialBSizeReflow, + "should not resize columns on special bsize reflow"); if (mBits.mResizedColumns) { aReflowState.SetHResize(true); } @@ -2685,13 +2729,15 @@ nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) // Position and size aKidFrame and update our reflow state. The origin of // aKidRect is relative to the upper-left origin of our frame -void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, - nsIFrame* aKidFrame, - nsPoint aKidPosition, - nsHTMLReflowMetrics& aKidDesiredSize, - const nsRect& aOriginalKidRect, - const nsRect& aOriginalKidVisualOverflow) +void +nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, + nsIFrame* aKidFrame, + nsPoint aKidPosition, + nsHTMLReflowMetrics& aKidDesiredSize, + const nsRect& aOriginalKidRect, + const nsRect& aOriginalKidVisualOverflow) { + WritingMode wm = aReflowState.reflowState.GetWritingMode(); bool isFirstReflow = (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; @@ -2702,12 +2748,12 @@ void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow, isFirstReflow); - // Adjust the running y-offset - aReflowState.y += aKidDesiredSize.Height(); + // Adjust the running block-offset + aReflowState.bCoord += aKidDesiredSize.BSize(wm); - // If our height is constrained, then update the available height - if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { - aReflowState.availSize.height -= aKidDesiredSize.Height(); + // If our bsize is constrained, then update the available bsize + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm)) { + aReflowState.availSize.BSize(wm) -= aKidDesiredSize.BSize(wm); } } @@ -2838,17 +2884,22 @@ nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState, // Reflow the child with unconstrained height WritingMode wm = aFrame->GetWritingMode(); LogicalSize availSize = aReflowState.reflowState.AvailableSize(wm); + + nscoord containerWidth = availSize.Width(wm); + // XXX check for containerWidth == NS_UNCONSTRAINEDSIZE + availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, - aFrame, availSize, - -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); + aFrame, availSize, nullptr, + nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(kidReflowState); kidReflowState.mFlags.mIsTopOfPage = true; nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); desiredSize.ClearSize(); nsReflowStatus status; ReflowChild(aFrame, presContext, desiredSize, kidReflowState, - aReflowState.x, aReflowState.y, 0, status); + wm, LogicalPoint(wm, aReflowState.iCoord, aReflowState.bCoord), + containerWidth, 0, status); // The child will be reflowed again "for real" so no need to place it now aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight)); @@ -2863,15 +2914,19 @@ nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState, { nsPresContext* presContext = PresContext(); WritingMode wm = aTfoot->GetWritingMode(); - LogicalSize kidAvailSize(wm, aReflowState.availSize); + LogicalSize kidAvailSize = aReflowState.availSize; + + nscoord containerWidth = kidAvailSize.Width(wm); + // XXX check for containerWidth == NS_UNCONSTRAINEDSIZE + kidAvailSize.BSize(wm) = aFooterHeight; nsHTMLReflowState footerReflowState(presContext, aReflowState.reflowState, aTfoot, kidAvailSize, - -1, -1, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(footerReflowState); - aReflowState.y += GetRowSpacing(GetRowCount()); + aReflowState.bCoord += GetRowSpacing(GetRowCount()); nsRect origTfootRect = aTfoot->GetRect(); nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect(); @@ -2879,13 +2934,18 @@ nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState, nsReflowStatus footerStatus; nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); desiredSize.ClearSize(); + LogicalPoint kidPosition(wm, aReflowState.iCoord, aReflowState.bCoord); ReflowChild(aTfoot, presContext, desiredSize, footerReflowState, - aReflowState.x, aReflowState.y, 0, footerStatus); - nsPoint kidPosition(aReflowState.x, aReflowState.y); - footerReflowState.ApplyRelativePositioning(&kidPosition); + wm, kidPosition, containerWidth, 0, footerStatus); + footerReflowState.ApplyRelativePositioning(&kidPosition, containerWidth); - PlaceChild(aReflowState, aTfoot, kidPosition, desiredSize, origTfootRect, - origTfootVisualOverflow); + PlaceChild(aReflowState, aTfoot, + // We subtract desiredSize.Width() from containerWidth here + // to account for the fact that in RTL modes, the origin is + // on the right-hand side so we're not simply converting a + // point, we're also swapping the child's origin side. + kidPosition.GetPhysicalPoint(wm, containerWidth - desiredSize.Width()), + desiredSize, origTfootRect, origTfootVisualOverflow); } // Reflow the children based on the avail size and reason in aReflowState @@ -2900,6 +2960,23 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, aLastChildReflowed = nullptr; nsIFrame* prevKidFrame = nullptr; + WritingMode wm = aReflowState.reflowState.GetWritingMode(); + nscoord containerWidth = aReflowState.reflowState.ComputedWidth(); + if (containerWidth == NS_UNCONSTRAINEDSIZE) { + NS_WARN_IF_FALSE(wm.IsVertical(), + "shouldn't have unconstrained width in horizontal mode"); + if (wm.IsVerticalRL()) { + nsHTMLReflowMetrics desiredSize(wm); + CalcDesiredBSize(aReflowState.reflowState, desiredSize); + containerWidth = desiredSize.Width(); + } else { + // in vertical-lr mode, containerWidth won't actually be used + containerWidth = 0; + } + } else { + containerWidth += + aReflowState.reflowState.ComputedPhysicalBorderPadding().LeftRight(); + } nsPresContext* presContext = PresContext(); // XXXldb Should we be checking constrained height instead? @@ -2907,7 +2984,7 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, // under paginated contexts tables are should not paginate if they are inside // column set bool isPaginated = presContext->IsPaginated() && - NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height && + NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm) && aReflowState.reflowState.mFlags.mTableIsSplittable; aOverflowAreas.Clear(); @@ -2948,15 +3025,15 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, for (size_t childX = 0; childX < rowGroups.Length(); childX++) { nsIFrame* kidFrame = rowGroups[childX]; nsTableRowGroupFrame* rowGroupFrame = rowGroups[childX]; - nscoord cellSpacingY = GetRowSpacing(rowGroupFrame->GetStartRowIndex()+ + nscoord cellSpacingB = GetRowSpacing(rowGroupFrame->GetStartRowIndex()+ rowGroupFrame->GetRowCount()); // Get the frame state bits // See if we should only reflow the dirty child frames if (reflowAllKids || NS_SUBTREE_DIRTY(kidFrame) || - (aReflowState.reflowState.mFlags.mSpecialHeightReflow && + (aReflowState.reflowState.mFlags.mSpecialBSizeReflow && (isPaginated || (kidFrame->GetStateBits() & - NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { + NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) { if (pageBreak) { if (allowRepeatedFooter) { PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); @@ -2969,17 +3046,17 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, break; } - nsSize kidAvailSize(aReflowState.availSize); + LogicalSize kidAvailSize(aReflowState.availSize); allowRepeatedFooter = false; - if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) { + if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.BSize(wm))) { nsTableRowGroupFrame* kidRG = static_cast(kidFrame); if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) { // the child is a tbody and there is a repeatable footer NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!"); - if (footerHeight + cellSpacingY < kidAvailSize.height) { + if (footerHeight + cellSpacingB < kidAvailSize.BSize(wm)) { allowRepeatedFooter = true; - kidAvailSize.height -= footerHeight + cellSpacingY; + kidAvailSize.BSize(wm) -= footerHeight + cellSpacingB; } } } @@ -2993,9 +3070,8 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, // Reflow the child into the available space nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, kidFrame, - LogicalSize(kidFrame->GetWritingMode(), - kidAvailSize), - -1, -1, + kidAvailSize, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(kidReflowState); @@ -3007,9 +3083,9 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, (rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) { kidReflowState.mFlags.mIsTopOfPage = false; } - aReflowState.y += cellSpacingY; - if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { - aReflowState.availSize.height -= cellSpacingY; + aReflowState.bCoord += cellSpacingB; + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm)) { + aReflowState.availSize.BSize(wm) -= cellSpacingB; } // record the presence of a next in flow, it might get destroyed so we // need to reorder the row group array @@ -3017,10 +3093,10 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, if (kidFrame->GetNextInFlow()) reorder = true; + LogicalPoint kidPosition(wm, aReflowState.iCoord, aReflowState.bCoord); ReflowChild(kidFrame, presContext, desiredSize, kidReflowState, - aReflowState.x, aReflowState.y, 0, aStatus); - nsPoint kidPosition(aReflowState.x, aReflowState.y); - kidReflowState.ApplyRelativePositioning(&kidPosition); + wm, kidPosition, containerWidth, 0, aStatus); + kidReflowState.ApplyRelativePositioning(&kidPosition, containerWidth); if (reorder) { // reorder row groups the reflow may have changed the nextinflows @@ -3052,8 +3128,10 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, if (childX+1 < rowGroups.Length()) { nsIFrame* nextRowGroupFrame = rowGroups[childX + 1]; if (nextRowGroupFrame) { - PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize, - oldKidRect, oldKidVisualOverflow); + PlaceChild(aReflowState, kidFrame, + kidPosition.GetPhysicalPoint(wm, containerWidth - + desiredSize.Width()), + desiredSize, oldKidRect, oldKidVisualOverflow); if (allowRepeatedFooter) { PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); } @@ -3081,8 +3159,10 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, break; } else { // we can't push so lets make clear how much space we need - PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize, - oldKidRect, oldKidVisualOverflow); + PlaceChild(aReflowState, kidFrame, + kidPosition.GetPhysicalPoint(wm, containerWidth - + desiredSize.Width()), + desiredSize, oldKidRect, oldKidVisualOverflow); aLastChildReflowed = kidFrame; if (allowRepeatedFooter) { PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); @@ -3105,8 +3185,10 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, } // Place the child - PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize, oldKidRect, - oldKidVisualOverflow); + PlaceChild(aReflowState, kidFrame, + kidPosition.GetPhysicalPoint(wm, containerWidth - + desiredSize.Width()), + desiredSize, oldKidRect, oldKidVisualOverflow); // Remember where we just were in case we end up pushing children prevKidFrame = kidFrame; @@ -3151,22 +3233,23 @@ nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, } } else { // it isn't being reflowed - aReflowState.y += cellSpacingY; - nsRect kidRect = kidFrame->GetNormalRect(); - if (kidRect.y != aReflowState.y) { + aReflowState.bCoord += cellSpacingB; + LogicalRect kidRect(wm, kidFrame->GetNormalRect(), containerWidth); + if (kidRect.BStart(wm) != aReflowState.bCoord) { // invalidate the old position kidFrame->InvalidateFrameSubtree(); // move to the new position - kidFrame->MovePositionBy(nsPoint(0, aReflowState.y - kidRect.y)); + kidFrame->MovePositionBy(wm, LogicalPoint(wm, 0, aReflowState.bCoord - + kidRect.BStart(wm))); RePositionViews(kidFrame); // invalidate the new position kidFrame->InvalidateFrameSubtree(); } - aReflowState.y += kidRect.height; + aReflowState.bCoord += kidRect.BSize(wm); - // If our height is constrained then update the available height. - if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { - aReflowState.availSize.height -= cellSpacingY + kidRect.height; + // If our bsize is constrained then update the available bsize. + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm)) { + aReflowState.availSize.BSize(wm) -= cellSpacingB + kidRect.BSize(wm); } } ConsiderChildOverflow(aOverflowAreas, kidFrame); @@ -3184,8 +3267,7 @@ nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext) if (!GetPrevInFlow() && !HaveReflowedColGroups()) { nsHTMLReflowMetrics kidMet(GetWritingMode()); nsPresContext *presContext = PresContext(); - for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame; - kidFrame = kidFrame->GetNextSibling()) { + for (nsIFrame* kidFrame : mColGroups) { if (NS_SUBTREE_DIRTY(kidFrame)) { // The column groups don't care about dimensions or reflow states. nsHTMLReflowState @@ -3202,61 +3284,63 @@ nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext) } void -nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) +nsTableFrame::CalcDesiredBSize(const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize) { + WritingMode wm = aReflowState.GetWritingMode(); nsTableCellMap* cellMap = GetCellMap(); if (!cellMap) { - NS_ASSERTION(false, "never ever call me until the cell map is built!"); - aDesiredSize.Height() = 0; + NS_ERROR("never ever call me until the cell map is built!"); + aDesiredSize.BSize(wm) = 0; return; } - nsMargin borderPadding = GetChildAreaOffset(&aReflowState); + LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState); - // get the natural height based on the last child's (row group) rect + // get the natural bsize based on the last child's (row group) rect RowGroupArray rowGroups; OrderRowGroups(rowGroups); if (rowGroups.IsEmpty()) { // tables can be used as rectangular items without content - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); - if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) && - (tableSpecifiedHeight > 0) && + nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowState); + if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedBSize) && + (tableSpecifiedBSize > 0) && eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) { // empty tables should not have a size in quirks mode - aDesiredSize.Height() = tableSpecifiedHeight; + aDesiredSize.BSize(wm) = tableSpecifiedBSize; + } else { + aDesiredSize.BSize(wm) = 0; } - else - aDesiredSize.Height() = 0; return; } int32_t rowCount = cellMap->GetRowCount(); int32_t colCount = cellMap->GetColCount(); - nscoord desiredHeight = borderPadding.top + borderPadding.bottom; + nscoord desiredBSize = borderPadding.BStartEnd(wm); if (rowCount > 0 && colCount > 0) { - desiredHeight += GetRowSpacing(-1); + desiredBSize += GetRowSpacing(-1); for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { - desiredHeight += rowGroups[rgX]->GetSize().height + + desiredBSize += rowGroups[rgX]->BSize(wm) + GetRowSpacing(rowGroups[rgX]->GetRowCount() + rowGroups[rgX]->GetStartRowIndex()); } } - // see if a specified table height requires dividing additional space to rows + // see if a specified table bsize requires dividing additional space to rows if (!GetPrevInFlow()) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); - if ((tableSpecifiedHeight > 0) && - (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && - (tableSpecifiedHeight > desiredHeight)) { - // proportionately distribute the excess height to unconstrained rows in each + nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowState); + if ((tableSpecifiedBSize > 0) && + (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE) && + (tableSpecifiedBSize > desiredBSize)) { + // proportionately distribute the excess bsize to unconstrained rows in each // unconstrained row group. - DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight); + DistributeBSizeToRows(aReflowState, tableSpecifiedBSize - desiredBSize); // this might have changed the overflow area incorporate the childframe overflow area. - for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { + for (nsIFrame* kidFrame : mFrames) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); } - desiredHeight = tableSpecifiedHeight; + desiredBSize = tableSpecifiedBSize; } } - aDesiredSize.Height() = desiredHeight; + aDesiredSize.BSize(wm) = desiredBSize; } static @@ -3290,43 +3374,48 @@ void ResizeCells(nsTableFrame& aTableFrame) } void -nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, - nscoord aAmount) +nsTableFrame::DistributeBSizeToRows(const nsHTMLReflowState& aReflowState, + nscoord aAmount) { - nsMargin borderPadding = GetChildAreaOffset(&aReflowState); + WritingMode wm = aReflowState.GetWritingMode(); + LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState); RowGroupArray rowGroups; OrderRowGroups(rowGroups); nscoord amountUsed = 0; - // distribute space to each pct height row whose row group doesn't have a computed - // height, and base the pct on the table height. If the row group had a computed - // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights - nscoord pctBasis = aReflowState.ComputedHeight() - GetRowSpacing(-1, GetRowCount()); - nscoord yOriginRG = borderPadding.top + GetRowSpacing(0); - nscoord yEndRG = yOriginRG; + // distribute space to each pct bsize row whose row group doesn't have a computed + // bsize, and base the pct on the table bsize. If the row group had a computed + // bsize, then this was already done in nsTableRowGroupFrame::CalculateRowBSizes + nscoord pctBasis = aReflowState.ComputedBSize() - GetRowSpacing(-1, GetRowCount()); + nscoord bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0); + nscoord bEndRG = bOriginRG; uint32_t rgX; for (rgX = 0; rgX < rowGroups.Length(); rgX++) { nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; nscoord amountUsedByRG = 0; - nscoord yOriginRow = 0; - nsRect rgNormalRect = rgFrame->GetNormalRect(); - if (!rgFrame->HasStyleHeight()) { + nscoord bOriginRow = 0; + // We pass containerWidth of zero when constructing the LogicalRect here, + // and similarly below. This is OK because these rects will be used to make + // relative adjustments, not for actual conversion to physical coords. + LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), 0); + if (!rgFrame->HasStyleBSize()) { nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); while (rowFrame) { - nsRect rowNormalRect = rowFrame->GetNormalRect(); - nscoord cellSpacingY = GetRowSpacing(rowFrame->GetRowIndex()); - if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { - nscoord pctHeight = rowFrame->GetHeight(pctBasis); + LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(), 0); + nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex()); + if ((amountUsed < aAmount) && rowFrame->HasPctBSize()) { + nscoord pctBSize = rowFrame->GetBSize(pctBasis); nscoord amountForRow = std::min(aAmount - amountUsed, - pctHeight - rowNormalRect.height); + pctBSize - rowNormalRect.BSize(wm)); if (amountForRow > 0) { - // XXXbz we don't need to move the row's y position to yOriginRow? + // XXXbz we don't need to move the row's b-position to bOriginRow? nsRect origRowRect = rowFrame->GetRect(); - nscoord newRowHeight = rowNormalRect.height + amountForRow; - rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight)); - yOriginRow += newRowHeight + cellSpacingY; - yEndRG += newRowHeight + cellSpacingY; + nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow; + rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm), + newRowBSize)); + bOriginRow += newRowBSize + cellSpacingB; + bEndRG += newRowBSize + cellSpacingB; amountUsed += amountForRow; amountUsedByRG += amountForRow; //rowFrame->DidResize(); @@ -3337,42 +3426,45 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, } } else { - if (amountUsed > 0 && yOriginRow != rowNormalRect.y && + if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm) && !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { rowFrame->InvalidateFrameSubtree(); - rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y)); + rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow - + rowNormalRect.BStart(wm))); nsTableFrame::RePositionViews(rowFrame); rowFrame->InvalidateFrameSubtree(); } - yOriginRow += rowNormalRect.height + cellSpacingY; - yEndRG += rowNormalRect.height + cellSpacingY; + bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB; + bEndRG += rowNormalRect.BSize(wm) + cellSpacingB; } rowFrame = rowFrame->GetNextRow(); } if (amountUsed > 0) { - if (rgNormalRect.y != yOriginRG) { + if (rgNormalRect.BStart(wm) != bOriginRG) { rgFrame->InvalidateFrameSubtree(); } nsRect origRgNormalRect = rgFrame->GetRect(); nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect(); - rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y)); - rgFrame->SetSize(nsSize(rgNormalRect.width, - rgNormalRect.height + amountUsedByRG)); + rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG - + rgNormalRect.BStart(wm))); + rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm), + rgNormalRect.BSize(wm) + amountUsedByRG)); nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect, origRgVisualOverflow, false); } } - else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) { + else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) { rgFrame->InvalidateFrameSubtree(); - rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y)); + rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG - + rgNormalRect.BStart(wm))); // Make sure child views are properly positioned nsTableFrame::RePositionViews(rgFrame); rgFrame->InvalidateFrameSubtree(); } - yOriginRG = yEndRG; + bOriginRG = bEndRG; } if (amountUsed >= aAmount) { @@ -3380,16 +3472,16 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, return; } - // get the first row without a style height where its row group has an - // unconstrained height + // get the first row without a style bsize where its row group has an + // unconstrained bsize nsTableRowGroupFrame* firstUnStyledRG = nullptr; nsTableRowFrame* firstUnStyledRow = nullptr; for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) { nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; - if (!rgFrame->HasStyleHeight()) { + if (!rgFrame->HasStyleBSize()) { nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); while (rowFrame) { - if (!rowFrame->HasStyleHeight()) { + if (!rowFrame->HasStyleBSize()) { firstUnStyledRG = rgFrame; firstUnStyledRow = rowFrame; break; @@ -3400,10 +3492,10 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, } nsTableRowFrame* lastEligibleRow = nullptr; - // Accumulate the correct divisor. This will be the total total height of all + // Accumulate the correct divisor. This will be the total bsize of all // unstyled rows inside unstyled row groups, unless there are none, in which // case, it will be number of all rows. If the unstyled rows don't have a - // height, divide the space equally among them. + // bsize, divide the space equally among them. nscoord divisor = 0; int32_t eligibleRows = 0; bool expandEmptyRows = false; @@ -3415,13 +3507,13 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, else { for (rgX = 0; rgX < rowGroups.Length(); rgX++) { nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; - if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { + if (!firstUnStyledRG || !rgFrame->HasStyleBSize()) { nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); while (rowFrame) { - if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { - NS_ASSERTION(rowFrame->GetSize().height >= 0, - "negative row frame height"); - divisor += rowFrame->GetSize().height; + if (!firstUnStyledRG || !rowFrame->HasStyleBSize()) { + NS_ASSERTION(rowFrame->BSize(wm) >= 0, + "negative row frame block-size"); + divisor += rowFrame->BSize(wm); eligibleRows++; lastEligibleRow = rowFrame; } @@ -3439,31 +3531,31 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, } } } - // allocate the extra height to the unstyled row groups and rows - nscoord heightToDistribute = aAmount - amountUsed; - yOriginRG = borderPadding.top + GetRowSpacing(-1); - yEndRG = yOriginRG; + // allocate the extra bsize to the unstyled row groups and rows + nscoord bSizeToDistribute = aAmount - amountUsed; + bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1); + bEndRG = bOriginRG; for (rgX = 0; rgX < rowGroups.Length(); rgX++) { nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; nscoord amountUsedByRG = 0; - nscoord yOriginRow = 0; - nsRect rgNormalRect = rgFrame->GetNormalRect(); + nscoord bOriginRow = 0; + LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), 0); nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect(); // see if there is an eligible row group or we distribute to all rows - if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) { + if (!firstUnStyledRG || !rgFrame->HasStyleBSize() || !eligibleRows) { nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); while (rowFrame) { - nscoord cellSpacingY = GetRowSpacing(rowFrame->GetRowIndex()); - nsRect rowNormalRect = rowFrame->GetNormalRect(); + nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex()); + LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(), 0); nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); // see if there is an eligible row or we distribute to all rows - if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) { + if (!firstUnStyledRow || !rowFrame->HasStyleBSize() || !eligibleRows) { float ratio; if (eligibleRows) { if (!expandEmptyRows) { - // The amount of additional space each row gets is proportional to - // its height - ratio = float(rowNormalRect.height) / float(divisor); + // The amount of additional space each row gets is proportional + // to its bsize + ratio = float(rowNormalRect.BSize(wm)) / float(divisor); } else { // empty rows get all the same additional space ratio = 1.0f / float(eligibleRows); @@ -3475,22 +3567,26 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, } // give rows their additional space, except for the last row which // gets the remainder - nscoord amountForRow = (rowFrame == lastEligibleRow) - ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio); + nscoord amountForRow = + (rowFrame == lastEligibleRow) + ? aAmount - amountUsed + : NSToCoordRound(((float)(bSizeToDistribute)) * ratio); amountForRow = std::min(amountForRow, aAmount - amountUsed); - if (yOriginRow != rowNormalRect.y) { + if (bOriginRow != rowNormalRect.BStart(wm)) { rowFrame->InvalidateFrameSubtree(); } - // update the row height + // update the row bsize nsRect origRowRect = rowFrame->GetRect(); - nscoord newRowHeight = rowNormalRect.height + amountForRow; - rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y)); - rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight)); + nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow; + rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow - + rowNormalRect.BStart(wm))); + rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm), + newRowBSize)); - yOriginRow += newRowHeight + cellSpacingY; - yEndRG += newRowHeight + cellSpacingY; + bOriginRow += newRowBSize + cellSpacingB; + bEndRG += newRowBSize + cellSpacingB; amountUsed += amountForRow; amountUsedByRG += amountForRow; @@ -3502,56 +3598,58 @@ nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, rowVisualOverflow, false); } else { - if (amountUsed > 0 && yOriginRow != rowNormalRect.y) { + if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm)) { rowFrame->InvalidateFrameSubtree(); - rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y)); + rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow - + rowNormalRect.BStart(wm))); nsTableFrame::RePositionViews(rowFrame); rowFrame->InvalidateFrameSubtree(); } - yOriginRow += rowNormalRect.height + cellSpacingY; - yEndRG += rowNormalRect.height + cellSpacingY; + bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB; + bEndRG += rowNormalRect.BSize(wm) + cellSpacingB; } rowFrame = rowFrame->GetNextRow(); } if (amountUsed > 0) { - if (rgNormalRect.y != yOriginRG) { + if (rgNormalRect.BStart(wm) != bOriginRG) { rgFrame->InvalidateFrameSubtree(); } nsRect origRgNormalRect = rgFrame->GetRect(); - rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y)); - rgFrame->SetSize(nsSize(rgNormalRect.width, - rgNormalRect.height + amountUsedByRG)); + rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG - + rgNormalRect.BStart(wm))); + rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm), + rgNormalRect.BSize(wm) + amountUsedByRG)); nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect, rgVisualOverflow, false); } // Make sure child views are properly positioned } - else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) { + else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) { rgFrame->InvalidateFrameSubtree(); - rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y)); + rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG - + rgNormalRect.BStart(wm))); // Make sure child views are properly positioned nsTableFrame::RePositionViews(rgFrame); rgFrame->InvalidateFrameSubtree(); } - yOriginRG = yEndRG; + bOriginRG = bEndRG; } ResizeCells(*this); } -int32_t nsTableFrame::GetColumnISize(int32_t aColIndex) +nscoord +nsTableFrame::GetColumnISizeFromFirstInFlow(int32_t aColIndex) { - nsTableFrame* firstInFlow = static_cast(FirstInFlow()); - if (this == firstInFlow) { - nsTableColFrame* colFrame = GetColFrame(aColIndex); - return colFrame ? colFrame->GetFinalISize() : 0; - } - return firstInFlow->GetColumnISize(aColIndex); + MOZ_ASSERT(this == FirstInFlow()); + nsTableColFrame* colFrame = GetColFrame(aColIndex); + return colFrame ? colFrame->GetFinalISize() : 0; } -nscoord nsTableFrame::GetColSpacing() +nscoord +nsTableFrame::GetColSpacing() { if (IsBorderCollapse()) return 0; @@ -3560,7 +3658,8 @@ nscoord nsTableFrame::GetColSpacing() } // XXX: could cache this. But be sure to check style changes if you do! -nscoord nsTableFrame::GetColSpacing(int32_t aColIndex) +nscoord +nsTableFrame::GetColSpacing(int32_t aColIndex) { NS_ASSERTION(aColIndex >= -1 && aColIndex <= GetColCount(), "Column index exceeds the bounds of the table"); @@ -3570,8 +3669,9 @@ nscoord nsTableFrame::GetColSpacing(int32_t aColIndex) return GetColSpacing(); } -nscoord nsTableFrame::GetColSpacing(int32_t aStartColIndex, - int32_t aEndColIndex) +nscoord +nsTableFrame::GetColSpacing(int32_t aStartColIndex, + int32_t aEndColIndex) { NS_ASSERTION(aStartColIndex >= -1 && aStartColIndex <= GetColCount(), "Start column index exceeds the bounds of the table"); @@ -3584,7 +3684,8 @@ nscoord nsTableFrame::GetColSpacing(int32_t aStartColIndex, return GetColSpacing() * (aEndColIndex - aStartColIndex); } -nscoord nsTableFrame::GetRowSpacing() +nscoord +nsTableFrame::GetRowSpacing() { if (IsBorderCollapse()) return 0; @@ -3593,7 +3694,8 @@ nscoord nsTableFrame::GetRowSpacing() } // XXX: could cache this. But be sure to check style changes if you do! -nscoord nsTableFrame::GetRowSpacing(int32_t aRowIndex) +nscoord +nsTableFrame::GetRowSpacing(int32_t aRowIndex) { NS_ASSERTION(aRowIndex >= -1 && aRowIndex <= GetRowCount(), "Row index exceeds the bounds of the table"); @@ -3603,8 +3705,9 @@ nscoord nsTableFrame::GetRowSpacing(int32_t aRowIndex) return GetRowSpacing(); } -nscoord nsTableFrame::GetRowSpacing(int32_t aStartRowIndex, - int32_t aEndRowIndex) +nscoord +nsTableFrame::GetRowSpacing(int32_t aStartRowIndex, + int32_t aEndRowIndex) { NS_ASSERTION(aStartRowIndex >= -1 && aStartRowIndex <= GetRowCount(), "Start row index exceeds the bounds of the table"); @@ -3697,26 +3800,27 @@ nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, } bool -nsTableFrame::IsAutoHeight() +nsTableFrame::IsAutoBSize(WritingMode aWM) { - const nsStyleCoord &height = StylePosition()->mHeight; + const nsStyleCoord &bsize = StylePosition()->BSize(aWM); // Don't consider calc() here like this quirk for percent. - return height.GetUnit() == eStyleUnit_Auto || - (height.GetUnit() == eStyleUnit_Percent && - height.GetPercentValue() <= 0.0f); + return bsize.GetUnit() == eStyleUnit_Auto || + (bsize.GetUnit() == eStyleUnit_Percent && + bsize.GetPercentValue() <= 0.0f); } nscoord -nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) +nsTableFrame::CalcBorderBoxBSize(const nsHTMLReflowState& aState) { - nscoord height = aState.ComputedHeight(); - if (NS_AUTOHEIGHT != height) { - nsMargin borderPadding = GetChildAreaOffset(&aState); - height += borderPadding.top + borderPadding.bottom; + nscoord bSize = aState.ComputedBSize(); + if (NS_AUTOHEIGHT != bSize) { + WritingMode wm = aState.GetWritingMode(); + LogicalMargin borderPadding = GetChildAreaOffset(wm, &aState); + bSize += borderPadding.BStartEnd(wm); } - height = std::max(0, height); + bSize = std::max(0, bSize); - return height; + return bSize; } bool @@ -3812,8 +3916,9 @@ nsTableFrame::Dump(bool aDumpRows, printf("mColWidths="); int32_t numCols = GetColCount(); int32_t colX; + nsTableFrame* fif = static_cast(FirstInFlow()); for (colX = 0; colX < numCols; colX++) { - printf("%d ", GetColumnISize(colX)); + printf("%d ", fif->GetColumnISizeFromFirstInFlow(colX)); } printf("\n"); @@ -3851,8 +3956,7 @@ nsTableFrame::Dump(bool aDumpRows, } } printf("\n colgroups->"); - for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame; - childFrame = childFrame->GetNextSibling()) { + for (nsIFrame* childFrame : mColGroups) { if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) { nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame; colGroupFrame->Dump(1); @@ -3885,67 +3989,35 @@ nsTableIterator::nsTableIterator(nsFrameList& aSource) Init(firstChild); } -void nsTableIterator::Init(nsIFrame* aFirstChild) +void +nsTableIterator::Init(nsIFrame* aFirstChild) { mFirstListChild = aFirstChild; mFirstChild = aFirstChild; mCurrentChild = nullptr; - mLeftToRight = true; mCount = -1; - - if (!mFirstChild) { - return; - } - - nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild); - mLeftToRight = (NS_STYLE_DIRECTION_LTR == - table->StyleVisibility()->mDirection); - - if (!mLeftToRight) { - mCount = 0; - nsIFrame* nextChild = mFirstChild->GetNextSibling(); - while (nullptr != nextChild) { - mCount++; - mFirstChild = nextChild; - nextChild = nextChild->GetNextSibling(); - } - } } -nsIFrame* nsTableIterator::First() +nsIFrame* +nsTableIterator::First() { mCurrentChild = mFirstChild; return mCurrentChild; } -nsIFrame* nsTableIterator::Next() +nsIFrame* +nsTableIterator::Next() { if (!mCurrentChild) { return nullptr; } - if (mLeftToRight) { - mCurrentChild = mCurrentChild->GetNextSibling(); - return mCurrentChild; - } - else { - nsIFrame* targetChild = mCurrentChild; - mCurrentChild = nullptr; - nsIFrame* child = mFirstListChild; - while (child && (child != targetChild)) { - mCurrentChild = child; - child = child->GetNextSibling(); - } - return mCurrentChild; - } + mCurrentChild = mCurrentChild->GetNextSibling(); + return mCurrentChild; } -bool nsTableIterator::IsLeftToRight() -{ - return mLeftToRight; -} - -int32_t nsTableIterator::Count() +int32_t +nsTableIterator::Count() { if (-1 == mCount) { mCount = 0; @@ -4125,41 +4197,41 @@ struct BCMapCellInfo // the current position inside the table and the cached frames that correspond // to this position. The widths are stored in member variables of the internal // table frames. - void SetTableTopLeftContBCBorder(); - void SetRowGroupLeftContBCBorder(); - void SetRowGroupRightContBCBorder(); - void SetRowGroupBottomContBCBorder(); - void SetRowLeftContBCBorder(); - void SetRowRightContBCBorder(); - void SetColumnTopRightContBCBorder(); - void SetColumnBottomContBCBorder(); - void SetColGroupBottomContBCBorder(); - void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, - nsTableRowFrame* aNextRow); + void SetTableBStartIStartContBCBorder(); + void SetRowGroupIStartContBCBorder(); + void SetRowGroupIEndContBCBorder(); + void SetRowGroupBEndContBCBorder(); + void SetRowIStartContBCBorder(); + void SetRowIEndContBCBorder(); + void SetColumnBStartIEndContBCBorder(); + void SetColumnBEndContBCBorder(); + void SetColGroupBEndContBCBorder(); + void SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup, + nsTableRowFrame* aNextRow); // functions to set the border widths on the table related frames, where the // knowledge about the current position in the table is used. - void SetTableTopBorderWidth(BCPixelSize aWidth); - void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth); - void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth); - void SetTableBottomBorderWidth(BCPixelSize aWidth); - void SetLeftBorderWidths(BCPixelSize aWidth); - void SetRightBorderWidths(BCPixelSize aWidth); - void SetTopBorderWidths(BCPixelSize aWidth); - void SetBottomBorderWidths(BCPixelSize aWidth); + void SetTableBStartBorderWidth(BCPixelSize aWidth); + void SetTableIStartBorderWidth(int32_t aRowY, BCPixelSize aWidth); + void SetTableIEndBorderWidth(int32_t aRowY, BCPixelSize aWidth); + void SetTableBEndBorderWidth(BCPixelSize aWidth); + void SetIStartBorderWidths(BCPixelSize aWidth); + void SetIEndBorderWidths(BCPixelSize aWidth); + void SetBStartBorderWidths(BCPixelSize aWidth); + void SetBEndBorderWidths(BCPixelSize aWidth); // functions to compute the borders; they depend on the // knowledge about the current position in the table. The edge functions // should be called if a table edge is involved, otherwise the internal // functions should be called. - BCCellBorder GetTopEdgeBorder(); - BCCellBorder GetBottomEdgeBorder(); - BCCellBorder GetLeftEdgeBorder(); - BCCellBorder GetRightEdgeBorder(); - BCCellBorder GetRightInternalBorder(); - BCCellBorder GetLeftInternalBorder(); - BCCellBorder GetTopInternalBorder(); - BCCellBorder GetBottomInternalBorder(); + BCCellBorder GetBStartEdgeBorder(); + BCCellBorder GetBEndEdgeBorder(); + BCCellBorder GetIStartEdgeBorder(); + BCCellBorder GetIEndEdgeBorder(); + BCCellBorder GetIEndInternalBorder(); + BCCellBorder GetIStartInternalBorder(); + BCCellBorder GetBStartInternalBorder(); + BCCellBorder GetBEndInternalBorder(); // functions to set the interal position information void SetColumn(int32_t aColX); @@ -4175,19 +4247,14 @@ struct BCMapCellInfo int32_t mNumTableRows; int32_t mNumTableCols; BCPropertyData* mTableBCData; - - // storage of table ltr information, the border collapse code swaps the sides - // to account for rtl tables, this is done through mStartSide and mEndSide - bool mTableIsLTR; - mozilla::css::Side mStartSide; - mozilla::css::Side mEndSide; + WritingMode mTableWM; // a cell can only belong to one rowgroup nsTableRowGroupFrame* mRowGroup; // a cell with a rowspan has a top and a bottom row, and rows in between - nsTableRowFrame* mTopRow; - nsTableRowFrame* mBottomRow; + nsTableRowFrame* mStartRow; + nsTableRowFrame* mEndRow; nsTableRowFrame* mCurrentRowFrame; // a cell with a colspan has a left and right column and columns in between @@ -4195,8 +4262,8 @@ struct BCMapCellInfo nsTableColGroupFrame* mColGroup; nsTableColGroupFrame* mCurrentColGroupFrame; - nsTableColFrame* mLeftCol; - nsTableColFrame* mRightCol; + nsTableColFrame* mStartCol; + nsTableColFrame* mEndCol; nsTableColFrame* mCurrentColFrame; // cell information @@ -4209,57 +4276,50 @@ struct BCMapCellInfo int32_t mColSpan; // flags to describe the position of the cell with respect to the row- and - // colgroups, for instance mRgAtTop documents that the top cell border hits + // colgroups, for instance mRgAtStart documents that the top cell border hits // a rowgroup border - bool mRgAtTop; - bool mRgAtBottom; - bool mCgAtLeft; - bool mCgAtRight; + bool mRgAtStart; + bool mRgAtEnd; + bool mCgAtStart; + bool mCgAtEnd; }; BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame) + : mTableFrame(aTableFrame) + , mNumTableRows(aTableFrame->GetRowCount()) + , mNumTableCols(aTableFrame->GetColCount()) + , mTableBCData(static_cast( + mTableFrame->Properties().Get(TableBCProperty()))) + , mTableWM(aTableFrame->StyleContext()) { - mTableFrame = aTableFrame; - mTableIsLTR = - aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; - if (mTableIsLTR) { - mStartSide = NS_SIDE_LEFT; - mEndSide = NS_SIDE_RIGHT; - } - else { - mStartSide = NS_SIDE_RIGHT; - mEndSide = NS_SIDE_LEFT; - } - mNumTableRows = mTableFrame->GetRowCount(); - mNumTableCols = mTableFrame->GetColCount(); - mTableBCData = static_cast - (mTableFrame->Properties().Get(TableBCProperty())); - ResetCellInfo(); } -void BCMapCellInfo::ResetCellInfo() +void +BCMapCellInfo::ResetCellInfo() { mCellData = nullptr; mRowGroup = nullptr; - mTopRow = nullptr; - mBottomRow = nullptr; + mStartRow = nullptr; + mEndRow = nullptr; mColGroup = nullptr; - mLeftCol = nullptr; - mRightCol = nullptr; + mStartCol = nullptr; + mEndCol = nullptr; mCell = nullptr; mRowIndex = mRowSpan = mColIndex = mColSpan = 0; - mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false; + mRgAtStart = mRgAtEnd = mCgAtStart = mCgAtEnd = false; } -inline int32_t BCMapCellInfo::GetCellEndRowIndex() const +inline int32_t +BCMapCellInfo::GetCellEndRowIndex() const { return mRowIndex + mRowSpan - 1; } -inline int32_t BCMapCellInfo::GetCellEndColIndex() const +inline int32_t +BCMapCellInfo::GetCellEndColIndex() const { return mColIndex + mColSpan - 1; } @@ -4354,7 +4414,7 @@ BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, // this row mRowIndex = 0; if (aNewRow) { - mTopRow = aNewRow; + mStartRow = aNewRow; mRowIndex = aNewRow->GetRowIndex(); } @@ -4365,34 +4425,34 @@ BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, if (aCellData) { mCell = static_cast(aCellData->GetCellFrame()); if (mCell) { - if (!mTopRow) { - mTopRow = static_cast(mCell->GetParent()); - if (!mTopRow) ABORT0(); - mRowIndex = mTopRow->GetRowIndex(); + if (!mStartRow) { + mStartRow = mCell->GetTableRowFrame(); + if (!mStartRow) ABORT0(); + mRowIndex = mStartRow->GetRowIndex(); } mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap); mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap); } } - if (!mTopRow) { - mTopRow = aIter->GetCurrentRow(); + if (!mStartRow) { + mStartRow = aIter->GetCurrentRow(); } if (1 == mRowSpan) { - mBottomRow = mTopRow; + mEndRow = mStartRow; } else { - mBottomRow = mTopRow->GetNextRow(); - if (mBottomRow) { - for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) { - mBottomRow = mBottomRow->GetNextRow(); + mEndRow = mStartRow->GetNextRow(); + if (mEndRow) { + for (int32_t span = 2; mEndRow && span < mRowSpan; span++) { + mEndRow = mEndRow->GetNextRow(); } - NS_ASSERTION(mBottomRow, "spanned row not found"); + NS_ASSERTION(mEndRow, "spanned row not found"); } else { - NS_ASSERTION(false, "error in cell map"); + NS_ERROR("error in cell map"); mRowSpan = 1; - mBottomRow = mTopRow; + mEndRow = mStartRow; } } // row group frame info @@ -4401,33 +4461,33 @@ BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, // possible uint32_t rgStart = aIter->mRowGroupStart; uint32_t rgEnd = aIter->mRowGroupEnd; - mRowGroup = static_cast(mTopRow->GetParent()); + mRowGroup = mStartRow->GetTableRowGroupFrame(); if (mRowGroup != aIter->GetCurrentRowGroup()) { rgStart = mRowGroup->GetStartRowIndex(); rgEnd = rgStart + mRowGroup->GetRowCount() - 1; } - uint32_t rowIndex = mTopRow->GetRowIndex(); - mRgAtTop = (rgStart == rowIndex); - mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1); + uint32_t rowIndex = mStartRow->GetRowIndex(); + mRgAtStart = rgStart == rowIndex; + mRgAtEnd = rgEnd == rowIndex + mRowSpan - 1; // col frame info - mLeftCol = mTableFrame->GetColFrame(aColIndex); - if (!mLeftCol) ABORT0(); + mStartCol = mTableFrame->GetColFrame(aColIndex); + if (!mStartCol) ABORT0(); - mRightCol = mLeftCol; + mEndCol = mStartCol; if (mColSpan > 1) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + mColSpan -1); if (!colFrame) ABORT0(); - mRightCol = colFrame; + mEndCol = colFrame; } // col group frame info - mColGroup = static_cast(mLeftCol->GetParent()); + mColGroup = mStartCol->GetTableColGroupFrame(); int32_t cgStart = mColGroup->GetStartColumnIndex(); int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1); - mCgAtLeft = (cgStart == aColIndex); - mCgAtRight = (cgEnd == aColIndex + mColSpan - 1); + mCgAtStart = cgStart == aColIndex; + mCgAtEnd = cgEnd == aColIndex + mColSpan - 1; } bool @@ -4673,101 +4733,69 @@ static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE // and the lowest: 'inset'. none is even weaker #define CELL_CORNER true -/** return the border style, border color for a given frame and side +/** return the border style, border color and optionally the width in + * pixel for a given frame and side * @param aFrame - query the info for this frame + * @param aTableWM - the writing-mode of the frame * @param aSide - the side of the frame * @param aStyle - the border style * @param aColor - the border color - * @param aTableIsLTR - table direction is LTR + * @param aWidth - the border width in px. + * @param aWidth - the border width in px */ static void -GetColorAndStyle(const nsIFrame* aFrame, - mozilla::css::Side aSide, - uint8_t& aStyle, - nscolor& aColor, - bool aTableIsLTR) +GetColorAndStyle(const nsIFrame* aFrame, + WritingMode aTableWM, + LogicalSide aSide, + uint8_t* aStyle, + nscolor* aColor, + BCPixelSize* aWidth = nullptr) { NS_PRECONDITION(aFrame, "null frame"); + NS_PRECONDITION(aStyle && aColor, "null argument"); // initialize out arg - aColor = 0; - const nsStyleBorder* styleData = aFrame->StyleBorder(); - if(!aTableIsLTR) { // revert the directions - if (NS_SIDE_RIGHT == aSide) { - aSide = NS_SIDE_LEFT; - } - else if (NS_SIDE_LEFT == aSide) { - aSide = NS_SIDE_RIGHT; - } + *aColor = 0; + if (aWidth) { + *aWidth = 0; } - aStyle = styleData->GetBorderStyle(aSide); - if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || - (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { + const nsStyleBorder* styleData = aFrame->StyleBorder(); + mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide); + *aStyle = styleData->GetBorderStyle(physicalSide); + + if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) || + (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) { return; } - aColor = aFrame->StyleContext()->GetVisitedDependentColor( - nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]); + *aColor = aFrame->StyleContext()->GetVisitedDependentColor( + nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[physicalSide]); + + if (aWidth) { + nscoord width = styleData->GetComputedBorderWidth(physicalSide); + *aWidth = aFrame->PresContext()->AppUnitsToDevPixels(width); + } } /** coerce the paint style as required by CSS2.1 * @param aFrame - query the info for this frame + * @param aTableWM - the writing mode of the frame * @param aSide - the side of the frame * @param aStyle - the border style * @param aColor - the border color - * @param aTableIsLTR - table direction is LTR */ static void -GetPaintStyleInfo(const nsIFrame* aFrame, - mozilla::css::Side aSide, - uint8_t& aStyle, - nscolor& aColor, - bool aTableIsLTR) +GetPaintStyleInfo(const nsIFrame* aFrame, + WritingMode aTableWM, + LogicalSide aSide, + uint8_t* aStyle, + nscolor* aColor) { - GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); - if (NS_STYLE_BORDER_STYLE_INSET == aStyle) { - aStyle = NS_STYLE_BORDER_STYLE_RIDGE; + GetColorAndStyle(aFrame, aTableWM, aSide, aStyle, aColor); + if (NS_STYLE_BORDER_STYLE_INSET == *aStyle) { + *aStyle = NS_STYLE_BORDER_STYLE_RIDGE; + } else if (NS_STYLE_BORDER_STYLE_OUTSET == *aStyle) { + *aStyle = NS_STYLE_BORDER_STYLE_GROOVE; } - else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) { - aStyle = NS_STYLE_BORDER_STYLE_GROOVE; - } -} - -/** return the border style, border color and the width in pixel for a given - * frame and side - * @param aFrame - query the info for this frame - * @param aSide - the side of the frame - * @param aStyle - the border style - * @param aColor - the border color - * @param aTableIsLTR - table direction is LTR - * @param aWidth - the border width in px. - * @param aTwipsToPixels - conversion factor from twips to pixel - */ -static void -GetColorAndStyle(const nsIFrame* aFrame, - mozilla::css::Side aSide, - uint8_t& aStyle, - nscolor& aColor, - bool aTableIsLTR, - BCPixelSize& aWidth) -{ - GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); - if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || - (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { - aWidth = 0; - return; - } - const nsStyleBorder* styleData = aFrame->StyleBorder(); - nscoord width; - if(!aTableIsLTR) { // revert the directions - if (NS_SIDE_RIGHT == aSide) { - aSide = NS_SIDE_LEFT; - } - else if (NS_SIDE_LEFT == aSide) { - aSide = NS_SIDE_RIGHT; - } - } - width = styleData->GetComputedBorderWidth(aSide); - aWidth = aFrame->PresContext()->AppUnitsToDevPixels(width); } class nsDelayedCalcBCBorders : public nsRunnable { @@ -4829,7 +4857,7 @@ CompareBorders(bool aIsCorner, // Pass true for corner calculatio const BCCellBorder& aBorder1, const BCCellBorder& aBorder2, bool aSecondIsHorizontal, - bool* aFirstDominates = nullptr) + bool* aFirstDominates = nullptr) { bool firstDominates = true; @@ -4875,14 +4903,13 @@ CompareBorders(bool aIsCorner, // Pass true for corner calculatio * scenarios with a adjacent owner. * @param xxxFrame - the frame for style information, might be zero if * it should not be considered + * @param aTableWM - the writing mode of the frame * @param aSide - side of the frames that should be considered * @param aAja - the border comparison takes place from the point of * a frame that is adjacent to the cellmap entry, for * when a cell owns its lower border it will be the * adjacent owner as in the cellmap only top and left * borders are stored. - * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel - * aligned. */ static BCCellBorder CompareBorders(const nsIFrame* aTableFrame, @@ -4891,16 +4918,17 @@ CompareBorders(const nsIFrame* aTableFrame, const nsIFrame* aRowGroupFrame, const nsIFrame* aRowFrame, const nsIFrame* aCellFrame, - bool aTableIsLTR, - mozilla::css::Side aSide, + WritingMode aTableWM, + LogicalSide aSide, bool aAja) { BCCellBorder border, tempBorder; - bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); + bool inlineAxis = IsBlock(aSide); // start with the table as dominant if present if (aTableFrame) { - GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width); + GetColorAndStyle(aTableFrame, aTableWM, aSide, + &border.style, &border.color, &border.width); border.owner = eTableOwner; if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { return border; @@ -4908,8 +4936,9 @@ CompareBorders(const nsIFrame* aTableFrame, } // see if the colgroup is dominant if (aColGroupFrame) { - GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); - tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; + GetColorAndStyle(aColGroupFrame, aTableWM, aSide, + &tempBorder.style, &tempBorder.color, &tempBorder.width); + tempBorder.owner = aAja && !inlineAxis ? eAjaColGroupOwner : eColGroupOwner; // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations. border = CompareBorders(!CELL_CORNER, border, tempBorder, false); if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { @@ -4918,8 +4947,9 @@ CompareBorders(const nsIFrame* aTableFrame, } // see if the col is dominant if (aColFrame) { - GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); - tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner; + GetColorAndStyle(aColFrame, aTableWM, aSide, + &tempBorder.style, &tempBorder.color, &tempBorder.width); + tempBorder.owner = aAja && !inlineAxis ? eAjaColOwner : eColOwner; border = CompareBorders(!CELL_CORNER, border, tempBorder, false); if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { return border; @@ -4927,8 +4957,9 @@ CompareBorders(const nsIFrame* aTableFrame, } // see if the rowgroup is dominant if (aRowGroupFrame) { - GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); - tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; + GetColorAndStyle(aRowGroupFrame, aTableWM, aSide, + &tempBorder.style, &tempBorder.color, &tempBorder.width); + tempBorder.owner = aAja && inlineAxis ? eAjaRowGroupOwner : eRowGroupOwner; border = CompareBorders(!CELL_CORNER, border, tempBorder, false); if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { return border; @@ -4936,8 +4967,9 @@ CompareBorders(const nsIFrame* aTableFrame, } // see if the row is dominant if (aRowFrame) { - GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); - tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; + GetColorAndStyle(aRowFrame, aTableWM, aSide, + &tempBorder.style, &tempBorder.color, &tempBorder.width); + tempBorder.owner = aAja && inlineAxis ? eAjaRowOwner : eRowOwner; border = CompareBorders(!CELL_CORNER, border, tempBorder, false); if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { return border; @@ -4945,8 +4977,9 @@ CompareBorders(const nsIFrame* aTableFrame, } // see if the cell is dominant if (aCellFrame) { - GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); - tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner; + GetColorAndStyle(aCellFrame, aTableWM, aSide, + &tempBorder.style, &tempBorder.color, &tempBorder.width); + tempBorder.owner = aAja ? eAjaCellOwner : eCellOwner; border = CompareBorders(!CELL_CORNER, border, tempBorder, false); } return border; @@ -5308,180 +5341,186 @@ nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const #define HORIZONTAL true void -BCMapCellInfo::SetTableTopLeftContBCBorder() +BCMapCellInfo::SetTableBStartIStartContBCBorder() { BCCellBorder currentBorder; //calculate continuous top first row & rowgroup border: special case //because it must include the table in the collapse - if (mTopRow) { + if (mStartRow) { currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, - mTopRow, nullptr, mTableIsLTR, - NS_SIDE_TOP, !ADJACENT); - mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); + mStartRow, nullptr, mTableWM, + eLogicalSideBStart, !ADJACENT); + mStartRow->SetContinuousBCBorderWidth(eLogicalSideBStart, + currentBorder.width); } - if (mCgAtRight && mColGroup) { + if (mCgAtEnd && mColGroup) { //calculate continuous top colgroup border once per colgroup currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, - mTopRow, nullptr, mTableIsLTR, - NS_SIDE_TOP, !ADJACENT); - mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); + mStartRow, nullptr, mTableWM, + eLogicalSideBStart, !ADJACENT); + mColGroup->SetContinuousBCBorderWidth(eLogicalSideBStart, + currentBorder.width); } if (0 == mColIndex) { - currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr, - nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, - !ADJACENT); + currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, nullptr, + nullptr, nullptr, mTableWM, + eLogicalSideIStart, !ADJACENT); mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width); } } void -BCMapCellInfo::SetRowGroupLeftContBCBorder() +BCMapCellInfo::SetRowGroupIStartContBCBorder() { BCCellBorder currentBorder; //get row group continuous borders - if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom - currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, - nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, - !ADJACENT); - mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width); + if (mRgAtEnd && mRowGroup) { //once per row group, so check for bottom + currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, + mRowGroup, nullptr, nullptr, mTableWM, + eLogicalSideIStart, !ADJACENT); + mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIStart, + currentBorder.width); } } void -BCMapCellInfo::SetRowGroupRightContBCBorder() +BCMapCellInfo::SetRowGroupIEndContBCBorder() { BCCellBorder currentBorder; //get row group continuous borders - if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom - currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, - nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT, + if (mRgAtEnd && mRowGroup) { //once per mRowGroup, so check for bottom + currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup, + nullptr, nullptr, mTableWM, eLogicalSideIEnd, ADJACENT); - mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width); + mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIEnd, + currentBorder.width); } } void -BCMapCellInfo::SetColumnTopRightContBCBorder() +BCMapCellInfo::SetColumnBStartIEndContBCBorder() { BCCellBorder currentBorder; //calculate column continuous borders //we only need to do this once, so we'll do it only on the first row currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, - mCurrentColFrame, mRowGroup, mTopRow, nullptr, - mTableIsLTR, NS_SIDE_TOP, !ADJACENT); - ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP, - currentBorder.width); + mCurrentColFrame, mRowGroup, mStartRow, + nullptr, mTableWM, eLogicalSideBStart, + !ADJACENT); + ((nsTableColFrame*) mCurrentColFrame)-> + SetContinuousBCBorderWidth(eLogicalSideBStart, currentBorder.width); if (mNumTableCols == GetCellEndColIndex() + 1) { currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, nullptr, nullptr, nullptr, - mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); + mTableWM, eLogicalSideIEnd, !ADJACENT); } else { currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame, mCurrentColFrame, nullptr,nullptr, nullptr, - mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); + mTableWM, eLogicalSideIEnd, !ADJACENT); } - mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT, + mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd, currentBorder.width); } void -BCMapCellInfo::SetColumnBottomContBCBorder() +BCMapCellInfo::SetColumnBEndContBCBorder() { BCCellBorder currentBorder; //get col continuous border currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, - mCurrentColFrame, mRowGroup, mBottomRow, - nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); - mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, + mCurrentColFrame, mRowGroup, mEndRow, + nullptr, mTableWM, eLogicalSideBEnd, ADJACENT); + mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width); } void -BCMapCellInfo::SetColGroupBottomContBCBorder() +BCMapCellInfo::SetColGroupBEndContBCBorder() { BCCellBorder currentBorder; if (mColGroup) { currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, - mBottomRow, nullptr, mTableIsLTR, - NS_SIDE_BOTTOM, ADJACENT); + mEndRow, nullptr, mTableWM, + eLogicalSideBEnd, ADJACENT); mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); } } void -BCMapCellInfo::SetRowGroupBottomContBCBorder() +BCMapCellInfo::SetRowGroupBEndContBCBorder() { BCCellBorder currentBorder; if (mRowGroup) { currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, - mBottomRow, nullptr, mTableIsLTR, - NS_SIDE_BOTTOM, ADJACENT); - mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); + mEndRow, nullptr, mTableWM, + eLogicalSideBEnd, ADJACENT); + mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width); } } void -BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, - nsTableRowFrame* aNextRow) +BCMapCellInfo::SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup, + nsTableRowFrame* aNextRow) { BCCellBorder currentBorder, adjacentBorder; - const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr; - currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow, - nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); + const nsIFrame* rowgroup = mRgAtEnd ? mRowGroup : nullptr; + currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mEndRow, + nullptr, mTableWM, eLogicalSideBEnd, ADJACENT); adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup, - aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP, + aNextRow, nullptr, mTableWM, eLogicalSideBStart, !ADJACENT); currentBorder = CompareBorders(false, currentBorder, adjacentBorder, HORIZONTAL); if (aNextRow) { - aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); + aNextRow->SetContinuousBCBorderWidth(eLogicalSideBStart, + currentBorder.width); } - if (mRgAtBottom && mRowGroup) { - mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); + if (mRgAtEnd && mRowGroup) { + mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width); } } void -BCMapCellInfo::SetRowLeftContBCBorder() +BCMapCellInfo::SetRowIStartContBCBorder() { //get row continuous borders if (mCurrentRowFrame) { BCCellBorder currentBorder; - currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, - mCurrentRowFrame, nullptr, mTableIsLTR, - NS_SIDE_LEFT, !ADJACENT); - mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide, + currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, + mRowGroup, mCurrentRowFrame, nullptr, + mTableWM, eLogicalSideIStart, !ADJACENT); + mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIStart, currentBorder.width); } } void -BCMapCellInfo::SetRowRightContBCBorder() +BCMapCellInfo::SetRowIEndContBCBorder() { if (mCurrentRowFrame) { BCCellBorder currentBorder; - currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, - mCurrentRowFrame, nullptr, mTableIsLTR, - NS_SIDE_RIGHT, ADJACENT); - mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide, + currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup, + mCurrentRowFrame, nullptr, mTableWM, + eLogicalSideIEnd, ADJACENT); + mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd, currentBorder.width); } } void -BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth) +BCMapCellInfo::SetTableBStartBorderWidth(BCPixelSize aWidth) { mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth); } void -BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth) +BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowY, BCPixelSize aWidth) { // update the left/right first cell border if (aRowY == 0) { - if (mTableIsLTR) { + if (mTableWM.IsBidiLTR()) { mTableBCData->mLeftCellBorderWidth = aWidth; } else { @@ -5493,11 +5532,11 @@ BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth) } void -BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth) +BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowY, BCPixelSize aWidth) { // update the left/right first cell border if (aRowY == 0) { - if (mTableIsLTR) { + if (mTableWM.IsBidiLTR()) { mTableBCData->mRightCellBorderWidth = aWidth; } else { @@ -5509,63 +5548,63 @@ BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth) } void -BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth) +BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth) { // update the borders of the cells and cols affected if (mCell) { - mCell->SetBorderWidth(mEndSide, std::max(aWidth, - mCell->GetBorderWidth(mEndSide))); + mCell->SetBorderWidth(eLogicalSideIEnd, std::max(aWidth, + mCell->GetBorderWidth(eLogicalSideIEnd))); } - if (mRightCol) { - BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth); - mRightCol->SetRightBorderWidth(std::max(nscoord(half), - mRightCol->GetRightBorderWidth())); + if (mEndCol) { + BCPixelSize half = BC_BORDER_START_HALF(aWidth); + mEndCol->SetIEndBorderWidth( + std::max(nscoord(half), mEndCol->GetIEndBorderWidth())); } } void -BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth) +BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth) { // update the borders of the affected cells and rows if (mCell) { - mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth, - mCell->GetBorderWidth(NS_SIDE_BOTTOM))); + mCell->SetBorderWidth(eLogicalSideBEnd, std::max(aWidth, + mCell->GetBorderWidth(eLogicalSideBEnd))); } - if (mBottomRow) { - BCPixelSize half = BC_BORDER_TOP_HALF(aWidth); - mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half), - mBottomRow->GetBottomBCBorderWidth())); + if (mEndRow) { + BCPixelSize half = BC_BORDER_START_HALF(aWidth); + mEndRow->SetBEndBCBorderWidth( + std::max(nscoord(half), mEndRow->GetBEndBCBorderWidth())); } } void -BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth) +BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth) { if (mCell) { - mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth, - mCell->GetBorderWidth(NS_SIDE_TOP))); + mCell->SetBorderWidth(eLogicalSideBStart, std::max(aWidth, + mCell->GetBorderWidth(eLogicalSideBStart))); } - if (mTopRow) { - BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth); - mTopRow->SetTopBCBorderWidth(std::max(nscoord(half), - mTopRow->GetTopBCBorderWidth())); + if (mStartRow) { + BCPixelSize half = BC_BORDER_END_HALF(aWidth); + mStartRow->SetBStartBCBorderWidth( + std::max(nscoord(half), mStartRow->GetBStartBCBorderWidth())); } } void -BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth) +BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth) { if (mCell) { - mCell->SetBorderWidth(mStartSide, std::max(aWidth, - mCell->GetBorderWidth(mStartSide))); + mCell->SetBorderWidth(eLogicalSideIStart, std::max(aWidth, + mCell->GetBorderWidth(eLogicalSideIStart))); } - if (mLeftCol) { - BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth); - mLeftCol->SetLeftBorderWidth(std::max(nscoord(half), - mLeftCol->GetLeftBorderWidth())); + if (mStartCol) { + BCPixelSize half = BC_BORDER_END_HALF(aWidth); + mStartCol->SetIStartBorderWidth( + std::max(nscoord(half), mStartCol->GetIStartBorderWidth())); } } void -BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth) +BCMapCellInfo::SetTableBEndBorderWidth(BCPixelSize aWidth) { mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth, aWidth); @@ -5588,69 +5627,69 @@ BCMapCellInfo::SetColumn(int32_t aColX) void BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell) { - mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow : - mCurrentRowFrame->GetNextRow(); + mCurrentRowFrame = + aResetToTopRowOfCell ? mStartRow : mCurrentRowFrame->GetNextRow(); } BCCellBorder -BCMapCellInfo::GetTopEdgeBorder() +BCMapCellInfo::GetBStartEdgeBorder() { return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, - mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP, - !ADJACENT); + mRowGroup, mStartRow, mCell, mTableWM, + eLogicalSideBStart, !ADJACENT); } BCCellBorder -BCMapCellInfo::GetBottomEdgeBorder() +BCMapCellInfo::GetBEndEdgeBorder() { return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, - mRowGroup, mBottomRow, mCell, mTableIsLTR, - NS_SIDE_BOTTOM, ADJACENT); + mRowGroup, mEndRow, mCell, mTableWM, + eLogicalSideBEnd, ADJACENT); } BCCellBorder -BCMapCellInfo::GetLeftEdgeBorder() +BCMapCellInfo::GetIStartEdgeBorder() { - return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, - mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT, + return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup, + mCurrentRowFrame, mCell, mTableWM, eLogicalSideIStart, !ADJACENT); } BCCellBorder -BCMapCellInfo::GetRightEdgeBorder() +BCMapCellInfo::GetIEndEdgeBorder() { - return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, - mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT, + return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup, + mCurrentRowFrame, mCell, mTableWM, eLogicalSideIEnd, ADJACENT); } BCCellBorder -BCMapCellInfo::GetRightInternalBorder() +BCMapCellInfo::GetIEndInternalBorder() { - const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr; - return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell, - mTableIsLTR, NS_SIDE_RIGHT, ADJACENT); + const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr; + return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell, + mTableWM, eLogicalSideIEnd, ADJACENT); } BCCellBorder -BCMapCellInfo::GetLeftInternalBorder() +BCMapCellInfo::GetIStartInternalBorder() { - const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr; - return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell, - mTableIsLTR, NS_SIDE_LEFT, !ADJACENT); + const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr; + return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell, + mTableWM, eLogicalSideIStart, !ADJACENT); } BCCellBorder -BCMapCellInfo::GetBottomInternalBorder() +BCMapCellInfo::GetBEndInternalBorder() { - const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr; - return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell, - mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); + const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr; + return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell, + mTableWM, eLogicalSideBEnd, ADJACENT); } BCCellBorder -BCMapCellInfo::GetTopInternalBorder() +BCMapCellInfo::GetBStartInternalBorder() { - const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr; - return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell, - mTableIsLTR, NS_SIDE_TOP, !ADJACENT); + const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr; + return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell, + mTableWM, eLogicalSideBStart, !ADJACENT); } /* Here is the order for storing border edges in the cell map as a cell is processed. There are @@ -5776,7 +5815,7 @@ nsTableFrame::CalcBCBorders() for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); colX++) { info.SetColumn(colX); - currentBorder = info.GetTopEdgeBorder(); + currentBorder = info.GetBStartEdgeBorder(); // update/store the top left & top right corners of the seg BCCornerInfo& tlCorner = topCorners[colX]; // top left if (0 == colX) { @@ -5798,11 +5837,11 @@ nsTableFrame::CalcBCBorders() 1, currentBorder.owner, currentBorder.width, startSeg); - info.SetTableTopBorderWidth(currentBorder.width); - info.SetTopBorderWidths(currentBorder.width); - info.SetColumnTopRightContBCBorder(); + info.SetTableBStartBorderWidth(currentBorder.width); + info.SetBStartBorderWidths(currentBorder.width); + info.SetColumnBStartIEndContBCBorder(); } - info.SetTableTopLeftContBCBorder(); + info.SetTableBStartIStartContBCBorder(); } else { // see if the top border needs to be the start of a segment due to a @@ -5832,7 +5871,7 @@ nsTableFrame::CalcBCBorders() for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); rowY++) { info.IncrementRow(rowY == info.mRowIndex); - currentBorder = info.GetLeftEdgeBorder(); + currentBorder = info.GetIStartEdgeBorder(); BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0]; tlCorner.Update(NS_SIDE_BOTTOM, currentBorder); tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, @@ -5849,11 +5888,11 @@ nsTableFrame::CalcBCBorders() iter.mRowGroupStart, rowY, info.mColIndex, 1, currentBorder.owner, currentBorder.width, startSeg); - info.SetTableLeftBorderWidth(rowY , currentBorder.width); - info.SetLeftBorderWidths(currentBorder.width); - info.SetRowLeftContBCBorder(); + info.SetTableIStartBorderWidth(rowY , currentBorder.width); + info.SetIStartBorderWidths(currentBorder.width); + info.SetRowIStartContBCBorder(); } - info.SetRowGroupLeftContBCBorder(); + info.SetRowGroupIStartContBCBorder(); } // find the dominant border considering the cell's right border, adjacent @@ -5868,7 +5907,7 @@ nsTableFrame::CalcBCBorders() for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); rowY++) { info.IncrementRow(rowY == info.mRowIndex); - currentBorder = info.GetRightEdgeBorder(); + currentBorder = info.GetIEndEdgeBorder(); // update/store the top right & bottom right corners BCCornerInfo& trCorner = (0 == rowY) ? topCorners[info.GetCellEndColIndex() + 1] : @@ -5897,11 +5936,11 @@ nsTableFrame::CalcBCBorders() info.GetCellEndColIndex(), 1, currentBorder.owner, currentBorder.width, startSeg); - info.SetTableRightBorderWidth(rowY, currentBorder.width); - info.SetRightBorderWidths(currentBorder.width); - info.SetRowRightContBCBorder(); + info.SetTableIEndBorderWidth(rowY, currentBorder.width); + info.SetIEndBorderWidths(currentBorder.width); + info.SetRowIEndContBCBorder(); } - info.SetRowGroupRightContBCBorder(); + info.SetRowGroupIEndContBCBorder(); } else { int32_t segLength = 0; @@ -5909,8 +5948,8 @@ nsTableFrame::CalcBCBorders() for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); rowY += segLength) { iter.PeekRight(info, rowY, ajaInfo); - currentBorder = info.GetRightInternalBorder(); - adjacentBorder = ajaInfo.GetLeftInternalBorder(); + currentBorder = info.GetIEndInternalBorder(); + adjacentBorder = ajaInfo.GetIStartInternalBorder(); currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, !HORIZONTAL); @@ -5928,8 +5967,8 @@ nsTableFrame::CalcBCBorders() info.GetCellEndColIndex(), segLength, currentBorder.owner, currentBorder.width, startSeg); - info.SetRightBorderWidths(currentBorder.width); - ajaInfo.SetLeftBorderWidths(currentBorder.width); + info.SetIEndBorderWidths(currentBorder.width); + ajaInfo.SetIStartBorderWidths(currentBorder.width); } // update the top right corner bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) && @@ -5941,8 +5980,8 @@ nsTableFrame::CalcBCBorders() // if this is not the first time through, // consider the segment to the right if (rowY != info.mRowIndex) { - currentBorder = priorAjaInfo.GetBottomInternalBorder(); - adjacentBorder = ajaInfo.GetTopInternalBorder(); + currentBorder = priorAjaInfo.GetBEndInternalBorder(); + adjacentBorder = ajaInfo.GetBStartInternalBorder(); currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL); trCorner->Update(NS_SIDE_RIGHT, currentBorder); @@ -5993,7 +6032,7 @@ nsTableFrame::CalcBCBorders() for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); colX++) { info.SetColumn(colX); - currentBorder = info.GetBottomEdgeBorder(); + currentBorder = info.GetBEndEdgeBorder(); // update/store the bottom left & bottom right corners BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left blCorner.Update(NS_SIDE_RIGHT, currentBorder); @@ -6034,20 +6073,20 @@ nsTableFrame::CalcBCBorders() lastBottomBorder.rowSpan = info.mRowSpan; lastBottomBorders[colX] = lastBottomBorder; - info.SetBottomBorderWidths(currentBorder.width); - info.SetTableBottomBorderWidth(currentBorder.width); - info.SetColumnBottomContBCBorder(); + info.SetBEndBorderWidths(currentBorder.width); + info.SetTableBEndBorderWidth(currentBorder.width); + info.SetColumnBEndContBCBorder(); } - info.SetRowGroupBottomContBCBorder(); - info.SetColGroupBottomContBCBorder(); + info.SetRowGroupBEndContBCBorder(); + info.SetColGroupBEndContBCBorder(); } else { int32_t segLength = 0; for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); colX += segLength) { iter.PeekBottom(info, colX, ajaInfo); - currentBorder = info.GetBottomInternalBorder(); - adjacentBorder = ajaInfo.GetTopInternalBorder(); + currentBorder = info.GetBEndInternalBorder(); + adjacentBorder = ajaInfo.GetBStartInternalBorder(); currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL); segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX); @@ -6120,22 +6159,22 @@ nsTableFrame::CalcBCBorders() info.GetCellEndRowIndex(), colX, segLength, currentBorder.owner, currentBorder.width, startSeg); - info.SetBottomBorderWidths(currentBorder.width); - ajaInfo.SetTopBorderWidths(currentBorder.width); + info.SetBEndBorderWidths(currentBorder.width); + ajaInfo.SetBStartBorderWidths(currentBorder.width); } // update bottom right corner BCCornerInfo& brCorner = bottomCorners[colX + segLength]; brCorner.Update(NS_SIDE_LEFT, currentBorder); } if (!gotRowBorder && 1 == info.mRowSpan && - (ajaInfo.mTopRow || info.mRgAtBottom)) { + (ajaInfo.mStartRow || info.mRgAtEnd)) { //get continuous row/row group border //we need to check the row group's bottom border if this is //the last row in the row group, but only a cell with rowspan=1 //will know whether *this* row is at the bottom - const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup : - nullptr; - info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow); + const nsIFrame* nextRowGroup = + ajaInfo.mRgAtStart ? ajaInfo.mRowGroup : nullptr; + info.SetInnerRowGroupBEndContBCBorder(nextRowGroup, ajaInfo.mStartRow); gotRowBorder = true; } } @@ -6270,8 +6309,6 @@ struct BCHorizontalSeg class BCPaintBorderIterator { public: - - explicit BCPaintBorderIterator(nsTableFrame* aTable); ~BCPaintBorderIterator() { if (mVerInfo) { delete [] mVerInfo; @@ -6297,7 +6334,7 @@ public: nsTableFrame* mTableFirstInFlow; nsTableCellMap* mTableCellMap; nsCellMap* mCellMap; - bool mTableIsLTR; + WritingMode mTableWM; int32_t mColInc; // +1 for ltr -1 for rtl const nsStyleBackground* mTableBgColor; nsTableFrame::RowGroupArray mRowGroups; @@ -6396,14 +6433,16 @@ private: BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) + : mTable(aTable) + , mTableFirstInFlow(static_cast(aTable->FirstInFlow())) + , mTableCellMap(aTable->GetCellMap()) + , mTableWM(aTable->StyleContext()) { - mTable = aTable; mVerInfo = nullptr; - nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); - mTableFirstInFlow = static_cast(mTable->FirstInFlow()); - mTableCellMap = mTable->GetCellMap(); + LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr); // y position of first row in damage area - mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top; + mInitialOffsetY = + mTable->GetPrevInFlow() ? 0 : childAreaOffset.BStart(mTableWM); mNumTableRows = mTable->GetRowCount(); mNumTableCols = mTable->GetColCount(); @@ -6412,9 +6451,7 @@ BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) // initialize to a non existing index mRepeatedHeaderRowIndex = -99; - mTableIsLTR = mTable->StyleVisibility()->mDirection == - NS_STYLE_DIRECTION_LTR; - mColInc = (mTableIsLTR) ? 1 : -1; + mColInc = mTableWM.IsBidiLTR() ? 1 : -1; nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aTable); @@ -6431,19 +6468,19 @@ BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) bool haveIntersect = false; // find startRowIndex, endRowIndex nscoord rowY = mInitialOffsetY; + nsPresContext* presContext = mTable->PresContext(); for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) { nsTableRowGroupFrame* rgFrame = mRowGroups[rgX]; for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { - // conservatively estimate the half border widths outside the row - nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 : - mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); - nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 : - mTable->PresContext()->DevPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); // get the row rect relative to the table rather than the row group nsSize rowSize = rowFrame->GetSize(); if (haveIntersect) { - if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { + // conservatively estimate the half border widths outside the row + nscoord borderHalf = mTable->GetPrevInFlow() ? 0 : + presContext->DevPixelsToAppUnits( + rowFrame->GetBStartBCBorderWidth() + 1); + if (aDirtyRect.YMost() >= rowY - borderHalf) { nsTableRowFrame* fifRow = static_cast(rowFrame->FirstInFlow()); endRowIndex = fifRow->GetRowIndex(); @@ -6451,7 +6488,11 @@ BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) else done = true; } else { - if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { + // conservatively estimate the half border widths outside the row + nscoord borderHalf = mTable->GetNextInFlow() ? 0 : + presContext->DevPixelsToAppUnits( + rowFrame->GetBEndBCBorderWidth() + 1); + if (rowY + rowSize.height + borderHalf >= aDirtyRect.y) { mStartRg = rgFrame; mStartRow = rowFrame; nsTableRowFrame* fifRow = @@ -6481,15 +6522,16 @@ BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) return false; int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol) - nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); - if (mTableIsLTR) { - mInitialOffsetX = childAreaOffset.left; // x position of first col in - // damage area + LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr); + if (mTableWM.IsBidiLTR()) { + // x position of first col in damage area + mInitialOffsetX = childAreaOffset.IStart(mTableWM); leftCol = 0; rightCol = mNumTableCols; } else { // x position of first col in damage area - mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; + mInitialOffsetX = + mTable->GetRect().width - childAreaOffset.IStart(mTableWM); leftCol = mNumTableCols-1; rightCol = -1; } @@ -6502,18 +6544,17 @@ BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) nsSize size = colFrame->GetSize(); if (haveIntersect) { // conservatively estimate the left half border width outside the col - nscoord leftBorderHalf = - mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); - if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { + nscoord istartBorderHalf = mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIStartBorderWidth() + 1); + if (aDirtyRect.XMost() >= x - istartBorderHalf) { endColIndex = colX; } else break; } else { // conservatively estimate the right half border width outside the col - nscoord rightBorderHalf = - mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); - if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { + nscoord iendBorderHalf = + mTable->PresContext()->DevPixelsToAppUnits(colFrame->GetIEndBorderWidth() + 1); + if ((x + size.width + iendBorderHalf) >= aDirtyRect.x) { startColIndex = endColIndex = colX; haveIntersect = true; } @@ -6523,9 +6564,10 @@ BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) } x += size.width; } - if (!mTableIsLTR) { + if (!mTableWM.IsBidiLTR()) { uint32_t temp; - mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; + mInitialOffsetX = + mTable->GetRect().width - childAreaOffset.IStart(mTableWM); temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; for (uint32_t column = 0; column < startColIndex; column++) { nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column); @@ -6572,7 +6614,7 @@ BCPaintBorderIterator::Reset() */ void BCPaintBorderIterator::SetNewData(int32_t aY, - int32_t aX) + int32_t aX) { if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0(); @@ -6960,8 +7002,8 @@ BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, BCPixelSize aHorSegHeight) { // get the border style, color and paint the segment - mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT : - NS_SIDE_LEFT; + LogicalSide side = + aIter.IsDamageAreaRightMost() ? eLogicalSideIEnd : eLogicalSideIStart; int32_t relColIndex = aIter.GetRelativeColIndex(); nsTableColFrame* col = mCol; if (!col) ABORT0(); nsTableCellFrame* cell = mFirstCell; // ??? @@ -6978,7 +7020,7 @@ BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, owner = aIter.mTable; break; case eAjaColGroupOwner: - side = NS_SIDE_RIGHT; + side = eLogicalSideIEnd; if (!aIter.IsTableRightMost() && (relColIndex > 0)) { col = aIter.mVerInfo[relColIndex - 1].mCol; } // and fall through @@ -6988,7 +7030,7 @@ BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, } break; case eAjaColOwner: - side = NS_SIDE_RIGHT; + side = eLogicalSideIEnd; if (!aIter.IsTableRightMost() && (relColIndex > 0)) { col = aIter.mVerInfo[relColIndex - 1].mCol; } // and fall through @@ -7004,21 +7046,21 @@ BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, owner = mFirstRowGroup; break; case eAjaRowOwner: - NS_ASSERTION(false, "program error"); // and fall through + NS_ERROR("program error"); // and fall through case eRowOwner: NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), "row can own border only at table edge"); owner = mFirstRow; break; case eAjaCellOwner: - side = NS_SIDE_RIGHT; + side = eLogicalSideIEnd; cell = mAjaCell; // and fall through case eCellOwner: owner = cell; break; } if (owner) { - ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); + ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color); } BCPixelSize smallHalf, largeHalf; DivideBCBorderSize(mWidth, smallHalf, largeHalf); @@ -7027,10 +7069,12 @@ BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth), mLength); nscoord bottomBevelOffset = (mIsBottomBevel) ? aIter.mTable->PresContext()->DevPixelsToAppUnits(mBottomHorSegHeight) : 0; - mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ? - NS_SIDE_RIGHT : NS_SIDE_LEFT; - mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)? - NS_SIDE_RIGHT : NS_SIDE_LEFT; + mozilla::css::Side bottomBevelSide = + (aHorSegHeight > 0) ^ !aIter.mTableWM.IsBidiLTR() ? + NS_SIDE_RIGHT : NS_SIDE_LEFT; + mozilla::css::Side topBevelSide = + (mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableWM.IsBidiLTR() ? + NS_SIDE_RIGHT : NS_SIDE_LEFT; nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, aIter.mTableBgColor, segRect, appUnitsPerDevPixel, @@ -7073,9 +7117,9 @@ BCHorizontalSeg::BCHorizontalSeg() + */ void BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, - BCBorderOwner aBorderOwner, - BCPixelSize aBottomVerSegWidth, - BCPixelSize aHorSegHeight) + BCBorderOwner aBorderOwner, + BCPixelSize aBottomVerSegWidth, + BCPixelSize aHorSegHeight) { mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; bool bevel = false; @@ -7091,11 +7135,11 @@ BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, aBottomVerSegWidth); nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth, maxVerSegWidth, true, leftBevel, - aIter.mTableIsLTR, aIter.mTable->PresContext()); + aIter.mTableWM.IsBidiLTR(), aIter.mTable->PresContext()); mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; // XXX this assumes that only corners where 2 segments join can be beveled mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; - if (aIter.mTableIsLTR) { + if (aIter.mTableWM.IsBidiLTR()) { mOffsetX += offset; } else { @@ -7129,7 +7173,7 @@ BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter, int32_t relColIndex = aIter.GetRelativeColIndex(); nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth); mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, false, - mIsRightBevel, aIter.mTableIsLTR, aIter.mTable->PresContext()); + mIsRightBevel, aIter.mTableWM.IsBidiLTR(), aIter.mTable->PresContext()); mLength += mEndOffset; mRightBevelOffset = (mIsRightBevel) ? aIter.mTable->PresContext()->DevPixelsToAppUnits(verWidth) : 0; @@ -7146,8 +7190,8 @@ BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, nsRenderingContext& aRenderingContext) { // get the border style, color and paint the segment - mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM : - NS_SIDE_TOP; + LogicalSide side = + aIter.IsDamageAreaBottomMost() ? eLogicalSideBEnd : eLogicalSideBStart; nsIFrame* rg = aIter.mRg; if (!rg) ABORT0(); nsIFrame* row = aIter.mRow; if (!row) ABORT0(); nsIFrame* cell = mFirstCell; @@ -7185,21 +7229,21 @@ BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); break; case eAjaRowGroupOwner: - side = NS_SIDE_BOTTOM; + side = eLogicalSideBEnd; rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg; // and fall through case eRowGroupOwner: owner = rg; break; case eAjaRowOwner: - side = NS_SIDE_BOTTOM; + side = eLogicalSideBEnd; row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow; // and fall through case eRowOwner: owner = row; break; case eAjaCellOwner: - side = NS_SIDE_BOTTOM; + side = eLogicalSideBEnd; // if this is null due to the damage area origin-y > 0, then the border // won't show up anyway cell = mAjaCell; @@ -7209,7 +7253,7 @@ BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, break; } if (owner) { - ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); + ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &style, &color); } BCPixelSize smallHalf, largeHalf; DivideBCBorderSize(mWidth, smallHalf, largeHalf); @@ -7217,7 +7261,7 @@ BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, mOffsetY - aIter.mTable->PresContext()->DevPixelsToAppUnits(largeHalf), mLength, aIter.mTable->PresContext()->DevPixelsToAppUnits(mWidth)); - if (aIter.mTableIsLTR) { + if (aIter.mTableWM.IsBidiLTR()) { nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, aIter.mTableBgColor, segRect, appUnitsPerDevPixel, @@ -7400,7 +7444,7 @@ BCPaintBorderIterator::ResetVerInfo() */ void nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext, - const nsRect& aDirtyRect) + const nsRect& aDirtyRect) { // We first transfer the aDirtyRect into cellmap coordinates to compute which // cell borders need to be painted @@ -7430,7 +7474,8 @@ nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext, } } -bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) +bool +nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) { bool result = false; nsTableCellMap* cellMap = GetCellMap(); @@ -7441,7 +7486,8 @@ bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) return result; } -bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) +bool +nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) { bool result = false; nsTableCellMap* cellMap = GetCellMap(); diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index 4d04322f2a..61f7201542 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -25,6 +25,10 @@ class nsTableRowFrame; class nsTableColGroupFrame; class nsITableLayoutStrategy; class nsStyleContext; +namespace mozilla { +class WritingMode; +class LogicalMargin; +} struct nsTableReflowState; struct BCPropertyData; @@ -123,6 +127,8 @@ enum nsTableColType { class nsTableFrame : public nsContainerFrame { typedef mozilla::image::DrawResult DrawResult; + typedef mozilla::WritingMode WritingMode; + typedef mozilla::LogicalMargin LogicalMargin; public: NS_DECL_QUERYFRAME_TARGET(nsTableFrame) @@ -152,16 +158,16 @@ public: static float GetTwipsToPixels(nsPresContext* aPresContext); // Return true if aParentReflowState.frame or any of its ancestors within - // the containing table have non-auto height. (e.g. pct or fixed height) - static bool AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState); + // the containing table have non-auto bsize. (e.g. pct or fixed bsize) + static bool AncestorsHaveStyleBSize(const nsHTMLReflowState& aParentReflowState); - // See if a special height reflow will occur due to having a pct height when - // the pct height basis may not yet be valid. - static void CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState); + // See if a special bsize reflow will occur due to having a pct bsize when + // the pct bsize basis may not yet be valid. + static void CheckRequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowState); // Notify the frame and its ancestors (up to the containing table) that a special // height reflow will occur. - static void RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState); + static void RequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowState); static void RePositionViews(nsIFrame* aFrame); @@ -206,7 +212,8 @@ public: virtual nsMargin GetUsedMargin() const override; // Get the offset from the border box to the area where the row groups fit - nsMargin GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const; + LogicalMargin GetChildAreaOffset(const WritingMode aWM, + const nsHTMLReflowState* aReflowState) const; /** helper method to find the table parent of any table frame object */ static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame); @@ -246,7 +253,7 @@ public: static nsIFrame* GetFrameAtOrBefore(nsIFrame* aParentFrame, nsIFrame* aPriorChildFrame, nsIAtom* aChildType); - bool IsAutoHeight(); + bool IsAutoBSize(mozilla::WritingMode aWM); /** @return true if aDisplayType represents a rowgroup of any sort * (header, footer, or body) @@ -273,18 +280,18 @@ public: * the table) of the largest segment (?) of border-collapsed border on * the table on each side, or 0 for non border-collapsed tables. */ - nsMargin GetOuterBCBorder() const; + LogicalMargin GetOuterBCBorder(const WritingMode aWM) const; /** Same as above, but only if it's included from the border-box width * of the table. */ - nsMargin GetIncludedOuterBCBorder() const; + LogicalMargin GetIncludedOuterBCBorder(const WritingMode aWM) const; /** Same as above, but only if it's excluded from the border-box width * of the table. This is the area that leaks out into the margin * (or potentially past it, if there is no margin). */ - nsMargin GetExcludedOuterBCBorder() const; + LogicalMargin GetExcludedOuterBCBorder(const WritingMode aWM) const; /** * In quirks mode, the size of the table background is reduced @@ -339,7 +346,7 @@ public: * A copy of nsFrame::ShrinkWidthToFit that calls a different * GetPrefISize, since tables have two different ones. */ - nscoord TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, + nscoord TableShrinkISizeToFit(nsRenderingContext *aRenderingContext, nscoord aWidthInCB); // XXXldb REWRITE THIS COMMENT! @@ -364,7 +371,7 @@ public: void ReflowTable(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nscoord aAvailHeight, + nscoord aAvailBSize, nsIFrame*& aLastChildReflowed, nsReflowStatus& aStatus); @@ -393,8 +400,10 @@ public: virtual nsresult GetFrameName(nsAString& aResult) const override; #endif - /** return the isize of the column at aColIndex */ - int32_t GetColumnISize(int32_t aColIndex); + /** Return the isize of the column at aColIndex. + * This may only be called on the table's first-in-flow. + */ + nscoord GetColumnISizeFromFirstInFlow(int32_t aColIndex); /** Helper to get the column spacing style value. * The argument refers to the space between column aColIndex and column @@ -628,11 +637,12 @@ protected: // (2) notify the table about colgroups or columns with hidden visibility void ReflowColGroups(nsRenderingContext* aRenderingContext); - /** return the width of the table taking into account visibility collapse + /** return the isize of the table taking into account visibility collapse * on columns and colgroups * @param aBorderPadding the border and padding of the table */ - nscoord GetCollapsedWidth(nsMargin aBorderPadding); + nscoord GetCollapsedISize(const WritingMode aWM, + const LogicalMargin& aBorderPadding); /** Adjust the table for visibility.collapse set on rowgroups, rows, @@ -641,7 +651,8 @@ protected: * @param aBorderPadding the border and padding of the table */ void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, - nsMargin aBorderPadding); + const WritingMode aWM, + const LogicalMargin& aBorderPadding); /** FixupPositionedTableParts is called at the end of table reflow to reflow * the absolutely positioned descendants of positioned table parts. This is @@ -673,21 +684,22 @@ private: public: - // calculate the computed height of aFrame including its border and padding given - // its reflow state. - nscoord CalcBorderBoxHeight(const nsHTMLReflowState& aReflowState); + // calculate the computed block-size of aFrame including its border and + // padding given its reflow state. + nscoord CalcBorderBoxBSize(const nsHTMLReflowState& aReflowState); protected: - // update the desired height of this table taking into account the current - // reflow state, the table attributes and the content driven rowgroup heights + // update the desired block-size of this table taking into account the current + // reflow state, the table attributes and the content driven rowgroup bsizes // this function can change the overflow area - void CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize); + void CalcDesiredBSize(const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize); - // The following is a helper for CalcDesiredHeight + // The following is a helper for CalcDesiredBSize - void DistributeHeightToRows(const nsHTMLReflowState& aReflowState, - nscoord aAmount); + void DistributeBSizeToRows(const nsHTMLReflowState& aReflowState, + nscoord aAmount); void PlaceChild(nsTableReflowState& aReflowState, nsIFrame* aKidFrame, @@ -801,8 +813,9 @@ protected: void ExpandBCDamageArea(mozilla::TableArea& aRect) const; - void SetColumnDimensions(nscoord aHeight, - const nsMargin& aReflowState); + void SetColumnDimensions(nscoord aHeight, WritingMode aWM, + const LogicalMargin& aBorderPadding, + nscoord aContainerWidth); int32_t CollectRows(nsIFrame* aFrame, nsTArray& aCollection); @@ -986,7 +999,7 @@ inline nscoord nsTableFrame::GetContinuousLeftBCBorderWidth() const { int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - return BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mBits.mLeftContBCBorder); + return BC_BORDER_END_HALF_COORD(aPixelsToTwips, mBits.mLeftContBCBorder); } inline void nsTableFrame::SetContinuousLeftBCBorderWidth(nscoord aValue) @@ -1001,12 +1014,10 @@ public: explicit nsTableIterator(nsFrameList& aSource); nsIFrame* First(); nsIFrame* Next(); - bool IsLeftToRight(); int32_t Count(); protected: void Init(nsIFrame* aFirstChild); - bool mLeftToRight; nsIFrame* mFirstListChild; nsIFrame* mFirstChild; nsIFrame* mCurrentChild; diff --git a/layout/tables/nsTableOuterFrame.cpp b/layout/tables/nsTableOuterFrame.cpp index a27f27a225..088da77ab7 100644 --- a/layout/tables/nsTableOuterFrame.cpp +++ b/layout/tables/nsTableOuterFrame.cpp @@ -242,11 +242,13 @@ nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext, nsMargin* pCollapsePadding = nullptr; if (aReflowState.frame == InnerTableFrame() && InnerTableFrame()->IsBorderCollapse()) { - collapseBorder = InnerTableFrame()->GetIncludedOuterBCBorder(); + WritingMode wm = aReflowState.GetWritingMode(); + LogicalMargin border = InnerTableFrame()->GetIncludedOuterBCBorder(wm); + collapseBorder = border.GetPhysicalMargin(wm); pCollapseBorder = &collapseBorder; pCollapsePadding = &collapsePadding; } - aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding); + aReflowState.Init(&aPresContext, nullptr, pCollapseBorder, pCollapsePadding); } // get the margin and padding data. nsHTMLReflowState doesn't handle the @@ -266,7 +268,7 @@ nsTableOuterFrame::GetChildMargin(nsPresContext* aPresContext, WritingMode wm = aChildFrame->GetWritingMode(); LogicalSize availSize(wm, aAvailISize, aOuterRS.AvailableSize(wm).BSize(wm)); nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, availSize, - -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, childRS); aMargin = childRS.ComputedLogicalMargin(); @@ -361,23 +363,30 @@ nsTableOuterFrame::GetPrefISize(nsRenderingContext *aRenderingContext) return maxWidth; } -// Compute the margin-box width of aChildFrame given the inputs. If -// aMarginResult is non-null, fill it with the part of the margin-width -// that was contributed by the margin. +// Compute the margin-box inline size of aChildFrame given the inputs. +// If aMarginResult is non-null, fill it with the part of the +// margin-isize that was contributed by the margin. static nscoord -ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext, - nsIFrame *aChildFrame, - nsSize aCBSize, nscoord aAvailableWidth, +ChildShrinkWrapISize(nsRenderingContext *aRenderingContext, + nsIFrame *aChildFrame, WritingMode aWM, + LogicalSize aCBSize, nscoord aAvailableWidth, nscoord *aMarginResult = nullptr) { AutoMaybeDisableFontInflation an(aChildFrame); - nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width); - WritingMode wm = offsets.GetWritingMode(); + // Not sure if it makes sense for different inner frames of a table to + // have orthogonal writing modes, but unless we enforce that + // somewhere, better to make sure that the size we pass to ComputeSize + // is in the child's writing mode. + WritingMode wm = aChildFrame->GetWritingMode(); + LogicalSize cbSize = aCBSize.ConvertTo(wm, aWM); + + // On the other hand, the inline size that we pass to nsCSSOffsetState + // needs to be in the containing block's writing mode. + nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.ISize(aWM)); LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, - wm, LogicalSize(wm, aCBSize), - aAvailableWidth, + wm, cbSize, aAvailableWidth, offsets.ComputedLogicalMargin().Size(wm), offsets.ComputedLogicalBorderPadding().Size(wm) - offsets.ComputedLogicalPadding().Size(wm), @@ -409,54 +418,47 @@ nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, // could be something that is not reflected in our GetMinISize and // GetPrefISize. See bug 349457 for an example. - // XXX The use of aCBSize.GetPhysicalSize(aWM) below is temporary, - // until ChildShrinkWrapWidth is updated and width becomes inlineSize. - // Match the availableWidth logic in Reflow. uint8_t captionSide = GetCaptionSide(); - nscoord width; + nscoord inlineSize; if (captionSide == NO_SIDE) { - width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth); + inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, + aCBSize, kidAvailableWidth); } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { - nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, - mCaptionFrames.FirstChild(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth); - width = capWidth + ChildShrinkWrapWidth(aRenderingContext, - InnerTableFrame(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth - capWidth); + nscoord capISize = ChildShrinkWrapISize(aRenderingContext, + mCaptionFrames.FirstChild(), aWM, + aCBSize, kidAvailableWidth); + inlineSize = capISize + ChildShrinkWrapISize(aRenderingContext, + InnerTableFrame(), aWM, + aCBSize, + kidAvailableWidth - capISize); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { nscoord margin; - width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth, &margin); - nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, - mCaptionFrames.FirstChild(), - aCBSize.GetPhysicalSize(aWM), - width - margin); - if (capWidth > width) - width = capWidth; + inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, + aCBSize, kidAvailableWidth, &margin); + nscoord capISize = ChildShrinkWrapISize(aRenderingContext, + mCaptionFrames.FirstChild(), aWM, + aCBSize, inlineSize - margin); + if (capISize > inlineSize) { + inlineSize = capISize; + } } else { NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); - width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth); - nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, - mCaptionFrames.FirstChild(), - aCBSize.GetPhysicalSize(aWM), - kidAvailableWidth); - if (capWidth > width) - width = capWidth; + inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, + aCBSize, kidAvailableWidth); + nscoord capISize = ChildShrinkWrapISize(aRenderingContext, + mCaptionFrames.FirstChild(), aWM, + aCBSize, kidAvailableWidth); + if (capISize > inlineSize) { + inlineSize = capISize; + } } - return LogicalSize(aWM, width, NS_UNCONSTRAINEDSIZE); + return LogicalSize(aWM, inlineSize, NS_UNCONSTRAINEDSIZE); } uint8_t @@ -692,11 +694,11 @@ nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide, } void -nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, - nsIFrame* aChildFrame, - const nsHTMLReflowState& aOuterRS, - void* aChildRSSpace, - nscoord aAvailISize) +nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, + nsIFrame* aChildFrame, + const nsHTMLReflowState& aOuterRS, + Maybe& aChildRS, + nscoord aAvailISize) { // work around pixel rounding errors, round down to ensure we don't exceed the avail height in WritingMode wm = aChildFrame->GetWritingMode(); @@ -720,21 +722,19 @@ nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, } } LogicalSize availSize(wm, aAvailISize, availBSize); - // create and init the child reflow state, using placement new on - // stack space allocated by the caller, so that the caller can destroy - // it - nsHTMLReflowState &childRS = * new (aChildRSSpace) - nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize, - -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); - InitChildReflowState(*aPresContext, childRS); + // create and init the child reflow state, using passed-in Maybe<>, + // so that caller can use it after we return. + aChildRS.emplace(aPresContext, aOuterRS, aChildFrame, availSize, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); + InitChildReflowState(*aPresContext, *aChildRS); // see if we need to reset top-of-page due to a caption - if (childRS.mFlags.mIsTopOfPage && + if (aChildRS->mFlags.mIsTopOfPage && mCaptionFrames.FirstChild() == aChildFrame) { uint8_t captionSide = GetCaptionSide(); if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { - childRS.mFlags.mIsTopOfPage = false; + aChildRS->mFlags.mIsTopOfPage = false; } } } @@ -782,9 +782,9 @@ nsTableOuterFrame::UpdateReflowMetrics(uint8_t aCaptionSide, void nsTableOuterFrame::Reflow(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aOuterRS, - nsReflowStatus& aStatus) + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aOuterRS, + nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame"); @@ -802,15 +802,8 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, MoveOverflowToChildList(); } - // Use longs to get more-aligned space. - #define LONGS_IN_HTMLRS \ - ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long)) - long captionRSSpace[LONGS_IN_HTMLRS]; - nsHTMLReflowState *captionRS = - static_cast((void*)captionRSSpace); - long innerRSSpace[LONGS_IN_HTMLRS]; - nsHTMLReflowState *innerRS = - static_cast((void*) innerRSSpace); + Maybe captionRS; + Maybe innerRS; nsRect origInnerRect = InnerTableFrame()->GetRect(); nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect(); @@ -833,18 +826,18 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, // We don't have a caption. wm = InnerTableFrame()->GetWritingMode(); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, - innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm)); + innerRS, aOuterRS.ComputedSize(wm).ISize(wm)); } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { // ComputeAutoSize takes care of making side captions small. Compute // the caption's size first, and tell the table to fit in what's left. wm = mCaptionFrames.FirstChild()->GetWritingMode(); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, - captionRSSpace, aOuterRS.ComputedSize(wm).ISize(wm)); + captionRS, aOuterRS.ComputedSize(wm).ISize(wm)); nscoord innerAvailISize = aOuterRS.ComputedSize(wm).ISize(wm) - captionRS->ComputedSizeWithMarginBorderPadding(wm).ISize(wm); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, - innerRSSpace, innerAvailISize); + innerRS, innerAvailISize); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { @@ -858,7 +851,7 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, // it would break 'auto' margins), but this effectively does that. wm = InnerTableFrame()->GetWritingMode(); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, - innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm)); + innerRS, aOuterRS.ComputedSize(wm).ISize(wm)); // It's good that CSS 2.1 says not to include margins, since we // can't, since they already been converted so they exactly // fill the available width (ignoring the margin on one side if @@ -867,7 +860,7 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, nscoord innerBorderWidth = innerRS->ComputedSizeWithBorderPadding(wm).ISize(wm); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, - captionRSSpace, innerBorderWidth); + captionRS, innerBorderWidth); } else { NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, @@ -875,22 +868,23 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, // Size the table and the caption independently. wm = mCaptionFrames.FirstChild()->GetWritingMode(); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, - captionRSSpace, aOuterRS.ComputedSize(wm).ISize(wm)); + captionRS, aOuterRS.ComputedSize(wm).ISize(wm)); wm = InnerTableFrame()->GetWritingMode(); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, - innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm)); + innerRS, aOuterRS.ComputedSize(wm).ISize(wm)); } // First reflow the caption. - nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode()); + Maybe captionMet; nsSize captionSize; nsMargin captionMargin; if (mCaptionFrames.NotEmpty()) { + captionMet.emplace(captionRS->GetWritingMode()); nsReflowStatus capStatus; // don't let the caption cause incomplete OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(), - *captionRS, captionMet, capStatus); - captionSize.width = captionMet.Width(); - captionSize.height = captionMet.Height(); + *captionRS, *captionMet, capStatus); + captionSize.width = captionMet->Width(); + captionSize.height = captionMet->Height(); captionMargin = captionRS->ComputedPhysicalMargin(); // Now that we know the height of the caption, reduce the available height // for the table frame if we are height constrained and the caption is above @@ -936,9 +930,9 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, nsPoint captionOrigin; GetCaptionOrigin(captionSide, containSize, innerSize, innerMargin, captionSize, captionMargin, captionOrigin); - FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet, - captionRS, captionOrigin.x, captionOrigin.y, 0); - captionRS->~nsHTMLReflowState(); + FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, *captionMet, + captionRS.ptr(), captionOrigin.x, captionOrigin.y, 0); + captionRS.reset(); } // XXX If the height is constrained then we need to check whether // everything still fits... @@ -946,9 +940,9 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, nsPoint innerOrigin; GetInnerOrigin(captionSide, containSize, captionSize, captionMargin, innerSize, innerMargin, innerOrigin); - FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS, + FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS.ptr(), innerOrigin.x, innerOrigin.y, 0); - innerRS->~nsHTMLReflowState(); + innerRS.reset(); nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect, origInnerVisualOverflow, innerFirstReflow); diff --git a/layout/tables/nsTableOuterFrame.h b/layout/tables/nsTableOuterFrame.h index d1af2dfea7..265e426281 100644 --- a/layout/tables/nsTableOuterFrame.h +++ b/layout/tables/nsTableOuterFrame.h @@ -6,6 +6,7 @@ #define nsTableOuterFrame_h__ #include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" #include "nscore.h" #include "nsContainerFrame.h" #include "nsCellMap.h" @@ -220,11 +221,11 @@ protected: nsPoint& aOrigin); // reflow the child (caption or innertable frame) - void OuterBeginReflowChild(nsPresContext* aPresContext, - nsIFrame* aChildFrame, - const nsHTMLReflowState& aOuterRS, - void* aChildRSSpace, - nscoord aAvailISize); + void OuterBeginReflowChild(nsPresContext* aPresContext, + nsIFrame* aChildFrame, + const nsHTMLReflowState& aOuterRS, + mozilla::Maybe& aChildRS, + nscoord aAvailISize); void OuterDoReflowChild(nsPresContext* aPresContext, nsIFrame* aChildFrame, diff --git a/layout/tables/nsTablePainter.cpp b/layout/tables/nsTablePainter.cpp index be1d96fa71..88cd427a18 100644 --- a/layout/tables/nsTablePainter.cpp +++ b/layout/tables/nsTablePainter.cpp @@ -12,6 +12,7 @@ #include "nsTablePainter.h" #include "nsCSSRendering.h" #include "nsDisplayList.h" +#include "mozilla/WritingModes.h" /* ~*~ Table Background Painting ~*~ @@ -95,6 +96,7 @@ XXX views are going */ +using namespace mozilla; using namespace mozilla::image; TableBackgroundPainter::TableBackgroundData::TableBackgroundData() @@ -206,6 +208,7 @@ TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame, TableBackgroundData tableData(aTableFrame); tableData.mRect.MoveTo(0,0); //using table's coords tableData.mRect.Deflate(aDeflate); + WritingMode wm = aTableFrame->GetWritingMode(); if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) { if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) { //only handle non-degenerate tables; we need a more robust BC model @@ -217,13 +220,14 @@ TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame, } border.right = tempBorder.right; - aLastRowGroup->GetContinuousBCBorderWidth(tempBorder); - border.bottom = tempBorder.bottom; + LogicalMargin logBorder(wm); + aLastRowGroup->GetContinuousBCBorderWidth(wm, logBorder); + border.bottom = logBorder.Bottom(wm); nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow(); if (rowFrame) { - rowFrame->GetContinuousBCBorderWidth(tempBorder); - border.top = tempBorder.top; + rowFrame->GetContinuousBCBorderWidth(wm, logBorder); + border.top = logBorder.Top(wm); } border.left = aTableFrame->GetContinuousLeftBCBorderWidth(); @@ -385,21 +389,22 @@ TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame, MOZ_ASSERT(aFrame, "null frame"); nsTableRowFrame* firstRow = aFrame->GetFirstRow(); + WritingMode wm = aFrame->GetWritingMode(); /* Load row group data */ if (aPassThrough) { aRowGroupBGData.MakeInvisible(); } else { if (mIsBorderCollapse && aRowGroupBGData.ShouldSetBCBorder()) { - nsMargin border; + LogicalMargin border(wm); if (firstRow) { - //pick up first row's top border (= rg top border) - firstRow->GetContinuousBCBorderWidth(border); - /* (row group doesn't store its top border) */ + //pick up first row's bstart border (= rg bstart border) + firstRow->GetContinuousBCBorderWidth(wm, border); + /* (row group doesn't store its bstart border) */ } //overwrite sides+bottom borders with rg's own - aFrame->GetContinuousBCBorderWidth(border); - aRowGroupBGData.SetBCBorder(border); + aFrame->GetContinuousBCBorderWidth(wm, border); + aRowGroupBGData.SetBCBorder(border.GetPhysicalMargin(wm)); } aPassThrough = !aRowGroupBGData.IsVisible(); } @@ -482,23 +487,24 @@ TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame, MOZ_ASSERT(aFrame, "null frame"); /* Load row data */ + WritingMode wm = aFrame->GetWritingMode(); if (aPassThrough) { aRowBGData.MakeInvisible(); } else { if (mIsBorderCollapse && aRowBGData.ShouldSetBCBorder()) { - nsMargin border; + LogicalMargin border(wm); nsTableRowFrame* nextRow = aFrame->GetNextRow(); if (nextRow) { //outer top below us is inner bottom for us - border.bottom = nextRow->GetOuterTopContBCBorderWidth(); + border.BEnd(wm) = nextRow->GetOuterBStartContBCBorderWidth(); } else { //acquire rg's bottom border nsTableRowGroupFrame* rowGroup = static_cast(aFrame->GetParent()); - rowGroup->GetContinuousBCBorderWidth(border); + rowGroup->GetContinuousBCBorderWidth(wm, border); } //get the rest of the borders; will overwrite all but bottom - aFrame->GetContinuousBCBorderWidth(border); + aFrame->GetContinuousBCBorderWidth(wm, border); - aRowBGData.SetBCBorder(border); + aRowBGData.SetBCBorder(border.GetPhysicalMargin(wm)); } aPassThrough = !aRowBGData.IsVisible(); } diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index 111e4f8eff..868b03e261 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -34,38 +34,38 @@ struct nsTableCellReflowState : public nsHTMLReflowState const LogicalSize& aAvailableSpace, uint32_t aFlags = 0) : nsHTMLReflowState(aPresContext, aParentReflowState, aFrame, - aAvailableSpace, -1, -1, aFlags) + aAvailableSpace, nullptr, aFlags) { } - void FixUp(const nsSize& aAvailSpace); + void FixUp(const LogicalSize& aAvailSpace); }; -void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) +void nsTableCellReflowState::FixUp(const LogicalSize& aAvailSpace) { // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base - NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.width, - "have unconstrained width; this should only result from " - "very large sizes, not attempts at intrinsic width " + NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.ISize(mWritingMode), + "have unconstrained inline-size; this should only result from " + "very large sizes, not attempts at intrinsic inline size " "calculation"); - if (NS_UNCONSTRAINEDSIZE != ComputedWidth()) { - nscoord computedWidth = - aAvailSpace.width - mComputedBorderPadding.LeftRight(); - computedWidth = std::max(0, computedWidth); - SetComputedWidth(computedWidth); + if (NS_UNCONSTRAINEDSIZE != ComputedISize()) { + nscoord computedISize = aAvailSpace.ISize(mWritingMode) - + ComputedLogicalBorderPadding().IStartEnd(mWritingMode); + computedISize = std::max(0, computedISize); + SetComputedISize(computedISize); } - if (NS_UNCONSTRAINEDSIZE != ComputedHeight() && - NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { - nscoord computedHeight = - aAvailSpace.height - mComputedBorderPadding.TopBottom(); - computedHeight = std::max(0, computedHeight); - SetComputedHeight(computedHeight); + if (NS_UNCONSTRAINEDSIZE != ComputedBSize() && + NS_UNCONSTRAINEDSIZE != aAvailSpace.BSize(mWritingMode)) { + nscoord computedBSize = aAvailSpace.BSize(mWritingMode) - + ComputedLogicalBorderPadding().BStartEnd(mWritingMode); + computedBSize = std::max(0, computedBSize); + SetComputedBSize(computedBSize); } } void -nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext, - const nsSize& aAvailSize, +nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext, + const LogicalSize& aAvailSize, bool aBorderCollapse, nsTableCellReflowState& aReflowState) { @@ -75,44 +75,46 @@ nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext, // we only reflow cells, so don't need to check frame type nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame; if (bcCellFrame) { - pCollapseBorder = bcCellFrame->GetBorderWidth(collapseBorder); + WritingMode wm = GetWritingMode(); + collapseBorder = bcCellFrame->GetBorderWidth(wm).GetPhysicalMargin(wm); + pCollapseBorder = &collapseBorder; } } - aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder); + aReflowState.Init(&aPresContext, nullptr, pCollapseBorder); aReflowState.FixUp(aAvailSize); } void -nsTableRowFrame::SetFixedHeight(nscoord aValue) +nsTableRowFrame::SetFixedBSize(nscoord aValue) { - nscoord height = std::max(0, aValue); - if (HasFixedHeight()) { - if (height > mStyleFixedHeight) { - mStyleFixedHeight = height; + nscoord bsize = std::max(0, aValue); + if (HasFixedBSize()) { + if (bsize > mStyleFixedBSize) { + mStyleFixedBSize = bsize; } } else { - mStyleFixedHeight = height; - if (height > 0) { - SetHasFixedHeight(true); + mStyleFixedBSize = bsize; + if (bsize > 0) { + SetHasFixedBSize(true); } } } void -nsTableRowFrame::SetPctHeight(float aPctValue, - bool aForce) +nsTableRowFrame::SetPctBSize(float aPctValue, + bool aForce) { - nscoord height = std::max(0, NSToCoordRound(aPctValue * 100.0f)); - if (HasPctHeight()) { - if ((height > mStylePctHeight) || aForce) { - mStylePctHeight = height; + nscoord bsize = std::max(0, NSToCoordRound(aPctValue * 100.0f)); + if (HasPctBSize()) { + if ((bsize > mStylePctBSize) || aForce) { + mStylePctBSize = bsize; } } else { - mStylePctHeight = height; - if (height > 0) { - SetHasPctHeight(true); + mStylePctBSize = bsize; + if (bsize > 0) { + SetHasPctBSize(true); } } } @@ -127,7 +129,7 @@ nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) { mBits.mRowIndex = mBits.mFirstInserted = 0; - ResetHeight(0); + ResetBSize(0); } nsTableRowFrame::~nsTableRowFrame() @@ -180,8 +182,8 @@ nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) } void -nsTableRowFrame::AppendFrames(ChildListID aListID, - nsFrameList& aFrameList) +nsTableRowFrame::AppendFrames(ChildListID aListID, + nsFrameList& aFrameList) { NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); @@ -192,7 +194,8 @@ nsTableRowFrame::AppendFrames(ChildListID aListID, nsTableFrame* tableFrame = GetTableFrame(); for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) { nsIFrame *childFrame = e.get(); - NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); + NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()), + "Not a table cell frame/pseudo frame construction failure"); tableFrame->AppendCell(static_cast(*childFrame), GetRowIndex()); } @@ -203,9 +206,9 @@ nsTableRowFrame::AppendFrames(ChildListID aListID, void -nsTableRowFrame::InsertFrames(ChildListID aListID, - nsIFrame* aPrevFrame, - nsFrameList& aFrameList) +nsTableRowFrame::InsertFrames(ChildListID aListID, + nsIFrame* aPrevFrame, + nsFrameList& aFrameList) { NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, @@ -221,7 +224,8 @@ nsTableRowFrame::InsertFrames(ChildListID aListID, nsTArray cellChildren; for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) { nsIFrame *childFrame = e.get(); - NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); + NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()), + "Not a table cell frame/pseudo frame construction failure"); cellChildren.AppendElement(static_cast(childFrame)); } // insert the cells into the cell map @@ -237,8 +241,8 @@ nsTableRowFrame::InsertFrames(ChildListID aListID, } void -nsTableRowFrame::RemoveFrame(ChildListID aListID, - nsIFrame* aOldFrame) +nsTableRowFrame::RemoveFrame(ChildListID aListID, + nsIFrame* aOldFrame) { NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); @@ -277,22 +281,23 @@ nsTableRowFrame::GetUsedPadding() const } nscoord -GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, - nsTableFrame& aTableFrame) +GetBSizeOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, + nsTableFrame& aTableFrame, + const WritingMode aWM) { - nscoord height = 0; + nscoord bsize = 0; int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame); - // add in height of rows spanned beyond the 1st one + // add in bsize of rows spanned beyond the 1st one nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling(); for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) { if (nsGkAtoms::tableRowFrame == nextRow->GetType()) { - height += nextRow->GetSize().height; + bsize += nextRow->BSize(aWM); rowX++; } - height += aTableFrame.GetRowSpacing(rowX); + bsize += aTableFrame.GetRowSpacing(rowX); nextRow = nextRow->GetNextSibling(); } - return height; + return bsize; } nsTableCellFrame* @@ -315,7 +320,7 @@ nsTableRowFrame::GetFirstCell() void nsTableRowFrame::DidResize() { - // Resize and re-align the cell frames based on our row height + // Resize and re-align the cell frames based on our row bsize nsTableFrame* tableFrame = GetTableFrame(); nsTableIterator iter(*this); nsIFrame* childFrame = iter.First(); @@ -328,29 +333,31 @@ nsTableRowFrame::DidResize() while (childFrame) { nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); if (cellFrame) { - nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame); + nscoord cellBSize = BSize(wm) + + GetBSizeOfRowsSpannedBelowFirst(*cellFrame, *tableFrame, wm); - // resize the cell's height - nsRect cellRect = cellFrame->GetRect(); + // resize the cell's bsize + LogicalSize cellSize = cellFrame->GetLogicalSize(wm); nsRect cellVisualOverflow = cellFrame->GetVisualOverflowRect(); - if (cellRect.height != cellHeight) - { - cellFrame->SetSize(nsSize(cellRect.width, cellHeight)); - nsTableFrame::InvalidateTableFrame(cellFrame, cellRect, + if (cellSize.BSize(wm) != cellBSize) { + cellSize.BSize(wm) = cellBSize; + nsRect cellOldRect = cellFrame->GetRect(); + cellFrame->SetSize(wm, cellSize); + nsTableFrame::InvalidateTableFrame(cellFrame, cellOldRect, cellVisualOverflow, false); } - // realign cell content based on the new height. We might be able to - // skip this if the height didn't change... maybe. Hard to tell. - cellFrame->VerticallyAlignChild(mMaxCellAscent); + // realign cell content based on the new bsize. We might be able to + // skip this if the bsize didn't change... maybe. Hard to tell. + cellFrame->BlockDirAlignChild(wm, mMaxCellAscent); // Always store the overflow, even if the height didn't change, since // we'll lose part of our overflow area otherwise. ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame); // Note that if the cell's *content* needs to change in response - // to this height, it will get a special height reflow. + // to this height, it will get a special bsize reflow. } // Get the next child childFrame = iter.Next(); @@ -370,10 +377,11 @@ nscoord nsTableRowFrame::GetMaxCellAscent() const return mMaxCellAscent; } -nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWritingMode) +nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWM) { - if(mMaxCellAscent) + if (mMaxCellAscent) { return mMaxCellAscent; + } // If we don't have a baseline on any of the cells we go for the lowest // content edge of the inner block frames. @@ -397,40 +405,44 @@ nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWritingMode) nsTableIterator iter(*this); nsIFrame* childFrame = iter.First(); nscoord ascent = 0; + nscoord containerWidth = GetRect().width; while (childFrame) { if (IS_TABLE_CELL(childFrame->GetType())) { nsIFrame* firstKid = childFrame->GetFirstPrincipalChild(); - ascent = std::max(ascent, firstKid->GetNormalRect().YMost()); + ascent = std::max(ascent, + LogicalRect(aWM, firstKid->GetNormalRect(), + containerWidth).BEnd(aWM)); } // Get the next child childFrame = iter.Next(); } return ascent; } + nscoord -nsTableRowFrame::GetHeight(nscoord aPctBasis) const +nsTableRowFrame::GetBSize(nscoord aPctBasis) const { - nscoord height = 0; - if ((aPctBasis > 0) && HasPctHeight()) { - height = NSToCoordRound(GetPctHeight() * (float)aPctBasis); + nscoord bsize = 0; + if ((aPctBasis > 0) && HasPctBSize()) { + bsize = NSToCoordRound(GetPctBSize() * (float)aPctBasis); } - if (HasFixedHeight()) { - height = std::max(height, GetFixedHeight()); + if (HasFixedBSize()) { + bsize = std::max(bsize, GetFixedBSize()); } - return std::max(height, GetContentHeight()); + return std::max(bsize, GetContentBSize()); } void -nsTableRowFrame::ResetHeight(nscoord aFixedHeight) +nsTableRowFrame::ResetBSize(nscoord aFixedBSize) { - SetHasFixedHeight(false); - SetHasPctHeight(false); - SetFixedHeight(0); - SetPctHeight(0); - SetContentHeight(0); + SetHasFixedBSize(false); + SetHasPctBSize(false); + SetFixedBSize(0); + SetPctBSize(0); + SetContentBSize(0); - if (aFixedHeight > 0) { - SetFixedHeight(aFixedHeight); + if (aFixedBSize > 0) { + SetFixedBSize(aFixedBSize); } mMaxCellAscent = 0; @@ -438,28 +450,29 @@ nsTableRowFrame::ResetHeight(nscoord aFixedHeight) } void -nsTableRowFrame::UpdateHeight(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame, - nsTableCellFrame* aCellFrame) +nsTableRowFrame::UpdateBSize(nscoord aBSize, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame, + nsTableCellFrame* aCellFrame) { if (!aTableFrame || !aCellFrame) { NS_ASSERTION(false , "invalid call"); return; } - if (aHeight != NS_UNCONSTRAINEDSIZE) { + if (aBSize != NS_UNCONSTRAINEDSIZE) { if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters - if (GetHeight() < aHeight) { + if (GetBSize() < aBSize) { int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame); if (rowSpan == 1) { - SetContentHeight(aHeight); + SetContentBSize(aBSize); } } } - else { // the alignment on the baseline can change the height - NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call"); + else { // the alignment on the baseline can change the bsize + NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && + (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call"); // see if this is a long ascender if (mMaxCellAscent < aAscent) { mMaxCellAscent = aAscent; @@ -471,28 +484,30 @@ nsTableRowFrame::UpdateHeight(nscoord aHeight, mMaxCellDescent = aDescent; } } - // keep the tallest height in sync - if (GetHeight() < mMaxCellAscent + mMaxCellDescent) { - SetContentHeight(mMaxCellAscent + mMaxCellDescent); + // keep the tallest bsize in sync + if (GetBSize() < mMaxCellAscent + mMaxCellDescent) { + SetContentBSize(mMaxCellAscent + mMaxCellDescent); } } } } nscoord -nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) +nsTableRowFrame::CalcBSize(const nsHTMLReflowState& aReflowState) { nsTableFrame* tableFrame = GetTableFrame(); - nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) - ? 0 : aReflowState.ComputedHeight(); - ResetHeight(computedHeight); + nscoord computedBSize = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedBSize()) + ? 0 : aReflowState.ComputedBSize(); + ResetBSize(computedBSize); + WritingMode wm = aReflowState.GetWritingMode(); const nsStylePosition* position = StylePosition(); - if (position->mHeight.ConvertsToLength()) { - SetFixedHeight(nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0)); + const nsStyleCoord& bsizeStyleCoord = position->BSize(wm); + if (bsizeStyleCoord.ConvertsToLength()) { + SetFixedBSize(nsRuleNode::ComputeCoordPercentCalc(bsizeStyleCoord, 0)); } - else if (eStyleUnit_Percent == position->mHeight.GetUnit()) { - SetPctHeight(position->mHeight.GetPercentValue()); + else if (eStyleUnit_Percent == bsizeStyleCoord.GetUnit()) { + SetPctBSize(bsizeStyleCoord.GetPercentValue()); } // calc() with percentages is treated like 'auto' on table rows. @@ -500,22 +515,22 @@ nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) kidFrame = kidFrame->GetNextSibling()) { nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); if (cellFrame) { - WritingMode wm = cellFrame->GetWritingMode(); + MOZ_ASSERT(cellFrame->GetWritingMode() == wm); LogicalSize desSize = cellFrame->GetDesiredSize(); - if ((NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) && !GetPrevInFlow()) { - CalculateCellActualHeight(cellFrame, desSize.BSize(wm)); + if ((NS_UNCONSTRAINEDSIZE == aReflowState.AvailableBSize()) && !GetPrevInFlow()) { + CalculateCellActualBSize(cellFrame, desSize.BSize(wm), wm); } - // height may have changed, adjust descent to absorb any excess difference + // bsize may have changed, adjust descent to absorb any excess difference nscoord ascent; if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) ascent = desSize.BSize(wm); else ascent = cellFrame->GetCellBaseline(); nscoord descent = desSize.BSize(wm) - ascent; - UpdateHeight(desSize.BSize(wm), ascent, descent, tableFrame, cellFrame); + UpdateBSize(desSize.BSize(wm), ascent, descent, tableFrame, cellFrame); } } - return GetHeight(); + return GetBSize(); } /** @@ -527,7 +542,7 @@ nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) class nsDisplayTableRowBackground : public nsDisplayTableItem { public: nsDisplayTableRowBackground(nsDisplayListBuilder* aBuilder, - nsTableRowFrame* aFrame) : + nsTableRowFrame* aFrame) : nsDisplayTableItem(aBuilder, aFrame) { MOZ_COUNT_CTOR(nsDisplayTableRowBackground); } @@ -538,13 +553,13 @@ public: #endif virtual void Paint(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx) override; + nsRenderingContext* aCtx) override; NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND) }; void nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx) + nsRenderingContext* aCtx) { auto rowFrame = static_cast(mFrame); TableBackgroundPainter painter(rowFrame->GetTableFrame(), @@ -597,23 +612,25 @@ nsTableRowFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) cons return skip; } -// Calculate the cell's actual height given its pass2 height. -// Takes into account the specified height (in the style). -// Modifies the desired height that is passed in. +// Calculate the cell's actual bsize given its pass2 bsize. +// Takes into account the specified bsize (in the style). +// Modifies the desired bsize that is passed in. nsresult -nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame, - nscoord& aDesiredHeight) +nsTableRowFrame::CalculateCellActualBSize(nsTableCellFrame* aCellFrame, + nscoord& aDesiredBSize, + WritingMode aWM) { - nscoord specifiedHeight = 0; + nscoord specifiedBSize = 0; - // Get the height specified in the style information + // Get the bsize specified in the style information const nsStylePosition* position = aCellFrame->StylePosition(); int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame); - switch (position->mHeight.GetUnit()) { + const nsStyleCoord& bsizeStyleCoord = position->BSize(aWM); + switch (bsizeStyleCoord.GetUnit()) { case eStyleUnit_Calc: { - if (position->mHeight.CalcHasPercent()) { + if (bsizeStyleCoord.CalcHasPercent()) { // Treat this like "auto" break; } @@ -621,18 +638,20 @@ nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame, } case eStyleUnit_Coord: { nscoord outsideBoxSizing = 0; - // In quirks mode, table cell width should be content-box, but height + // In quirks mode, table cell isize should be content-box, but bsize // should be border-box. // Because of this historic anomaly, we do not use quirk.css - // (since we can't specify one value of box-sizing for width and another - // for height) + // (since we can't specify one value of box-sizing for isize and another + // for bsize) if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) { switch (position->mBoxSizing) { case NS_STYLE_BOX_SIZING_CONTENT: - outsideBoxSizing = aCellFrame->GetUsedBorderAndPadding().TopBottom(); + outsideBoxSizing = + aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM); break; case NS_STYLE_BOX_SIZING_PADDING: - outsideBoxSizing = aCellFrame->GetUsedBorder().TopBottom(); + outsideBoxSizing = + aCellFrame->GetLogicalUsedBorder(aWM).BStartEnd(aWM); break; default: // NS_STYLE_BOX_SIZING_BORDER @@ -640,18 +659,21 @@ nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame, } } - specifiedHeight = - nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0) + - outsideBoxSizing; + specifiedBSize = + nsRuleNode::ComputeCoordPercentCalc(bsizeStyleCoord, 0) + + outsideBoxSizing; - if (1 == rowSpan) - SetFixedHeight(specifiedHeight); + if (1 == rowSpan) { + SetFixedBSize(specifiedBSize); + } break; } case eStyleUnit_Percent: { - if (1 == rowSpan) - SetPctHeight(position->mHeight.GetPercentValue()); - // pct heights are handled when all of the cells are finished, so don't set specifiedHeight + if (1 == rowSpan) { + SetPctBSize(bsizeStyleCoord.GetPercentValue()); + } + // pct bsizes are handled when all of the cells are finished, + // so don't set specifiedBSize break; } case eStyleUnit_Auto: @@ -659,33 +681,38 @@ nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame, break; } - // If the specified height is greater than the desired height, then use the specified height - if (specifiedHeight > aDesiredHeight) - aDesiredHeight = specifiedHeight; + // If the specified bsize is greater than the desired bsize, + // then use the specified bsize + if (specifiedBSize > aDesiredBSize) { + aDesiredBSize = specifiedBSize; + } return NS_OK; } -// Calculates the available width for the table cell based on the known -// column widths taking into account column spans and column spacing +// Calculates the available isize for the table cell based on the known +// column isizes taking into account column spans and column spacing static nscoord -CalcAvailWidth(nsTableFrame& aTableFrame, +CalcAvailISize(nsTableFrame& aTableFrame, nsTableCellFrame& aCellFrame) { - nscoord cellAvailWidth = 0; + nscoord cellAvailISize = 0; int32_t colIndex; aCellFrame.GetColIndex(colIndex); int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame); NS_ASSERTION(colspan > 0, "effective colspan should be positive"); + nsTableFrame* fifTable = + static_cast(aTableFrame.FirstInFlow()); for (int32_t spanX = 0; spanX < colspan; spanX++) { - cellAvailWidth += aTableFrame.GetColumnISize(colIndex + spanX); + cellAvailISize += + fifTable->GetColumnISizeFromFirstInFlow(colIndex + spanX); if (spanX > 0 && aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) { - cellAvailWidth += aTableFrame.GetColSpacing(colIndex + spanX - 1); + cellAvailISize += aTableFrame.GetColSpacing(colIndex + spanX - 1); } } - return cellAvailWidth; + return cellAvailISize; } nscoord @@ -693,81 +720,57 @@ GetSpaceBetween(int32_t aPrevColIndex, int32_t aColIndex, int32_t aColSpan, nsTableFrame& aTableFrame, - bool aIsLeftToRight, bool aCheckVisibility) { nscoord space = 0; int32_t colX; - if (aIsLeftToRight) { - for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) { - bool isCollapsed = false; - if (!aCheckVisibility) { - space += aTableFrame.GetColumnISize(colX); - } - else { - nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); - const nsStyleVisibility* colVis = colFrame->StyleVisibility(); - bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); - nsIFrame* cgFrame = colFrame->GetParent(); - const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); - bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == - groupVis->mVisible); - isCollapsed = collapseCol || collapseGroup; - if (!isCollapsed) - space += aTableFrame.GetColumnISize(colX); - } - if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { - space += aTableFrame.GetColSpacing(colX - 1); - } + nsTableFrame* fifTable = + static_cast(aTableFrame.FirstInFlow()); + for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) { + bool isCollapsed = false; + if (!aCheckVisibility) { + space += fifTable->GetColumnISizeFromFirstInFlow(colX); } - } - else { - int32_t lastCol = aColIndex + aColSpan - 1; - for (colX = aPrevColIndex - 1; colX > lastCol; colX--) { - bool isCollapsed = false; - if (!aCheckVisibility) { - space += aTableFrame.GetColumnISize(colX); - } - else { - nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); - const nsStyleVisibility* colVis = colFrame->StyleVisibility(); - bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); - nsIFrame* cgFrame = colFrame->GetParent(); - const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); - bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == - groupVis->mVisible); - isCollapsed = collapseCol || collapseGroup; - if (!isCollapsed) - space += aTableFrame.GetColumnISize(colX); - } - if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { - space += aTableFrame.GetColSpacing(colX - 1); - } + else { + nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); + const nsStyleVisibility* colVis = colFrame->StyleVisibility(); + bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); + nsIFrame* cgFrame = colFrame->GetParent(); + const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); + bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == + groupVis->mVisible); + isCollapsed = collapseCol || collapseGroup; + if (!isCollapsed) + space += fifTable->GetColumnISizeFromFirstInFlow(colX); + } + if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { + space += aTableFrame.GetColSpacing(colX - 1); } } return space; } -// subtract the heights of aRow's prev in flows from the unpaginated height +// subtract the bsizes of aRow's prev in flows from the unpaginated bsize static -nscoord CalcHeightFromUnpaginatedHeight(nsPresContext* aPresContext, - nsTableRowFrame& aRow) +nscoord CalcBSizeFromUnpaginatedBSize(nsPresContext* aPresContext, + nsTableRowFrame& aRow, + WritingMode aWM) { - nscoord height = 0; + nscoord bsize = 0; nsTableRowFrame* firstInFlow = static_cast(aRow.FirstInFlow()); - if (firstInFlow->HasUnpaginatedHeight()) { - height = firstInFlow->GetUnpaginatedHeight(aPresContext); + if (firstInFlow->HasUnpaginatedBSize()) { + bsize = firstInFlow->GetUnpaginatedBSize(aPresContext); for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) { - height -= prevInFlow->GetSize().height; + bsize -= prevInFlow->BSize(aWM); } } - return std::max(height, 0); + return std::max(bsize, 0); } void -nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, +nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame, @@ -775,7 +778,7 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, { aStatus = NS_FRAME_COMPLETE; - // XXXldb Should we be checking constrained height instead? + // XXXldb Should we be checking constrained bsize instead? const bool isPaginated = aPresContext->IsPaginated(); const bool borderCollapse = aTableFrame.IsBorderCollapse(); @@ -783,14 +786,22 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, nsTableIterator iter(*this); // remember the col index of the previous cell to handle rowspans into this row - int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount(); + int32_t firstPrevColIndex = -1; int32_t prevColIndex = firstPrevColIndex; - nscoord x = 0; // running total of children x offset + nscoord iCoord = 0; // running total of children inline-coord offset - // This computes the max of all cell heights - nscoord cellMaxHeight = 0; + // This computes the max of all cell bsizes + nscoord cellMaxBSize = 0; // Reflow each of our existing cell frames + WritingMode wm = aReflowState.GetWritingMode(); + nscoord containerWidth = aReflowState.ComputedWidth(); + if (containerWidth == NS_UNCONSTRAINEDSIZE) { + containerWidth = 0; // cell positions will not yet be correct + } else { + containerWidth += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); + } + for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); if (!cellFrame) { @@ -802,7 +813,7 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, kidReflowState(aPresContext, aReflowState, kidFrame, LogicalSize(kidFrame->GetWritingMode(), 0, 0), nsHTMLReflowState::CALLER_WILL_INIT); - InitChildReflowState(*aPresContext, nsSize(0,0), false, kidReflowState); + InitChildReflowState(*aPresContext, LogicalSize(wm), false, kidReflowState); nsHTMLReflowMetrics desiredSize(aReflowState); nsReflowStatus status; ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status); @@ -816,19 +827,19 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, if (!aReflowState.ShouldReflowAllKids() && !aTableFrame.IsGeometryDirty() && !NS_SUBTREE_DIRTY(kidFrame)) { - if (!aReflowState.mFlags.mSpecialHeightReflow) + if (!aReflowState.mFlags.mSpecialBSizeReflow) doReflowChild = false; } - else if ((NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { - // We don't reflow a rowspan >1 cell here with a constrained height. + else if ((NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize())) { + // We don't reflow a rowspan >1 cell here with a constrained bsize. // That happens in nsTableRowGroupFrame::SplitSpanningCells. if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) { doReflowChild = false; } } - if (aReflowState.mFlags.mSpecialHeightReflow) { + if (aReflowState.mFlags.mSpecialBSizeReflow) { if (!isPaginated && !(cellFrame->GetStateBits() & - NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { + NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { continue; } } @@ -838,76 +849,80 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame); // If the adjacent cell is in a prior row (because of a rowspan) add in the space - if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || - (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) { - x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, - iter.IsLeftToRight(), false); + if (prevColIndex != (cellColIndex - 1)) { + iCoord += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, + false); } // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into - prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex; + prevColIndex = cellColIndex + (cellColSpan - 1); // Reflow the child frame nsRect kidRect = kidFrame->GetRect(); - nsPoint origKidNormalPosition = kidFrame->GetNormalPosition(); - MOZ_ASSERT(origKidNormalPosition.y == 0); + LogicalPoint origKidNormalPosition = + kidFrame->GetLogicalNormalPosition(wm, containerWidth); + // All cells' no-relative-positioning position should be snapped to the + // row's bstart edge. + // XXX This currently doesn't hold in vertical-rl mode, which is probably + // a bug, but to enable progress with testing I'm temporarily neutering + // the assertion in that case (bug 1174711). + MOZ_ASSERT(origKidNormalPosition.B(wm) == 0 || wm.IsVerticalRL()); nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect(); - nsPoint kidPosition(x, 0); + LogicalPoint kidPosition(wm, iCoord, 0); bool firstReflow = (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; if (doReflowChild) { - // Calculate the available width for the table cell using the known column widths - nscoord availCellWidth = - CalcAvailWidth(aTableFrame, *cellFrame); + // Calculate the available isize for the table cell using the known + // column isizes + nscoord availCellISize = CalcAvailISize(aTableFrame, *cellFrame); Maybe kidReflowState; nsHTMLReflowMetrics desiredSize(aReflowState); - // If the avail width is not the same as last time we reflowed the cell or + // If the avail isize is not the same as last time we reflowed the cell or // the cell wants to be bigger than what was available last time or // it is a style change reflow or we are printing, then we must reflow the // cell. Otherwise we can skip the reflow. // XXXldb Why is this condition distinct from doReflowChild above? - WritingMode rowWM = aReflowState.GetWritingMode(); - WritingMode cellWM = cellFrame->GetWritingMode(); + WritingMode wm = aReflowState.GetWritingMode(); + NS_ASSERTION(cellFrame->GetWritingMode() == wm, + "expected consistent writing-mode within table"); LogicalSize cellDesiredSize = cellFrame->GetDesiredSize(); - if ((availCellWidth != cellFrame->GetPriorAvailWidth()) || - (cellDesiredSize.ISize(cellWM) > cellFrame->GetPriorAvailWidth()) || + if ((availCellISize != cellFrame->GetPriorAvailISize()) || + (cellDesiredSize.ISize(wm) > cellFrame->GetPriorAvailISize()) || (GetStateBits() & NS_FRAME_IS_DIRTY) || isPaginated || NS_SUBTREE_DIRTY(cellFrame) || // See if it needs a special reflow, or if it had one that we need to undo. - (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) || - HasPctHeight()) { - // Reflow the cell to fit the available width, height - // XXX The old IR_ChildIsDirty code used availCellWidth here. - nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight()); + (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) || + HasPctBSize()) { + // Reflow the cell to fit the available isize, bsize + // XXX The old IR_ChildIsDirty code used availCellISize here. + LogicalSize kidAvailSize(wm, availCellISize, aReflowState.AvailableBSize()); // Reflow the child kidReflowState.emplace(aPresContext, aReflowState, kidFrame, - LogicalSize(kidFrame->GetWritingMode(), - kidAvailSize), + kidAvailSize, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse, *kidReflowState); nsReflowStatus status; ReflowChild(kidFrame, aPresContext, desiredSize, *kidReflowState, - x, 0, 0, status); + wm, kidPosition, containerWidth, 0, status); // allow the table to determine if/how the table needs to be rebalanced // If any of the cells are not complete, then we're not complete if (NS_FRAME_IS_NOT_COMPLETE(status)) { aStatus = NS_FRAME_NOT_COMPLETE; } - } - else { - if (x != origKidNormalPosition.x) { + } else { + if (iCoord != origKidNormalPosition.I(wm)) { kidFrame->InvalidateFrameSubtree(); } - desiredSize.SetSize(cellWM, cellDesiredSize); + desiredSize.SetSize(wm, cellDesiredSize); desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas(); // if we are in a floated table, our position is not yet established, so we cannot reposition our views @@ -920,117 +935,145 @@ nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, } } - if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { + if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableBSize()) { if (!GetPrevInFlow()) { - // Calculate the cell's actual height given its pass2 height. This - // function takes into account the specified height (in the style) - CalculateCellActualHeight(cellFrame, desiredSize.Height()); + // Calculate the cell's actual bsize given its pass2 bsize. This + // function takes into account the specified bsize (in the style) + CalculateCellActualBSize(cellFrame, desiredSize.BSize(wm), wm); } - // height may have changed, adjust descent to absorb any excess difference + // bsize may have changed, adjust descent to absorb any excess difference nscoord ascent; if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) { - ascent = desiredSize.BSize(rowWM); + ascent = desiredSize.BSize(wm); } else { ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline(); } - nscoord descent = desiredSize.BSize(rowWM) - ascent; - UpdateHeight(desiredSize.BSize(rowWM), ascent, descent, &aTableFrame, cellFrame); - } - else { - cellMaxHeight = std::max(cellMaxHeight, desiredSize.Height()); + nscoord descent = desiredSize.BSize(wm) - ascent; + UpdateBSize(desiredSize.BSize(wm), ascent, descent, &aTableFrame, cellFrame); + } else { + cellMaxBSize = std::max(cellMaxBSize, desiredSize.BSize(wm)); int32_t rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); if (1 == rowSpan) { - SetContentHeight(cellMaxHeight); + SetContentBSize(cellMaxBSize); } } // Place the child - desiredSize.ISize(rowWM) = availCellWidth; + desiredSize.ISize(wm) = availCellISize; if (kidReflowState) { // We reflowed. Apply relative positioning in the normal way. - kidReflowState->ApplyRelativePositioning(&kidPosition); + kidReflowState->ApplyRelativePositioning(&kidPosition, containerWidth); } else if (kidFrame->IsRelativelyPositioned()) { // We didn't reflow. Do the positioning part of what // MovePositionBy does internally. (This codepath should really // be merged into the else below if we can.) - const nsMargin* computedOffsets = static_cast - (kidFrame->Properties().Get(nsIFrame::ComputedOffsetProperty())); - nsHTMLReflowState::ApplyRelativePositioning(kidFrame, *computedOffsets, - &kidPosition); + LogicalMargin computedOffsets(wm, *static_cast + (kidFrame->Properties().Get(nsIFrame::ComputedOffsetProperty()))); + nsHTMLReflowState::ApplyRelativePositioning(kidFrame, wm, computedOffsets, + &kidPosition, containerWidth); } + + // In vertical-rl writing mode, we are likely to have containerWidth 0 + // because ComputedWidth() was NS_UNCONSTRAINEDSIZE. The true value + // we need is the row's bsize, but we don't know that yet. So we'll use + // the kid's (cell's) desiredSize.Width() value for now. + // For cases where that's wrong, we will fix up the position later. FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr, - kidPosition.x, kidPosition.y, 0); + wm, kidPosition, + wm.IsVerticalRL() && containerWidth == 0 + ? desiredSize.Width() + : containerWidth, + 0); nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow, firstReflow); - x += desiredSize.Width(); - } - else { - if (x != origKidNormalPosition.x) { + iCoord += desiredSize.ISize(wm); + } else { + if (iCoord != origKidNormalPosition.I(wm)) { // Invalidate the old position kidFrame->InvalidateFrameSubtree(); // Move to the new position. As above, we need to account for relative // positioning. - kidFrame->MovePositionBy(nsPoint(x - origKidNormalPosition.x, 0)); + kidFrame->MovePositionBy(wm, + LogicalPoint(wm, iCoord - origKidNormalPosition.I(wm), 0)); nsTableFrame::RePositionViews(kidFrame); // invalidate the new position kidFrame->InvalidateFrameSubtree(); } - // we need to account for the cell's width even if it isn't reflowed - x += kidRect.width; + // we need to account for the cell's isize even if it isn't reflowed + iCoord += kidFrame->ISize(wm); if (kidFrame->GetNextInFlow()) { aStatus = NS_FRAME_NOT_COMPLETE; } } ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); - x += aTableFrame.GetColSpacing(cellColIndex); + iCoord += aTableFrame.GetColSpacing(cellColIndex); } - // just set our width to what was available. The table will calculate the width and not use our value. - aDesiredSize.Width() = aReflowState.AvailableWidth(); + // Just set our isize to what was available. + // The table will calculate the isize and not use our value. + aDesiredSize.ISize(wm) = aReflowState.AvailableISize(); - if (aReflowState.mFlags.mSpecialHeightReflow) { - aDesiredSize.Height() = mRect.height; - } - else if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { - aDesiredSize.Height() = CalcHeight(aReflowState); + if (aReflowState.mFlags.mSpecialBSizeReflow) { + aDesiredSize.BSize(wm) = BSize(wm); + } else if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableBSize()) { + aDesiredSize.BSize(wm) = CalcBSize(aReflowState); if (GetPrevInFlow()) { - nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this); - aDesiredSize.Height() = std::max(aDesiredSize.Height(), height); - } - else { - if (isPaginated && HasStyleHeight()) { - // set the unpaginated height so next in flows can try to honor it - SetHasUnpaginatedHeight(true); - SetUnpaginatedHeight(aPresContext, aDesiredSize.Height()); + nscoord bsize = CalcBSizeFromUnpaginatedBSize(aPresContext, *this, wm); + aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm), bsize); + } else { + if (isPaginated && HasStyleBSize()) { + // set the unpaginated bsize so next in flows can try to honor it + SetHasUnpaginatedBSize(true); + SetUnpaginatedBSize(aPresContext, aDesiredSize.BSize(wm)); } - if (isPaginated && HasUnpaginatedHeight()) { - aDesiredSize.Height() = std::max(aDesiredSize.Height(), GetUnpaginatedHeight(aPresContext)); + if (isPaginated && HasUnpaginatedBSize()) { + aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm), + GetUnpaginatedBSize(aPresContext)); } } - } - else { // constrained height, paginated - // Compute the height we should have from style (subtracting the - // height from our prev-in-flows from the style height) - nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this); - if (styleHeight > aReflowState.AvailableHeight()) { - styleHeight = aReflowState.AvailableHeight(); + } else { // constrained bsize, paginated + // Compute the bsize we should have from style (subtracting the + // bsize from our prev-in-flows from the style bsize) + nscoord styleBSize = CalcBSizeFromUnpaginatedBSize(aPresContext, *this, + wm); + if (styleBSize > aReflowState.AvailableBSize()) { + styleBSize = aReflowState.AvailableBSize(); NS_FRAME_SET_INCOMPLETE(aStatus); } - aDesiredSize.Height() = std::max(cellMaxHeight, styleHeight); + aDesiredSize.BSize(wm) = std::max(cellMaxBSize, styleBSize); } + + if (wm.IsVerticalRL()) { + // Any children whose width was not the same as our final + // aDesiredSize.BSize will have been misplaced earlier at the + // FinishReflowChild stage. So fix them up now. + for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { + nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); + if (!cellFrame) { + continue; + } + if (kidFrame->BSize(wm) != aDesiredSize.BSize(wm)) { + kidFrame->MovePositionBy(wm, + LogicalPoint(wm, 0, kidFrame->BSize(wm) - aDesiredSize.BSize(wm))); + nsTableFrame::RePositionViews(kidFrame); + // Do we need to InvalidateFrameSubtree() here? + } + } + } + aDesiredSize.UnionOverflowAreasWithDesiredBounds(); FinishAndStoreOverflow(&aDesiredSize); } /** Layout the entire row. - * This method stacks cells horizontally according to HTML 4.0 rules. + * This method stacks cells in the inline dir according to HTML 4.0 rules. */ void -nsTableRowFrame::Reflow(nsPresContext* aPresContext, +nsTableRowFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) @@ -1039,6 +1082,8 @@ nsTableRowFrame::Reflow(nsPresContext* aPresContext, DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); + WritingMode wm = aReflowState.GetWritingMode(); + nsTableFrame* tableFrame = GetTableFrame(); const nsStyleVisibility* rowVis = StyleVisibility(); bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible); @@ -1046,11 +1091,11 @@ nsTableRowFrame::Reflow(nsPresContext* aPresContext, tableFrame->SetNeedToCollapse(true); } - // see if a special height reflow needs to occur due to having a pct height - nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); + // see if a special bsize reflow needs to occur due to having a pct bsize + nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowState); - // See if we have a cell with specified/pct height - InitHasCellWithStyleHeight(tableFrame); + // See if we have a cell with specified/pct bsize + InitHasCellWithStyleBSize(tableFrame); ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame, aStatus); @@ -1059,8 +1104,9 @@ nsTableRowFrame::Reflow(nsPresContext* aPresContext, aStatus = NS_INLINE_LINE_BREAK_BEFORE(); } - // just set our width to what was available. The table will calculate the width and not use our value. - aDesiredSize.Width() = aReflowState.AvailableWidth(); + // Just set our isize to what was available. + // The table will calculate the isize and not use our value. + aDesiredSize.ISize(wm) = aReflowState.AvailableISize(); // If our parent is in initial reflow, it'll handle invalidating our // entire overflow rect. @@ -1083,23 +1129,27 @@ nsTableRowFrame::Reflow(nsPresContext* aPresContext, * should be reflowed with the specified height */ nscoord -nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, +nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, bool aIsTopOfPage, nsTableCellFrame* aCellFrame, - nscoord aAvailableHeight, + nscoord aAvailableBSize, nsReflowStatus& aStatus) { + WritingMode wm = aReflowState.GetWritingMode(); + // Reflow the cell frame with the specified height. Use the existing width - nsRect cellRect = aCellFrame->GetRect(); + nscoord containerWidth = aCellFrame->GetSize().width; + LogicalRect cellRect = aCellFrame->GetLogicalRect(wm, containerWidth); nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect(); - nsSize availSize(cellRect.width, aAvailableHeight); + LogicalSize cellSize = cellRect.Size(wm); + LogicalSize availSize(wm, cellRect.ISize(wm), aAvailableBSize); bool borderCollapse = GetTableFrame()->IsBorderCollapse(); + NS_ASSERTION(aCellFrame->GetWritingMode() == wm, + "expected consistent writing-mode within table"); nsTableCellReflowState - cellReflowState(aPresContext, aReflowState, aCellFrame, - LogicalSize(aCellFrame->GetWritingMode(), - availSize), + cellReflowState(aPresContext, aReflowState, aCellFrame, availSize, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState); cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage; @@ -1110,32 +1160,34 @@ nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); bool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); if (fullyComplete) { - desiredSize.Height() = aAvailableHeight; + desiredSize.BSize(wm) = aAvailableBSize; } - aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.Height())); + aCellFrame->SetSize(wm, LogicalSize(wm, cellSize.ISize(wm), + desiredSize.BSize(wm))); - // Note: VerticallyAlignChild can affect the overflow rect. + // Note: BlockDirAlignChild can affect the overflow rect. // XXX What happens if this cell has 'vertical-align: baseline' ? // XXX Why is it assumed that the cell's ascent hasn't changed ? if (fullyComplete) { - aCellFrame->VerticallyAlignChild(mMaxCellAscent); + aCellFrame->BlockDirAlignChild(wm, mMaxCellAscent); } - nsTableFrame::InvalidateTableFrame(aCellFrame, cellRect, + nsTableFrame::InvalidateTableFrame(aCellFrame, + cellRect.GetPhysicalRect(wm, containerWidth), cellVisualOverflow, (aCellFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0); aCellFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); - return desiredSize.Height(); + return desiredSize.BSize(wm); } nscoord nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, - nscoord aWidth, + nscoord aISize, bool aCollapseGroup, - bool& aDidCollapse) + bool& aDidCollapse) { const nsStyleVisibility* rowVis = StyleVisibility(); bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible); @@ -1150,25 +1202,29 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, InvalidateFrameSubtree(); } - nsRect rowRect = GetRect(); - nsRect oldRect = rowRect; + WritingMode wm = GetWritingMode(); + + nscoord parentWidth = GetParent()->GetRect().width; + LogicalRect rowRect = GetLogicalRect(wm, parentWidth); + nsRect oldRect = mRect; nsRect oldVisualOverflow = GetVisualOverflowRect(); - rowRect.y -= aRowOffset; - rowRect.width = aWidth; + rowRect.BStart(wm) -= aRowOffset; + rowRect.ISize(wm) = aISize; nsOverflowAreas overflow; nscoord shift = 0; + nscoord containerWidth = mRect.width; if (aCollapseGroup || collapseRow) { aDidCollapse = true; - shift = rowRect.height; + shift = rowRect.BSize(wm); nsTableCellFrame* cellFrame = GetFirstCell(); if (cellFrame) { int32_t rowIndex; cellFrame->GetRowIndex(rowIndex); shift += tableFrame->GetRowSpacing(rowIndex); while (cellFrame) { - nsRect cRect = cellFrame->GetRect(); + LogicalRect cRect = cellFrame->GetLogicalRect(wm, containerWidth); // If aRowOffset != 0, there's no point in invalidating the cells, since // we've already invalidated our overflow area. Note that we _do_ still // need to invalidate if our row is not moving, because the cell might @@ -1176,25 +1232,26 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, if (aRowOffset == 0) { InvalidateFrame(); } - cRect.height = 0; - cellFrame->SetRect(cRect); + cRect.BSize(wm) = 0; + cellFrame->SetRect(wm, cRect, containerWidth); cellFrame = cellFrame->GetNextCell(); } } else { shift += tableFrame->GetRowSpacing(GetRowIndex()); } - rowRect.height = 0; + rowRect.BSize(wm) = 0; } else { // row is not collapsed nsTableIterator iter(*this); // remember the col index of the previous cell to handle rowspans into this // row - int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : - tableFrame->GetColCount(); + int32_t firstPrevColIndex = -1; int32_t prevColIndex = firstPrevColIndex; - nscoord x = 0; // running total of children x offset + nscoord iPos = 0; // running total of children inline-axis offset + nsTableFrame* fifTable = + static_cast(tableFrame->FirstInFlow()); - int32_t colIncrement = iter.IsLeftToRight() ? 1 : -1; + int32_t colIncrement = 1; nsIFrame* kidFrame = iter.First(); while (kidFrame) { @@ -1206,21 +1263,15 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, // If the adjacent cell is in a prior row (because of a rowspan) add in // the space - if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || - (!iter.IsLeftToRight() && - (prevColIndex != cellColIndex + cellColSpan))) { - x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, - *tableFrame, iter.IsLeftToRight(), - true); + if (prevColIndex != (cellColIndex - 1)) { + iPos += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, + *tableFrame, true); } - nsRect cRect(x, 0, 0, rowRect.height); + LogicalRect cRect(wm, iPos, 0, 0, rowRect.BSize(wm)); - // remember the rightmost (ltr) or leftmost (rtl) column this cell - // spans into - prevColIndex = (iter.IsLeftToRight()) ? - cellColIndex + (cellColSpan - 1) : cellColIndex; - int32_t startIndex = (iter.IsLeftToRight()) ? - cellColIndex : cellColIndex + (cellColSpan - 1); + // remember the last (iend-wards-most) column this cell spans into + prevColIndex = cellColIndex + cellColSpan - 1; + int32_t startIndex = cellColIndex; int32_t actualColSpan = cellColSpan; bool isVisible = false; for (int32_t colX = startIndex; actualColSpan > 0; @@ -1236,7 +1287,7 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, groupVis->mVisible); bool isCollapsed = collapseCol || collapseGroup; if (!isCollapsed) { - cRect.width += tableFrame->GetColumnISize(colX); + cRect.ISize(wm) += fifTable->GetColumnISizeFromFirstInFlow(colX); isVisible = true; if ((actualColSpan > 1)) { nsTableColFrame* nextColFrame = @@ -1245,14 +1296,15 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, nextColFrame->StyleVisibility(); if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) && tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) { - cRect.width += tableFrame->GetColSpacing(cellColIndex); + cRect.ISize(wm) += tableFrame->GetColSpacing(cellColIndex); } } } } - x += cRect.width; - if (isVisible) - x += tableFrame->GetColSpacing(cellColIndex); + iPos += cRect.ISize(wm); + if (isVisible) { + iPos += tableFrame->GetColSpacing(cellColIndex); + } int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame); nsTableRowFrame* rowFrame = GetNextRow(); for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) { @@ -1260,46 +1312,52 @@ nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE == nextRowVis->mVisible); if (!collapseNextRow) { - nsRect nextRect = rowFrame->GetRect(); - cRect.height += nextRect.height + - tableFrame->GetRowSpacing(rowFrame->GetRowIndex()); + LogicalRect nextRect = rowFrame->GetLogicalRect(wm, + containerWidth); + cRect.BSize(wm) += + nextRect.BSize(wm) + + tableFrame->GetRowSpacing(rowFrame->GetRowIndex()); } rowFrame = rowFrame->GetNextRow(); } nsRect oldCellRect = cellFrame->GetRect(); - nsPoint oldCellNormalPos = cellFrame->GetNormalPosition(); + LogicalPoint oldCellNormalPos = + cellFrame->GetLogicalNormalPosition(wm, containerWidth); + nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect(); - if (aRowOffset == 0 && cRect.TopLeft() != oldCellNormalPos) { + if (aRowOffset == 0 && cRect.Origin(wm) != oldCellNormalPos) { // We're moving the cell. Invalidate the old overflow area cellFrame->InvalidateFrameSubtree(); } - cellFrame->MovePositionBy(cRect.TopLeft() - oldCellNormalPos); - cellFrame->SetSize(cRect.Size()); + cellFrame->MovePositionBy(wm, cRect.Origin(wm) - oldCellNormalPos); + cellFrame->SetSize(wm, cRect.Size(wm)); // XXXbz This looks completely bogus in the cases when we didn't // collapse the cell! - nsRect cellBounds(0, 0, cRect.width, cRect.height); - nsOverflowAreas cellOverflow(cellBounds, cellBounds); - cellFrame->FinishAndStoreOverflow(cellOverflow, cRect.Size()); + LogicalRect cellBounds(wm, 0, 0, cRect.ISize(wm), cRect.BSize(wm)); + nsRect cellPhysicalBounds = + cellBounds.GetPhysicalRect(wm, containerWidth); + nsOverflowAreas cellOverflow(cellPhysicalBounds, cellPhysicalBounds); + cellFrame->FinishAndStoreOverflow(cellOverflow, + cRect.Size(wm).GetPhysicalSize(wm)); nsTableFrame::RePositionViews(cellFrame); ConsiderChildOverflow(overflow, cellFrame); if (aRowOffset == 0) { nsTableFrame::InvalidateTableFrame(cellFrame, oldCellRect, - oldCellVisualOverflow, - false); + oldCellVisualOverflow, false); } } kidFrame = iter.Next(); // Get the next child } } - SetRect(rowRect); - overflow.UnionAllWith(nsRect(0, 0, rowRect.width, rowRect.height)); - FinishAndStoreOverflow(overflow, rowRect.Size()); + SetRect(wm, rowRect, containerWidth); + overflow.UnionAllWith(nsRect(0, 0, rowRect.Width(wm), rowRect.Height(wm))); + FinishAndStoreOverflow(overflow, rowRect.Size(wm).GetPhysicalSize(wm)); nsTableFrame::RePositionViews(this); nsTableFrame::InvalidateTableFrame(this, oldRect, oldVisualOverflow, false); @@ -1356,8 +1414,8 @@ nsTableRowFrame::GetNextRow() const NS_DECLARE_FRAME_PROPERTY(RowUnpaginatedHeightProperty, nullptr) void -nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext, - nscoord aValue) +nsTableRowFrame::SetUnpaginatedBSize(nsPresContext* aPresContext, + nscoord aValue) { NS_ASSERTION(!GetPrevInFlow(), "program error"); // Get the property @@ -1366,24 +1424,24 @@ nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext, } nscoord -nsTableRowFrame::GetUnpaginatedHeight(nsPresContext* aPresContext) +nsTableRowFrame::GetUnpaginatedBSize(nsPresContext* aPresContext) { FrameProperties props = FirstInFlow()->Properties(); return NS_PTR_TO_INT32(props.Get(RowUnpaginatedHeightProperty())); } -void nsTableRowFrame::SetContinuousBCBorderWidth(uint8_t aForSide, +void nsTableRowFrame::SetContinuousBCBorderWidth(LogicalSide aForSide, BCPixelSize aPixelValue) { switch (aForSide) { - case NS_SIDE_RIGHT: - mRightContBorderWidth = aPixelValue; + case eLogicalSideIEnd: + mIEndContBorderWidth = aPixelValue; return; - case NS_SIDE_TOP: - mTopContBorderWidth = aPixelValue; + case eLogicalSideBStart: + mBStartContBorderWidth = aPixelValue; return; - case NS_SIDE_LEFT: - mLeftContBorderWidth = aPixelValue; + case eLogicalSideIStart: + mIStartContBorderWidth = aPixelValue; return; default: NS_ERROR("invalid NS_SIDE arg"); @@ -1397,13 +1455,14 @@ nsTableRowFrame::AccessibleType() } #endif /** - * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether - * this row has any cells that have non-auto-height. (Row-spanning + * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether + * this row has any cells that have non-auto-bsize. (Row-spanning * cells are ignored.) */ -void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame) +void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame* aTableFrame) { nsTableIterator iter(*this); + WritingMode wm = GetWritingMode(); for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); @@ -1412,16 +1471,16 @@ void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame) continue; } // Ignore row-spanning cells - const nsStyleCoord &cellHeight = cellFrame->StylePosition()->mHeight; + const nsStyleCoord &cellBSize = cellFrame->StylePosition()->BSize(wm); if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 && - cellHeight.GetUnit() != eStyleUnit_Auto && + cellBSize.GetUnit() != eStyleUnit_Auto && /* calc() with percentages treated like 'auto' */ - (!cellHeight.IsCalcUnit() || !cellHeight.HasPercent())) { - AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT); + (!cellBSize.IsCalcUnit() || !cellBSize.HasPercent())) { + AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE); return; } } - RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT); + RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE); } void diff --git a/layout/tables/nsTableRowFrame.h b/layout/tables/nsTableRowFrame.h index e8e0301398..18d775d2e6 100644 --- a/layout/tables/nsTableRowFrame.h +++ b/layout/tables/nsTableRowFrame.h @@ -10,6 +10,7 @@ #include "nsContainerFrame.h" #include "nsTablePainter.h" #include "nsTableRowGroupFrame.h" +#include "mozilla/WritingModes.h" class nsTableCellFrame; struct nsTableCellReflowState; @@ -110,22 +111,25 @@ public: #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif + + virtual mozilla::WritingMode GetWritingMode() const override + { return GetTableFrame()->GetWritingMode(); } - void UpdateHeight(nscoord aHeight, - nscoord aAscent, - nscoord aDescent, - nsTableFrame* aTableFrame = nullptr, - nsTableCellFrame* aCellFrame = nullptr); + void UpdateBSize(nscoord aBSize, + nscoord aAscent, + nscoord aDescent, + nsTableFrame* aTableFrame = nullptr, + nsTableCellFrame* aCellFrame = nullptr); - void ResetHeight(nscoord aRowStyleHeight); + void ResetBSize(nscoord aRowStyleBSize); - // calculate the height, considering content height of the - // cells and the style height of the row and cells, excluding pct heights - nscoord CalcHeight(const nsHTMLReflowState& aReflowState); + // calculate the bsize, considering content bsize of the + // cells and the style bsize of the row and cells, excluding pct bsizes + nscoord CalcBSize(const nsHTMLReflowState& aReflowState); // Support for cells with 'vertical-align: baseline'. - /** + /** * returns the max-ascent amongst all the cells that have * 'vertical-align: baseline', *including* cells with rowspans. * returns 0 if we don't have any cell with 'vertical-align: baseline' @@ -143,26 +147,26 @@ public: void SetRowIndex (int aRowIndex); /** used by row group frame code */ - nscoord ReflowCellFrame(nsPresContext* aPresContext, + nscoord ReflowCellFrame(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, bool aIsTopOfPage, nsTableCellFrame* aCellFrame, - nscoord aAvailableHeight, + nscoord aAvailableBSize, nsReflowStatus& aStatus); /** * Collapse the row if required, apply col and colgroup visibility: collapse * info to the cells in the row. - * @return he amount to shift up all following rows - * @param aRowOffset - shift the row up by this amount - * @param aWidth - new width of the row + * @return the amount to shift bstart-wards all following rows + * @param aRowOffset - shift the row bstart-wards by this amount + * @param aISize - new isize of the row * @param aCollapseGroup - parent rowgroup is collapsed so this row needs * to be collapsed * @param aDidCollapse - the row has been collapsed */ nscoord CollapseRowIfNecessary(nscoord aRowOffset, - nscoord aWidth, + nscoord aISize, bool aCollapseGroup, - bool& aDidCollapse); + bool& aDidCollapse); /** * Insert a cell frame after the last cell frame that has a col index @@ -174,61 +178,64 @@ public: void InsertCellFrame(nsTableCellFrame* aFrame, int32_t aColIndex); - nsresult CalculateCellActualHeight(nsTableCellFrame* aCellFrame, - nscoord& aDesiredHeight); + nsresult CalculateCellActualBSize(nsTableCellFrame* aCellFrame, + nscoord& aDesiredBSize, + mozilla::WritingMode aWM); bool IsFirstInserted() const; void SetFirstInserted(bool aValue); - nscoord GetContentHeight() const; - void SetContentHeight(nscoord aTwipValue); + nscoord GetContentBSize() const; + void SetContentBSize(nscoord aTwipValue); - bool HasStyleHeight() const; + bool HasStyleBSize() const; - bool HasFixedHeight() const; - void SetHasFixedHeight(bool aValue); + bool HasFixedBSize() const; + void SetHasFixedBSize(bool aValue); - bool HasPctHeight() const; - void SetHasPctHeight(bool aValue); + bool HasPctBSize() const; + void SetHasPctBSize(bool aValue); - nscoord GetFixedHeight() const; - void SetFixedHeight(nscoord aValue); + nscoord GetFixedBSize() const; + void SetFixedBSize(nscoord aValue); - float GetPctHeight() const; - void SetPctHeight(float aPctValue, + float GetPctBSize() const; + void SetPctBSize(float aPctValue, bool aForce = false); - nscoord GetHeight(nscoord aBasis = 0) const; + nscoord GetBSize(nscoord aBasis = 0) const; nsTableRowFrame* GetNextRow() const; - bool HasUnpaginatedHeight(); - void SetHasUnpaginatedHeight(bool aValue); - nscoord GetUnpaginatedHeight(nsPresContext* aPresContext); - void SetUnpaginatedHeight(nsPresContext* aPresContext, nscoord aValue); + bool HasUnpaginatedBSize(); + void SetHasUnpaginatedBSize(bool aValue); + nscoord GetUnpaginatedBSize(nsPresContext* aPresContext); + void SetUnpaginatedBSize(nsPresContext* aPresContext, nscoord aValue); - nscoord GetTopBCBorderWidth(); - void SetTopBCBorderWidth(BCPixelSize aWidth); - nscoord GetBottomBCBorderWidth(); - void SetBottomBCBorderWidth(BCPixelSize aWidth); - nsMargin* GetBCBorderWidth(nsMargin& aBorder); + nscoord GetBStartBCBorderWidth() const { return mBStartBorderWidth; } + nscoord GetBEndBCBorderWidth() const { return mBEndBorderWidth; } + void SetBStartBCBorderWidth(BCPixelSize aWidth) { mBStartBorderWidth = aWidth; } + void SetBEndBCBorderWidth(BCPixelSize aWidth) { mBEndBorderWidth = aWidth; } + mozilla::LogicalMargin GetBCBorderWidth(mozilla::WritingMode aWM); /** * Gets inner border widths before collapsing with cell borders - * Caller must get bottom border from next row or from table - * GetContinuousBCBorderWidth will not overwrite aBorder.bottom + * Caller must get block-end border from next row or from table + * GetContinuousBCBorderWidth will not overwrite that border * see nsTablePainter about continuous borders */ - void GetContinuousBCBorderWidth(nsMargin& aBorder); + void GetContinuousBCBorderWidth(mozilla::WritingMode aWM, + mozilla::LogicalMargin& aBorder); + /** - * @returns outer top bc border == prev row's bottom inner + * @returns outer block-start bc border == prev row's block-end inner */ - nscoord GetOuterTopContBCBorderWidth(); + nscoord GetOuterBStartContBCBorderWidth(); /** * Sets full border widths before collapsing with cell borders - * @param aForSide - side to set; only accepts right, left, and top + * @param aForSide - side to set; only accepts iend, istart, and bstart */ - void SetContinuousBCBorderWidth(uint8_t aForSide, + void SetContinuousBCBorderWidth(mozilla::LogicalSide aForSide, BCPixelSize aPixelValue); virtual bool IsFrameOfType(uint32_t aFlags) const override @@ -251,10 +258,10 @@ protected: */ explicit nsTableRowFrame(nsStyleContext *aContext); - void InitChildReflowState(nsPresContext& aPresContext, - const nsSize& aAvailSize, - bool aBorderCollapse, - nsTableCellReflowState& aReflowState); + void InitChildReflowState(nsPresContext& aPresContext, + const mozilla::LogicalSize& aAvailSize, + bool aBorderCollapse, + nsTableCellReflowState& aReflowState); virtual LogicalSides GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const override; @@ -276,19 +283,19 @@ protected: private: struct RowBits { unsigned mRowIndex:29; - unsigned mHasFixedHeight:1; // set if the dominating style height on the row or any cell is pixel based - unsigned mHasPctHeight:1; // set if the dominating style height on the row or any cell is pct based - unsigned mFirstInserted:1; // if true, then it was the top most newly inserted row + unsigned mHasFixedBSize:1; // set if the dominating style bsize on the row or any cell is pixel based + unsigned mHasPctBSize:1; // set if the dominating style bsize on the row or any cell is pct based + unsigned mFirstInserted:1; // if true, then it was the bstart-most newly inserted row } mBits; - // the desired height based on the content of the tallest cell in the row - nscoord mContentHeight; - // the height based on a style percentage height on either the row or any cell - // if mHasPctHeight is set - nscoord mStylePctHeight; - // the height based on a style pixel height on the row or any - // cell if mHasFixedHeight is set - nscoord mStyleFixedHeight; + // the desired bsize based on the content of the tallest cell in the row + nscoord mContentBSize; + // the bsize based on a style percentage bsize on either the row or any cell + // if mHasPctBSize is set + nscoord mStylePctBSize; + // the bsize based on a style pixel bsize on the row or any + // cell if mHasFixedBSize is set + nscoord mStyleFixedBSize; // max-ascent and max-descent amongst all cells that have 'vertical-align: baseline' nscoord mMaxCellAscent; // does include cells with rowspan > 1 @@ -296,18 +303,18 @@ private: // border widths in pixels in the collapsing border model of the *inner* // half of the border only - BCPixelSize mTopBorderWidth; - BCPixelSize mBottomBorderWidth; - BCPixelSize mRightContBorderWidth; - BCPixelSize mTopContBorderWidth; - BCPixelSize mLeftContBorderWidth; + BCPixelSize mBStartBorderWidth; + BCPixelSize mBEndBorderWidth; + BCPixelSize mIEndContBorderWidth; + BCPixelSize mBStartContBorderWidth; + BCPixelSize mIStartContBorderWidth; /** - * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether - * this row has any cells that have non-auto-height. (Row-spanning + * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether + * this row has any cells that have non-auto-bsize. (Row-spanning * cells are ignored.) */ - void InitHasCellWithStyleHeight(nsTableFrame* aTableFrame); + void InitHasCellWithStyleBSize(nsTableFrame* aTableFrame); }; @@ -331,118 +338,97 @@ inline void nsTableRowFrame::SetFirstInserted(bool aValue) mBits.mFirstInserted = aValue; } -inline bool nsTableRowFrame::HasStyleHeight() const +inline bool nsTableRowFrame::HasStyleBSize() const { - return (bool)mBits.mHasFixedHeight || (bool)mBits.mHasPctHeight; + return (bool)mBits.mHasFixedBSize || (bool)mBits.mHasPctBSize; } -inline bool nsTableRowFrame::HasFixedHeight() const +inline bool nsTableRowFrame::HasFixedBSize() const { - return (bool)mBits.mHasFixedHeight; + return (bool)mBits.mHasFixedBSize; } -inline void nsTableRowFrame::SetHasFixedHeight(bool aValue) +inline void nsTableRowFrame::SetHasFixedBSize(bool aValue) { - mBits.mHasFixedHeight = aValue; + mBits.mHasFixedBSize = aValue; } -inline bool nsTableRowFrame::HasPctHeight() const +inline bool nsTableRowFrame::HasPctBSize() const { - return (bool)mBits.mHasPctHeight; + return (bool)mBits.mHasPctBSize; } -inline void nsTableRowFrame::SetHasPctHeight(bool aValue) +inline void nsTableRowFrame::SetHasPctBSize(bool aValue) { - mBits.mHasPctHeight = aValue; + mBits.mHasPctBSize = aValue; } -inline nscoord nsTableRowFrame::GetContentHeight() const +inline nscoord nsTableRowFrame::GetContentBSize() const { - return mContentHeight; + return mContentBSize; } -inline void nsTableRowFrame::SetContentHeight(nscoord aValue) +inline void nsTableRowFrame::SetContentBSize(nscoord aValue) { - mContentHeight = aValue; + mContentBSize = aValue; } -inline nscoord nsTableRowFrame::GetFixedHeight() const +inline nscoord nsTableRowFrame::GetFixedBSize() const { - if (mBits.mHasFixedHeight) - return mStyleFixedHeight; - else - return 0; + if (mBits.mHasFixedBSize) { + return mStyleFixedBSize; + } + return 0; } -inline float nsTableRowFrame::GetPctHeight() const +inline float nsTableRowFrame::GetPctBSize() const { - if (mBits.mHasPctHeight) - return (float)mStylePctHeight / 100.0f; - else - return 0.0f; + if (mBits.mHasPctBSize) { + return (float)mStylePctBSize / 100.0f; + } + return 0.0f; } -inline bool nsTableRowFrame::HasUnpaginatedHeight() +inline bool nsTableRowFrame::HasUnpaginatedBSize() { - return (mState & NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT) == - NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT; + return (mState & NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE) == + NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE; } -inline void nsTableRowFrame::SetHasUnpaginatedHeight(bool aValue) +inline void nsTableRowFrame::SetHasUnpaginatedBSize(bool aValue) { if (aValue) { - mState |= NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT; + mState |= NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE; } else { - mState &= ~NS_TABLE_ROW_HAS_UNPAGINATED_HEIGHT; + mState &= ~NS_TABLE_ROW_HAS_UNPAGINATED_BSIZE; } } -inline nscoord nsTableRowFrame::GetTopBCBorderWidth() +inline mozilla::LogicalMargin +nsTableRowFrame::GetBCBorderWidth(mozilla::WritingMode aWM) { - return mTopBorderWidth; -} - -inline void nsTableRowFrame::SetTopBCBorderWidth(BCPixelSize aWidth) -{ - mTopBorderWidth = aWidth; -} - -inline nscoord nsTableRowFrame::GetBottomBCBorderWidth() -{ - return mBottomBorderWidth; -} - -inline void nsTableRowFrame::SetBottomBCBorderWidth(BCPixelSize aWidth) -{ - mBottomBorderWidth = aWidth; -} - -inline nsMargin* nsTableRowFrame::GetBCBorderWidth(nsMargin& aBorder) -{ - aBorder.left = aBorder.right = 0; - - aBorder.top = nsPresContext::CSSPixelsToAppUnits(mTopBorderWidth); - aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(mBottomBorderWidth); - - return &aBorder; + return mozilla::LogicalMargin( + aWM, nsPresContext::CSSPixelsToAppUnits(mBStartBorderWidth), 0, + nsPresContext::CSSPixelsToAppUnits(mBEndBorderWidth), 0); } inline void -nsTableRowFrame::GetContinuousBCBorderWidth(nsMargin& aBorder) +nsTableRowFrame::GetContinuousBCBorderWidth(mozilla::WritingMode aWM, + mozilla::LogicalMargin& aBorder) { int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, - mLeftContBorderWidth); - aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, - mTopContBorderWidth); - aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, - mRightContBorderWidth); + aBorder.IEnd(aWM) = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mIStartContBorderWidth); + aBorder.BStart(aWM) = BC_BORDER_END_HALF_COORD(aPixelsToTwips, + mBStartContBorderWidth); + aBorder.IStart(aWM) = BC_BORDER_END_HALF_COORD(aPixelsToTwips, + mIEndContBorderWidth); } -inline nscoord nsTableRowFrame::GetOuterTopContBCBorderWidth() +inline nscoord nsTableRowFrame::GetOuterBStartContBCBorderWidth() { int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - return BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mTopContBorderWidth); + return BC_BORDER_START_HALF_COORD(aPixelsToTwips, mBStartContBorderWidth); } #endif diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index a8be4c05a7..7cceb13570 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -255,13 +255,14 @@ nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) return skip; } -// Position and size aKidFrame and update our reflow state. The origin of -// aKidRect is relative to the upper-left origin of our frame +// Position and size aKidFrame and update our reflow state. void nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext, nsRowGroupReflowState& aReflowState, nsIFrame* aKidFrame, - nsPoint aKidPosition, + WritingMode aWM, + const LogicalPoint& aKidPosition, + nscoord aContainerWidth, nsHTMLReflowMetrics& aDesiredSize, const nsRect& aOriginalKidRect, const nsRect& aOriginalKidVisualOverflow) @@ -271,17 +272,17 @@ nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext, // Place and size the child FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, - aKidPosition.x, aKidPosition.y, 0); + aWM, aKidPosition, aContainerWidth, 0); nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow, isFirstReflow); - // Adjust the running y-offset - aReflowState.y += aDesiredSize.Height(); + // Adjust the running block-offset + aReflowState.bCoord += aDesiredSize.BSize(aWM); - // If our height is constrained then update the available height - if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { - aReflowState.availSize.height -= aDesiredSize.Height(); + // If our block-size is constrained then update the available bsize + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(aWM)) { + aReflowState.availSize.BSize(aWM) -= aDesiredSize.BSize(aWM); } } @@ -296,20 +297,24 @@ nsTableRowGroupFrame::InitChildReflowState(nsPresContext& aPresContext, if (aBorderCollapse) { nsTableRowFrame *rowFrame = do_QueryFrame(aReflowState.frame); if (rowFrame) { - pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder); + WritingMode wm = GetWritingMode(); + LogicalMargin border = rowFrame->GetBCBorderWidth(wm); + collapseBorder = border.GetPhysicalMargin(wm); + pCollapseBorder = &collapseBorder; } } - aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); + aReflowState.Init(&aPresContext, nullptr, pCollapseBorder, &padding); } static void -CacheRowHeightsForPrinting(nsPresContext* aPresContext, - nsTableRowFrame* aFirstRow) +CacheRowBSizesForPrinting(nsPresContext* aPresContext, + nsTableRowFrame* aFirstRow, + WritingMode aWM) { for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) { if (!row->GetPrevInFlow()) { - row->SetHasUnpaginatedHeight(true); - row->SetUnpaginatedHeight(aPresContext, row->GetSize().height); + row->SetHasUnpaginatedBSize(true); + row->SetUnpaginatedBSize(aPresContext, row->BSize(aWM)); } } } @@ -321,21 +326,33 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, nsReflowStatus& aStatus, bool* aPageBreakBeforeEnd) { - if (aPageBreakBeforeEnd) + if (aPageBreakBeforeEnd) { *aPageBreakBeforeEnd = false; + } + WritingMode wm = aReflowState.reflowState.GetWritingMode(); nsTableFrame* tableFrame = GetTableFrame(); const bool borderCollapse = tableFrame->IsBorderCollapse(); - // XXXldb Should we really be checking this rather than available height? + // XXXldb Should we really be checking IsPaginated(), + // or should we *only* check available block-size? // (Think about multi-column layout!) bool isPaginated = aPresContext->IsPaginated() && - NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height; + NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm); bool haveRow = false; bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || tableFrame->IsGeometryDirty(); - bool needToCalcRowHeights = reflowAllKids; + bool needToCalcRowBSizes = reflowAllKids; + + nscoord containerWidth = aReflowState.reflowState.ComputedWidth(); + if (containerWidth == NS_UNCONSTRAINEDSIZE) { + containerWidth = 0; // we can't position frames correctly in RTL yet, + // so they will need to be adjusted later + } else { + containerWidth += + aReflowState.reflowState.ComputedPhysicalBorderPadding().LeftRight(); + } nsIFrame *prevKidFrame = nullptr; for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; @@ -346,16 +363,16 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, NS_NOTREACHED("yikes, a non-row child"); continue; } - nscoord cellSpacingY = tableFrame->GetRowSpacing(rowFrame->GetRowIndex()); + nscoord cellSpacingB = tableFrame->GetRowSpacing(rowFrame->GetRowIndex()); haveRow = true; // Reflow the row frame if (reflowAllKids || NS_SUBTREE_DIRTY(kidFrame) || - (aReflowState.reflowState.mFlags.mSpecialHeightReflow && + (aReflowState.reflowState.mFlags.mSpecialBSizeReflow && (isPaginated || (kidFrame->GetStateBits() & - NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { - nsRect oldKidRect = kidFrame->GetRect(); + NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) { + LogicalRect oldKidRect = kidFrame->GetLogicalRect(wm, containerWidth); nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); // XXXldb We used to only pass aDesiredSize.mFlags through for the @@ -364,21 +381,20 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, aDesiredSize.mFlags); desiredSize.ClearSize(); - // Reflow the child into the available space, giving it as much height as + // Reflow the child into the available space, giving it as much bsize as // it wants. We'll deal with splitting later after we've computed the row - // heights, taking into account cells with row spans... - WritingMode wm = kidFrame->GetWritingMode(); - LogicalSize kidAvailSize(wm, aReflowState.availSize); + // bsizes, taking into account cells with row spans... + LogicalSize kidAvailSize = aReflowState.availSize; kidAvailSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame, kidAvailSize, - -1, -1, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, borderCollapse, kidReflowState); // This can indicate that columns were resized. - if (aReflowState.reflowState.IsHResize()) { - kidReflowState.SetHResize(true); + if (aReflowState.reflowState.IsIResize()) { + kidReflowState.SetIResize(true); } NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame, @@ -389,33 +405,35 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, kidReflowState.mFlags.mIsTopOfPage = false; } + LogicalPoint kidPosition(wm, 0, aReflowState.bCoord); ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, - 0, aReflowState.y, 0, aStatus); - nsPoint kidPosition(0, aReflowState.y); - kidReflowState.ApplyRelativePositioning(&kidPosition); + wm, kidPosition, containerWidth, 0, aStatus); + kidReflowState.ApplyRelativePositioning(&kidPosition, containerWidth); // Place the child - PlaceChild(aPresContext, aReflowState, kidFrame, kidPosition, - desiredSize, oldKidRect, oldKidVisualOverflow); - aReflowState.y += cellSpacingY; + PlaceChild(aPresContext, aReflowState, kidFrame, + wm, kidPosition, containerWidth, + desiredSize, oldKidRect.GetPhysicalRect(wm, containerWidth), + oldKidVisualOverflow); + aReflowState.bCoord += cellSpacingB; if (!reflowAllKids) { if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) { - // Inform the row of its new height. + // Inform the row of its new bsize. rowFrame->DidResize(); // the overflow area may have changed inflate the overflow area const nsStylePosition *stylePos = StylePosition(); - nsStyleUnit unit = stylePos->mHeight.GetUnit(); - if (aReflowState.tableFrame->IsAutoHeight() && + nsStyleUnit unit = stylePos->BSize(wm).GetUnit(); + if (aReflowState.tableFrame->IsAutoBSize(wm) && unit != eStyleUnit_Coord) { // Because other cells in the row may need to be aligned // differently, repaint the entire row InvalidateFrame(); + } else if (oldKidRect.BSize(wm) != desiredSize.BSize(wm)) { + needToCalcRowBSizes = true; } - else if (oldKidRect.height != desiredSize.Height()) - needToCalcRowHeights = true; } else { - needToCalcRowHeights = true; + needToCalcRowBSizes = true; } } @@ -428,33 +446,34 @@ nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, } else { SlideChild(aReflowState, kidFrame); - // Adjust the running y-offset so we know where the next row should be placed - nscoord height = kidFrame->GetSize().height + cellSpacingY; - aReflowState.y += height; + // Adjust the running b-offset so we know where the next row should be placed + nscoord bSize = kidFrame->BSize(wm) + cellSpacingB; + aReflowState.bCoord += bSize; - if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { - aReflowState.availSize.height -= height; + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.BSize(wm)) { + aReflowState.availSize.BSize(wm) -= bSize; } } ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); } - if (haveRow) - aReflowState.y -= tableFrame->GetRowSpacing(GetStartRowIndex() + - GetRowCount()); + if (haveRow) { + aReflowState.bCoord -= tableFrame->GetRowSpacing(GetStartRowIndex() + + GetRowCount()); + } // Return our desired rect - aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth(); - aDesiredSize.Height() = aReflowState.y; + aDesiredSize.ISize(wm) = aReflowState.reflowState.AvailableISize(); + aDesiredSize.BSize(wm) = aReflowState.bCoord; - if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) { + if (aReflowState.reflowState.mFlags.mSpecialBSizeReflow) { DidResizeRows(aDesiredSize); if (isPaginated) { - CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); + CacheRowBSizesForPrinting(aPresContext, GetFirstRow(), wm); } } - else if (needToCalcRowHeights) { - CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState); + else if (needToCalcRowBSizes) { + CalculateRowBSizes(aPresContext, aDesiredSize, aReflowState.reflowState); if (!reflowAllKids) { InvalidateFrame(); } @@ -476,34 +495,35 @@ nsTableRowGroupFrame::GetFirstRow() struct RowInfo { - RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; } - unsigned height; // content height or fixed height, excluding pct height - unsigned pctHeight:29; // pct height - unsigned hasStyleHeight:1; - unsigned hasPctHeight:1; + RowInfo() { bSize = pctBSize = hasStyleBSize = hasPctBSize = isSpecial = 0; } + unsigned bSize; // content bsize or fixed bsize, excluding pct bsize + unsigned pctBSize:29; // pct bsize + unsigned hasStyleBSize:1; + unsigned hasPctBSize:1; unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at - // least 2 cells spanning the row and there is no style height on the row + // least 2 cells spanning the row and there is no style bsize on the row }; static void -UpdateHeights(RowInfo& aRowInfo, - nscoord aAdditionalHeight, - nscoord& aTotal, - nscoord& aUnconstrainedTotal) +UpdateBSizes(RowInfo& aRowInfo, + nscoord aAdditionalBSize, + nscoord& aTotal, + nscoord& aUnconstrainedTotal) { - aRowInfo.height += aAdditionalHeight; - aTotal += aAdditionalHeight; - if (!aRowInfo.hasStyleHeight) { - aUnconstrainedTotal += aAdditionalHeight; + aRowInfo.bSize += aAdditionalBSize; + aTotal += aAdditionalBSize; + if (!aRowInfo.hasStyleBSize) { + aUnconstrainedTotal += aAdditionalBSize; } } void nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize) { - // update the cells spanning rows with their new heights - // this is the place where all of the cells in the row get set to the height of the row - // Reset the overflow area + // Update the cells spanning rows with their new bsizes. + // This is the place where all of the cells in the row get set to the bsize + // of the row. + // Reset the overflow area. aDesiredSize.mOverflowAreas.Clear(); for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { @@ -512,13 +532,13 @@ nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize) } } -// This calculates the height of all the rows and takes into account -// style height on the row group, style heights on rows and cells, style heights on rowspans. -// Actual row heights will be adjusted later if the table has a style height. -// Even if rows don't change height, this method must be called to set the heights of each -// cell in the row to the height of its row. +// This calculates the bsize of all the rows and takes into account +// style bsize on the row group, style bsizes on rows and cells, style bsizes on rowspans. +// Actual row bsizes will be adjusted later if the table has a style bsize. +// Even if rows don't change bsize, this method must be called to set the bsizes of each +// cell in the row to the bsize of its row. void -nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, +nsTableRowGroupFrame::CalculateRowBSizes(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState) { @@ -531,13 +551,20 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, // find the row corresponding to the row index we just found nsTableRowFrame* startRowFrame = GetFirstRow(); - if (!startRowFrame) return; + if (!startRowFrame) { + return; + } - // the current row group height is the y origin of the 1st row we are about to calculated a height for - nscoord startRowGroupHeight = startRowFrame->GetNormalPosition().y; + // The current row group block-size is the block-origin of the 1st row + // we are about to calculate a block-size for. + WritingMode wm = aReflowState.GetWritingMode(); + nscoord containerWidth = 0; // actual value is unimportant as we're initially + // computing sizes, not physical positions + nscoord startRowGroupBSize = + startRowFrame->GetLogicalNormalPosition(wm, containerWidth).B(wm); int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); - // collect the current height of each row. nscoord* rowHeights = nullptr; + // Collect the current bsize of each row. if (numRows <= 0) return; @@ -547,30 +574,30 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, } bool hasRowSpanningCell = false; - nscoord heightOfRows = 0; - nscoord heightOfUnStyledRows = 0; - // Get the height of each row without considering rowspans. This will be the max of - // the largest desired height of each cell, the largest style height of each cell, - // the style height of the row. - nscoord pctHeightBasis = GetHeightBasis(aReflowState); + nscoord bSizeOfRows = 0; + nscoord bSizeOfUnStyledRows = 0; + // Get the bsize of each row without considering rowspans. This will be the max of + // the largest desired bsize of each cell, the largest style bsize of each cell, + // the style bsize of the row. + nscoord pctBSizeBasis = GetBSizeBasis(aReflowState); int32_t rowIndex; // the index in rowInfo, not among the rows in the row group nsTableRowFrame* rowFrame; for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { - nscoord nonPctHeight = rowFrame->GetContentHeight(); + nscoord nonPctBSize = rowFrame->GetContentBSize(); if (isPaginated) { - nonPctHeight = std::max(nonPctHeight, rowFrame->GetSize().height); + nonPctBSize = std::max(nonPctBSize, rowFrame->BSize(wm)); } if (!rowFrame->GetPrevInFlow()) { - if (rowFrame->HasPctHeight()) { - rowInfo[rowIndex].hasPctHeight = true; - rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis); + if (rowFrame->HasPctBSize()) { + rowInfo[rowIndex].hasPctBSize = true; + rowInfo[rowIndex].pctBSize = rowFrame->GetBSize(pctBSizeBasis); } - rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight(); - nonPctHeight = std::max(nonPctHeight, rowFrame->GetFixedHeight()); + rowInfo[rowIndex].hasStyleBSize = rowFrame->HasStyleBSize(); + nonPctBSize = std::max(nonPctBSize, rowFrame->GetFixedBSize()); } - UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows); + UpdateBSizes(rowInfo[rowIndex], nonPctBSize, bSizeOfRows, bSizeOfUnStyledRows); - if (!rowInfo[rowIndex].hasStyleHeight) { + if (!rowInfo[rowIndex].hasStyleBSize) { if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) { rowInfo[rowIndex].isSpecial = true; // iteratate the row's cell frames to see if any do not have rowspan > 1 @@ -594,7 +621,7 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, } if (hasRowSpanningCell) { - // Get the height of cells with rowspans and allocate any extra space to the rows they span + // Get the bsize of cells with rowspans and allocate any extra space to the rows they span // iteratate the child frames and process the row frames among them for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a @@ -604,70 +631,69 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); // iteratate the row's cell frames while (cellFrame) { - nscoord cellSpacingY = tableFrame->GetRowSpacing(startRowIndex + rowIndex); + nscoord cellSpacingB = tableFrame->GetRowSpacing(startRowIndex + rowIndex); int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); if ((rowIndex + rowSpan) > numRows) { // there might be rows pushed already to the nextInFlow rowSpan = numRows - rowIndex; } - if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans - nscoord heightOfRowsSpanned = 0; - nscoord heightOfUnStyledRowsSpanned = 0; + if (rowSpan > 1) { // a cell with rowspan > 1, determine the bsize of the rows it spans + nscoord bsizeOfRowsSpanned = 0; + nscoord bsizeOfUnStyledRowsSpanned = 0; nscoord numSpecialRowsSpanned = 0; nscoord cellSpacingTotal = 0; int32_t spanX; for (spanX = 0; spanX < rowSpan; spanX++) { - heightOfRowsSpanned += rowInfo[rowIndex + spanX].height; - if (!rowInfo[rowIndex + spanX].hasStyleHeight) { - heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height; + bsizeOfRowsSpanned += rowInfo[rowIndex + spanX].bSize; + if (!rowInfo[rowIndex + spanX].hasStyleBSize) { + bsizeOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].bSize; } if (0 != spanX) { - cellSpacingTotal += cellSpacingY; + cellSpacingTotal += cellSpacingB; } if (rowInfo[rowIndex + spanX].isSpecial) { numSpecialRowsSpanned++; } } - nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal; - // get the height of the cell - nsSize cellFrameSize = cellFrame->GetSize(); - nsSize cellDesSize = - cellFrame->GetDesiredSize().GetPhysicalSize(cellFrame->GetWritingMode()); - rowFrame->CalculateCellActualHeight(cellFrame, cellDesSize.height); - cellFrameSize.height = cellDesSize.height; + nscoord bsizeOfAreaSpanned = bsizeOfRowsSpanned + cellSpacingTotal; + // get the bsize of the cell + LogicalSize cellFrameSize = cellFrame->GetLogicalSize(wm); + LogicalSize cellDesSize = cellFrame->GetDesiredSize(); + rowFrame->CalculateCellActualBSize(cellFrame, cellDesSize.BSize(wm), wm); + cellFrameSize.BSize(wm) = cellDesSize.BSize(wm); if (cellFrame->HasVerticalAlignBaseline()) { // to ensure that a spanning cell with a long descender doesn't // collide with the next row, we need to take into account the shift // that will be done to align the cell on the baseline of the row. - cellFrameSize.height += rowFrame->GetMaxCellAscent() - - cellFrame->GetCellBaseline(); + cellFrameSize.BSize(wm) += rowFrame->GetMaxCellAscent() - + cellFrame->GetCellBaseline(); } - if (heightOfAreaSpanned < cellFrameSize.height) { - // the cell's height is larger than the available space of the rows it - // spans so distribute the excess height to the rows affected - nscoord extra = cellFrameSize.height - heightOfAreaSpanned; + if (bsizeOfAreaSpanned < cellFrameSize.BSize(wm)) { + // the cell's bsize is larger than the available space of the rows it + // spans so distribute the excess bsize to the rows affected + nscoord extra = cellFrameSize.BSize(wm) - bsizeOfAreaSpanned; nscoord extraUsed = 0; if (0 == numSpecialRowsSpanned) { - //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation"); - bool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0); + //NS_ASSERTION(bsizeOfRowsSpanned > 0, "invalid row span situation"); + bool haveUnStyledRowsSpanned = (bsizeOfUnStyledRowsSpanned > 0); nscoord divisor = (haveUnStyledRowsSpanned) - ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned; + ? bsizeOfUnStyledRowsSpanned : bsizeOfRowsSpanned; if (divisor > 0) { for (spanX = rowSpan - 1; spanX >= 0; spanX--) { - if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) { - // The amount of additional space each row gets is proportional to its height - float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor); + if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleBSize) { + // The amount of additional space each row gets is proportional to its bsize + float percent = ((float)rowInfo[rowIndex + spanX].bSize) / ((float)divisor); // give rows their percentage, except for the first row which gets the remainder nscoord extraForRow = (0 == spanX) ? extra - extraUsed : NSToCoordRound(((float)(extra)) * percent); extraForRow = std::min(extraForRow, extra - extraUsed); - // update the row height - UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + // update the row bsize + UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow, bSizeOfRows, bSizeOfUnStyledRows); extraUsed += extraForRow; if (extraUsed >= extra) { - NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + NS_ASSERTION((extraUsed == extra), "invalid row bsize calculation"); break; } } @@ -675,7 +701,7 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, } else { // put everything in the last row - UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows); + UpdateBSizes(rowInfo[rowIndex + rowSpan - 1], extra, bSizeOfRows, bSizeOfUnStyledRows); } } else { @@ -691,11 +717,11 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, ? extra - extraUsed : NSToCoordRound(((float)(extra)) * percent); extraForRow = std::min(extraForRow, extra - extraUsed); - // update the row height - UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); + // update the row bsize + UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow, bSizeOfRows, bSizeOfUnStyledRows); extraUsed += extraForRow; if (extraUsed >= extra) { - NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); + NS_ASSERTION((extraUsed == extra), "invalid row bsize calculation"); break; } } @@ -709,99 +735,113 @@ nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, } // while (rowFrame) } - // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis - nscoord extra = pctHeightBasis - heightOfRows; - for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) { + // pct bsize rows have already got their content bsizes. + // Give them their pct bsizes up to pctBSizeBasis + nscoord extra = pctBSizeBasis - bSizeOfRows; + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); + rowFrame = rowFrame->GetNextRow(), rowIndex++) { RowInfo& rInfo = rowInfo[rowIndex]; - if (rInfo.hasPctHeight) { - nscoord rowExtra = (rInfo.pctHeight > rInfo.height) - ? rInfo.pctHeight - rInfo.height: 0; + if (rInfo.hasPctBSize) { + nscoord rowExtra = (rInfo.pctBSize > rInfo.bSize) + ? rInfo.pctBSize - rInfo.bSize: 0; rowExtra = std::min(rowExtra, extra); - UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows); + UpdateBSizes(rInfo, rowExtra, bSizeOfRows, bSizeOfUnStyledRows); extra -= rowExtra; } } - bool styleHeightAllocation = false; - nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + + bool styleBSizeAllocation = false; + nscoord rowGroupBSize = startRowGroupBSize + bSizeOfRows + tableFrame->GetRowSpacing(0, numRows-1); - // if we have a style height, allocate the extra height to unconstrained rows - if ((aReflowState.ComputedHeight() > rowGroupHeight) && - (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) { - nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight; + // if we have a style bsize, allocate the extra bsize to unconstrained rows + if ((aReflowState.ComputedBSize() > rowGroupBSize) && + (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedBSize())) { + nscoord extraComputedBSize = aReflowState.ComputedBSize() - rowGroupBSize; nscoord extraUsed = 0; - bool haveUnStyledRows = (heightOfUnStyledRows > 0); + bool haveUnStyledRows = (bSizeOfUnStyledRows > 0); nscoord divisor = (haveUnStyledRows) - ? heightOfUnStyledRows : heightOfRows; + ? bSizeOfUnStyledRows : bSizeOfRows; if (divisor > 0) { - styleHeightAllocation = true; + styleBSizeAllocation = true; for (rowIndex = 0; rowIndex < numRows; rowIndex++) { - if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) { + if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleBSize) { // The amount of additional space each row gets is based on the // percentage of space it occupies - float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor); + float percent = ((float)rowInfo[rowIndex].bSize) / ((float)divisor); // give rows their percentage, except for the last row which gets the remainder nscoord extraForRow = (numRows - 1 == rowIndex) - ? extraComputedHeight - extraUsed - : NSToCoordRound(((float)extraComputedHeight) * percent); - extraForRow = std::min(extraForRow, extraComputedHeight - extraUsed); - // update the row height - UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows); + ? extraComputedBSize - extraUsed + : NSToCoordRound(((float)extraComputedBSize) * percent); + extraForRow = std::min(extraForRow, extraComputedBSize - extraUsed); + // update the row bsize + UpdateBSizes(rowInfo[rowIndex], extraForRow, bSizeOfRows, bSizeOfUnStyledRows); extraUsed += extraForRow; - if (extraUsed >= extraComputedHeight) { - NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation"); + if (extraUsed >= extraComputedBSize) { + NS_ASSERTION((extraUsed == extraComputedBSize), "invalid row bsize calculation"); break; } } } } - rowGroupHeight = aReflowState.ComputedHeight(); + rowGroupBSize = aReflowState.ComputedBSize(); } - nscoord yOrigin = startRowGroupHeight; - // update the rows with their (potentially) new heights - for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { + if (wm.IsVertical()) { + // we need the correct containerWidth below for block positioning in + // vertical-rl writing mode + containerWidth = rowGroupBSize; + } + + nscoord bOrigin = startRowGroupBSize; + // update the rows with their (potentially) new bsizes + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; + rowFrame = rowFrame->GetNextRow(), rowIndex++) { nsRect rowBounds = rowFrame->GetRect(); + LogicalSize rowBoundsSize(wm, rowBounds.Size()); nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); - nscoord deltaY = yOrigin - rowFrame->GetNormalPosition().y; + nscoord deltaB = + bOrigin - rowFrame->GetLogicalNormalPosition(wm, containerWidth).B(wm); - nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0; + nscoord rowBSize = (rowInfo[rowIndex].bSize > 0) ? rowInfo[rowIndex].bSize : 0; - if (deltaY != 0 || (rowHeight != rowBounds.height)) { + if (deltaB != 0 || (rowBSize != rowBoundsSize.BSize(wm))) { // Resize/move the row to its final size and position - if (deltaY != 0) { + if (deltaB != 0) { rowFrame->InvalidateFrameSubtree(); } - rowFrame->MovePositionBy(nsPoint(0, deltaY)); - rowFrame->SetSize(nsSize(rowBounds.width, rowHeight)); + rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, deltaB)); + rowFrame->SetSize(LogicalSize(wm, rowBoundsSize.ISize(wm), rowBSize)); nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow, false); - if (deltaY != 0) { + if (deltaB != 0) { nsTableFrame::RePositionViews(rowFrame); // XXXbz we don't need to update our overflow area? } } - yOrigin += rowHeight + tableFrame->GetRowSpacing(startRowIndex + rowIndex); + bOrigin += rowBSize + tableFrame->GetRowSpacing(startRowIndex + rowIndex); } - if (isPaginated && styleHeightAllocation) { - // since the row group has a style height, cache the row heights, so next in flows can honor them - CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); + if (isPaginated && styleBSizeAllocation) { + // since the row group has a style bsize, cache the row bsizes, + // so next in flows can honor them + CacheRowBSizesForPrinting(aPresContext, GetFirstRow(), wm); } DidResizeRows(aDesiredSize); - aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size + aDesiredSize.BSize(wm) = rowGroupBSize; // Adjust our desired size } nscoord -nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, - nscoord aWidth) +nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aBTotalOffset, + nscoord aISize, + WritingMode aWM) { nsTableFrame* tableFrame = GetTableFrame(); + nscoord containerWidth = tableFrame->GetRect().width; const nsStyleVisibility* groupVis = StyleVisibility(); bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); if (collapseGroup) { @@ -810,43 +850,44 @@ nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, nsOverflowAreas overflow; - nsTableRowFrame* rowFrame= GetFirstRow(); + nsTableRowFrame* rowFrame = GetFirstRow(); bool didCollapse = false; - nscoord yGroupOffset = 0; + nscoord bGroupOffset = 0; while (rowFrame) { - yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset, - aWidth, collapseGroup, + bGroupOffset += rowFrame->CollapseRowIfNecessary(bGroupOffset, + aISize, collapseGroup, didCollapse); ConsiderChildOverflow(overflow, rowFrame); rowFrame = rowFrame->GetNextRow(); } - nsRect groupRect = GetRect(); - nsRect oldGroupRect = groupRect; + LogicalRect groupRect = GetLogicalRect(aWM, containerWidth); + nsRect oldGroupRect = GetRect(); nsRect oldGroupVisualOverflow = GetVisualOverflowRect(); - groupRect.height -= yGroupOffset; + groupRect.BSize(aWM) -= bGroupOffset; if (didCollapse) { // add back the cellspacing between rowgroups - groupRect.height += tableFrame->GetRowSpacing(GetStartRowIndex() + - GetRowCount()); + groupRect.BSize(aWM) += tableFrame->GetRowSpacing(GetStartRowIndex() + + GetRowCount()); } - groupRect.y -= aYTotalOffset; - groupRect.width = aWidth; + groupRect.BStart(aWM) -= aBTotalOffset; + groupRect.ISize(aWM) = aISize; - if (aYTotalOffset != 0) { + if (aBTotalOffset != 0) { InvalidateFrameSubtree(); } - SetRect(groupRect); - overflow.UnionAllWith(nsRect(0, 0, groupRect.width, groupRect.height)); - FinishAndStoreOverflow(overflow, groupRect.Size()); + SetRect(aWM, groupRect, containerWidth); + overflow.UnionAllWith(nsRect(0, 0, groupRect.Width(aWM), + groupRect.Height(aWM))); + FinishAndStoreOverflow(overflow, groupRect.Size(aWM).GetPhysicalSize(aWM)); nsTableFrame::RePositionViews(this); nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow, false); - return yGroupOffset; + return bGroupOffset; } // Move a child that was skipped during a reflow. @@ -855,13 +896,14 @@ nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState, nsIFrame* aKidFrame) { // Move the frame if we need to - nsPoint oldPosition = aKidFrame->GetNormalPosition(); - nsPoint newPosition = oldPosition; - newPosition.y = aReflowState.y; - if (oldPosition.y != newPosition.y) { + WritingMode wm = aReflowState.reflowState.GetWritingMode(); + LogicalPoint oldPosition = aKidFrame->GetLogicalNormalPosition(wm, 0); + LogicalPoint newPosition = oldPosition; + newPosition.B(wm) = aReflowState.bCoord; + if (oldPosition.B(wm) != newPosition.B(wm)) { aKidFrame->InvalidateFrameSubtree(); - aReflowState.reflowState.ApplyRelativePositioning(&newPosition); - aKidFrame->SetPosition(newPosition); + aReflowState.reflowState.ApplyRelativePositioning(&newPosition, 0); + aKidFrame->SetPosition(wm, newPosition, 0); nsTableFrame::RePositionViews(aKidFrame); aKidFrame->InvalidateFrameSubtree(); } @@ -897,14 +939,14 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, nsTableRowFrame& aFirstRow, nsTableRowFrame& aLastRow, bool aFirstRowIsTopOfPage, - nscoord aSpanningRowBottom, + nscoord aSpanningRowBEnd, nsTableRowFrame*& aContRow, nsTableRowFrame*& aFirstTruncatedRow, - nscoord& aDesiredHeight) + nscoord& aDesiredBSize) { - NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights"); + NS_ASSERTION(aSpanningRowBEnd >= 0, "Can't split negative bsizes"); aFirstTruncatedRow = nullptr; - aDesiredHeight = 0; + aDesiredBSize = 0; const bool borderCollapse = aTable.IsBorderCollapse(); int32_t lastRowIndex = aLastRow.GetRowIndex(); @@ -919,14 +961,14 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) { int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell); // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow - // were reflowed correctly during the unconstrained height reflow. + // were reflowed correctly during the unconstrained bsize reflow. if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) { haveRowSpan = true; nsReflowStatus status; - // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow - // aAvailHeight is the space between the row group start and the end of the page - nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y; - NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?"); + // Ask the row to reflow the cell to the bsize of all the rows it spans up through aLastRow + // cellAvailBSize is the space between the row group start and the end of the page + nscoord cellAvailBSize = aSpanningRowBEnd - rowPos.y; + NS_ASSERTION(cellAvailBSize >= 0, "No space for cell?"); bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage; nsRect rowRect = row->GetNormalRect(); @@ -934,22 +976,22 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, std::max(aReflowState.AvailableHeight() - rowRect.y, 0)); // don't let the available height exceed what - // CalculateRowHeights set for it + // CalculateRowBSizes set for it rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height); nsHTMLReflowState rowReflowState(&aPresContext, aReflowState, row, LogicalSize(row->GetWritingMode(), rowAvailSize), - -1, -1, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(aPresContext, borderCollapse, rowReflowState); rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page - nscoord cellHeight = row->ReflowCellFrame(&aPresContext, rowReflowState, + nscoord cellBSize = row->ReflowCellFrame(&aPresContext, rowReflowState, isTopOfPage, cell, - cellAvailHeight, status); - aDesiredHeight = std::max(aDesiredHeight, rowPos.y + cellHeight); + cellAvailBSize, status); + aDesiredBSize = std::max(aDesiredBSize, rowPos.y + cellBSize); if (NS_FRAME_IS_COMPLETE(status)) { - if (cellHeight > cellAvailHeight) { + if (cellBSize > cellAvailBSize) { aFirstTruncatedRow = row; if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) { // return now, since we will be getting another reflow after either (1) row is @@ -979,7 +1021,7 @@ nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, } } if (!haveRowSpan) { - aDesiredHeight = aLastRow.GetNormalRect().YMost(); + aDesiredBSize = aLastRow.GetNormalRect().YMost(); } } @@ -1059,7 +1101,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, // in the available space for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) { bool rowIsOnPage = true; - nscoord cellSpacingY = aTableFrame->GetRowSpacing(rowFrame->GetRowIndex()); + nscoord cellSpacingB = aTableFrame->GetRowSpacing(rowFrame->GetRowIndex()); nsRect rowRect = rowFrame->GetNormalRect(); // See if the row fits on this page if (rowRect.YMost() > availHeight) { @@ -1075,7 +1117,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, nsHTMLReflowState rowReflowState(aPresContext, aReflowState, rowFrame, LogicalSize(rowFrame->GetWritingMode(), availSize), - -1, -1, + nullptr, nsHTMLReflowState::CALLER_WILL_INIT); InitChildReflowState(*aPresContext, borderCollapse, rowReflowState); @@ -1115,7 +1157,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, if (contRow) { aDesiredSize.Height() += rowMetrics.Height(); if (prevRowFrame) - aDesiredSize.Height() += cellSpacingY; + aDesiredSize.Height() += cellSpacingB; } else return NS_ERROR_NULL_POINTER; } @@ -1142,7 +1184,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, } aDesiredSize.Height() += rowMetrics.Height(); if (prevRowFrame) - aDesiredSize.Height() += cellSpacingY; + aDesiredSize.Height() += cellSpacingB; NS_WARNING("data loss - complete row needed more height than available, on top of page"); } else { @@ -1181,10 +1223,10 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, // reflow the cells with rowspan >1 that occur on the page nsTableRowFrame* firstTruncatedRow; - nscoord yMost; + nscoord bMost; SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage, *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, - firstTruncatedRow, yMost); + firstTruncatedRow, bMost); if (firstTruncatedRow) { // A rowspan >1 cell did not fit (and could not split) in the space we gave it if (firstTruncatedRow == firstRowThisPage) { @@ -1238,7 +1280,7 @@ nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, } // if (firstTruncatedRow == firstRowThisPage) } // if (firstTruncatedRow) else { - aDesiredSize.Height() = std::max(aDesiredSize.Height(), yMost); + aDesiredSize.Height() = std::max(aDesiredSize.Height(), bMost); if (contRow) { aStatus = NS_FRAME_NOT_COMPLETE; } @@ -1288,8 +1330,8 @@ nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, // Row geometry may be going to change so we need to invalidate any row cursor. ClearRowCursor(); - // see if a special height reflow needs to occur due to having a pct height - nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); + // see if a special bsize reflow needs to occur due to having a pct bsize + nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowState); nsTableFrame* tableFrame = GetTableFrame(); nsRowGroupReflowState state(aReflowState, tableFrame); @@ -1314,13 +1356,13 @@ nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak || aDesiredSize.Height() > aReflowState.AvailableHeight())) { // Nope, find a place to split the row group - bool specialReflow = (bool)aReflowState.mFlags.mSpecialHeightReflow; - ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = false; + bool specialReflow = (bool)aReflowState.mFlags.mSpecialBSizeReflow; + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialBSizeReflow = false; SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus, splitDueToPageBreak); - ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow; + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialBSizeReflow = specialReflow; } // XXXmats The following is just bogus. We leave it here for now because @@ -1330,11 +1372,13 @@ nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, NS_FRAME_SET_INCOMPLETE(aStatus); } - SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) && - (aReflowState.ComputedHeight() > 0)); + SetHasStyleBSize((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedBSize()) && + (aReflowState.ComputedBSize() > 0)); - // just set our width to what was available. The table will calculate the width and not use our value. - aDesiredSize.Width() = aReflowState.AvailableWidth(); + // Just set our isize to what was available. + // The table will calculate the isize and not use our value. + WritingMode wm = aReflowState.GetWritingMode(); + aDesiredSize.ISize(wm) = aReflowState.AvailableISize(); aDesiredSize.UnionOverflowAreasWithDesiredBounds(); @@ -1510,16 +1554,16 @@ nsTableRowGroupFrame::GetUsedPadding() const } nscoord -nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) +nsTableRowGroupFrame::GetBSizeBasis(const nsHTMLReflowState& aReflowState) { nscoord result = 0; nsTableFrame* tableFrame = GetTableFrame(); int32_t startRowIndex = GetStartRowIndex(); - if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { + if ((aReflowState.ComputedBSize() > 0) && (aReflowState.ComputedBSize() < NS_UNCONSTRAINEDSIZE)) { nscoord cellSpacing = tableFrame->GetRowSpacing(startRowIndex, std::max(startRowIndex, startRowIndex + GetRowCount() - 1)); - result = aReflowState.ComputedHeight() - cellSpacing; + result = aReflowState.ComputedBSize() - cellSpacing; } else { const nsHTMLReflowState* parentRS = aReflowState.parentReflowState; @@ -1527,9 +1571,9 @@ nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) parentRS = parentRS->parentReflowState; } if (parentRS && (tableFrame == parentRS->frame) && - (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { + (parentRS->ComputedBSize() > 0) && (parentRS->ComputedBSize() < NS_UNCONSTRAINEDSIZE)) { nscoord cellSpacing = tableFrame->GetRowSpacing(-1, tableFrame->GetRowCount()); - result = parentRS->ComputedHeight() - cellSpacing; + result = parentRS->ComputedBSize() - cellSpacing; } } @@ -1600,11 +1644,10 @@ nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const } #endif -nsMargin* -nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder) +LogicalMargin +nsTableRowGroupFrame::GetBCBorderWidth(WritingMode aWM) { - aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0; - + LogicalMargin border(aWM); nsTableRowFrame* firstRowFrame = nullptr; nsTableRowFrame* lastRowFrame = nullptr; for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { @@ -1614,28 +1657,29 @@ nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder) lastRowFrame = rowFrame; } if (firstRowFrame) { - aBorder.top = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth()); - aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth()); + border.BStart(aWM) = nsPresContext:: + CSSPixelsToAppUnits(firstRowFrame->GetBStartBCBorderWidth()); + border.BEnd(aWM) = nsPresContext:: + CSSPixelsToAppUnits(lastRowFrame->GetBEndBCBorderWidth()); } - - return &aBorder; + return border; } -void nsTableRowGroupFrame::SetContinuousBCBorderWidth(uint8_t aForSide, +void nsTableRowGroupFrame::SetContinuousBCBorderWidth(LogicalSide aForSide, BCPixelSize aPixelValue) { switch (aForSide) { - case NS_SIDE_RIGHT: - mRightContBorderWidth = aPixelValue; + case eLogicalSideIEnd: + mIEndContBorderWidth = aPixelValue; return; - case NS_SIDE_BOTTOM: - mBottomContBorderWidth = aPixelValue; + case eLogicalSideBEnd: + mBEndContBorderWidth = aPixelValue; return; - case NS_SIDE_LEFT: - mLeftContBorderWidth = aPixelValue; + case eLogicalSideIStart: + mIStartContBorderWidth = aPixelValue; return; default: - NS_ERROR("invalid NS_SIDE argument"); + NS_ERROR("invalid LogicalSide argument"); } } diff --git a/layout/tables/nsTableRowGroupFrame.h b/layout/tables/nsTableRowGroupFrame.h index bc10b9a5f5..fc8ec82a21 100644 --- a/layout/tables/nsTableRowGroupFrame.h +++ b/layout/tables/nsTableRowGroupFrame.h @@ -13,6 +13,7 @@ #include "nsTablePainter.h" #include "nsTArray.h" #include "nsTableFrame.h" +#include "mozilla/WritingModes.h" class nsTableRowFrame; @@ -22,18 +23,20 @@ struct nsRowGroupReflowState { nsTableFrame* tableFrame; // The available size (computed from the parent) - nsSize availSize; + mozilla::LogicalSize availSize; - // Running y-offset - nscoord y; + // Running block-offset + nscoord bCoord; nsRowGroupReflowState(const nsHTMLReflowState& aReflowState, nsTableFrame* aTableFrame) - :reflowState(aReflowState), tableFrame(aTableFrame) + : reflowState(aReflowState) + , tableFrame(aTableFrame) + , availSize(aReflowState.GetWritingMode(), + aReflowState.AvailableISize(), + aReflowState.AvailableBSize()) + , bCoord(0) { - availSize.width = reflowState.AvailableWidth(); - availSize.height = reflowState.AvailableHeight(); - y = 0; } ~nsRowGroupReflowState() {} @@ -97,11 +100,11 @@ public: const nsDisplayListSet& aLists) override; /** calls Reflow for all of its child rows. - * Rows are all set to the same width and stacked vertically. + * Rows are all set to the same isize and stacked in the block direction. *

rows are not split unless absolutely necessary. * - * @param aDesiredSize width set to width of rows, height set to - * sum of height of rows that fit in aMaxSize.height. + * @param aDesiredSize isize set to isize of rows, bsize set to + * sum of bsize of rows that fit in AvailableBSize. * * @see nsIFrame::Reflow */ @@ -125,6 +128,9 @@ public: virtual nsresult GetFrameName(nsAString& aResult) const override; #endif + virtual mozilla::WritingMode GetWritingMode() const override + { return GetTableFrame()->GetWritingMode(); } + /** return the number of child rows (not necessarily == number of child frames) */ int32_t GetRowCount(); @@ -154,36 +160,40 @@ public: /** - * Get the total height of all the row rects + * Get the total bsize of all the row rects */ - nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); + nscoord GetBSizeBasis(const nsHTMLReflowState& aReflowState); - nsMargin* GetBCBorderWidth(nsMargin& aBorder); + mozilla::LogicalMargin GetBCBorderWidth(mozilla::WritingMode aWM); /** * Gets inner border widths before collapsing with cell borders - * Caller must get top border from previous row group or from table - * GetContinuousBCBorderWidth will not overwrite aBorder.top + * Caller must get bstart border from previous row group or from table + * GetContinuousBCBorderWidth will not overwrite aBorder.BStart() * see nsTablePainter about continuous borders */ - void GetContinuousBCBorderWidth(nsMargin& aBorder); + void GetContinuousBCBorderWidth(mozilla::WritingMode aWM, + mozilla::LogicalMargin& aBorder); + /** * Sets full border widths before collapsing with cell borders - * @param aForSide - side to set; only right, left, and bottom valid + * @param aForSide - side to set; only IEnd, IStart, BEnd are valid */ - void SetContinuousBCBorderWidth(uint8_t aForSide, + void SetContinuousBCBorderWidth(mozilla::LogicalSide aForSide, BCPixelSize aPixelValue); /** - * Adjust to the effect of visibibility:collapse on the row group and + * Adjust to the effect of visibility:collapse on the row group and * its children - * @return additional shift upward that should be applied to - * subsequent rowgroups due to rows and this rowgroup - * being collapsed - * @param aYTotalOffset the total amount that the rowgroup is shifted up - * @param aWidth new width of the rowgroup + * @return additional shift bstart-wards that should be applied + * to subsequent rowgroups due to rows and this + * rowgroup being collapsed + * @param aBTotalOffset the total amount that the rowgroup is shifted + * @param aISize new isize of the rowgroup + * @param aWM the table's writing mode */ - nscoord CollapseRowGroupIfNecessary(nscoord aYTotalOffset, - nscoord aWidth); + nscoord CollapseRowGroupIfNecessary(nscoord aBTotalOffset, + nscoord aISize, + mozilla::WritingMode aWM); // nsILineIterator methods public: @@ -343,14 +353,16 @@ protected: void PlaceChild(nsPresContext* aPresContext, nsRowGroupReflowState& aReflowState, nsIFrame* aKidFrame, - nsPoint aKidPosition, + mozilla::WritingMode aWM, + const mozilla::LogicalPoint& aKidPosition, + nscoord aContainerWidth, nsHTMLReflowMetrics& aDesiredSize, const nsRect& aOriginalKidRect, const nsRect& aOriginalKidVisualOverflow); - void CalculateRowHeights(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState); + void CalculateRowBSizes(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState); void DidResizeRows(nsHTMLReflowMetrics& aDesiredSize); @@ -401,15 +413,15 @@ protected: private: // border widths in pixels in the collapsing border model - BCPixelSize mRightContBorderWidth; - BCPixelSize mBottomContBorderWidth; - BCPixelSize mLeftContBorderWidth; + BCPixelSize mIEndContBorderWidth; + BCPixelSize mBEndContBorderWidth; + BCPixelSize mIStartContBorderWidth; public: bool IsRepeatable() const; - void SetRepeatable(bool aRepeatable); - bool HasStyleHeight() const; - void SetHasStyleHeight(bool aValue); + void SetRepeatable(bool aRepeatable); + bool HasStyleBSize() const; + void SetHasStyleBSize(bool aValue); bool HasInternalBreakBefore() const; bool HasInternalBreakAfter() const; }; @@ -429,30 +441,30 @@ inline void nsTableRowGroupFrame::SetRepeatable(bool aRepeatable) } } -inline bool nsTableRowGroupFrame::HasStyleHeight() const +inline bool nsTableRowGroupFrame::HasStyleBSize() const { - return (mState & NS_ROWGROUP_HAS_STYLE_HEIGHT) == NS_ROWGROUP_HAS_STYLE_HEIGHT; + return (mState & NS_ROWGROUP_HAS_STYLE_BSIZE) == NS_ROWGROUP_HAS_STYLE_BSIZE; } -inline void nsTableRowGroupFrame::SetHasStyleHeight(bool aValue) +inline void nsTableRowGroupFrame::SetHasStyleBSize(bool aValue) { if (aValue) { - mState |= NS_ROWGROUP_HAS_STYLE_HEIGHT; + mState |= NS_ROWGROUP_HAS_STYLE_BSIZE; } else { - mState &= ~NS_ROWGROUP_HAS_STYLE_HEIGHT; + mState &= ~NS_ROWGROUP_HAS_STYLE_BSIZE; } } inline void -nsTableRowGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder) +nsTableRowGroupFrame::GetContinuousBCBorderWidth(mozilla::WritingMode aWM, + mozilla::LogicalMargin& aBorder) { int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); - aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, - mRightContBorderWidth); - aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, - mBottomContBorderWidth); - aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, - mLeftContBorderWidth); - return; + aBorder.IEnd(aWM) = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mIEndContBorderWidth); + aBorder.BEnd(aWM) = BC_BORDER_START_HALF_COORD(aPixelsToTwips, + mBEndContBorderWidth); + aBorder.IStart(aWM) = BC_BORDER_END_HALF_COORD(aPixelsToTwips, + mIStartContBorderWidth); } #endif diff --git a/toolkit/components/places/PlacesTransactions.jsm b/toolkit/components/places/PlacesTransactions.jsm index bbab281c9c..0cb22bcdc6 100644 --- a/toolkit/components/places/PlacesTransactions.jsm +++ b/toolkit/components/places/PlacesTransactions.jsm @@ -199,10 +199,12 @@ TransactionsHistory.__proto__ = { get undoPosition() this._undoPosition, // Handy shortcuts - get topUndoEntry() this.undoPosition < this.length ? - this[this.undoPosition] : null, - get topRedoEntry() this.undoPosition > 0 ? - this[this.undoPosition - 1] : null, + get topUndoEntry() { + return this.undoPosition < this.length ? this[this.undoPosition] : null; + }, + get topRedoEntry() { + return this.undoPosition > 0 ? this[this.undoPosition - 1] : null; + }, // Outside of this module, the API of transactions is inaccessible, and so // are any internal properties. To achieve that, transactions are proxified @@ -219,7 +221,9 @@ TransactionsHistory.__proto__ = { */ proxifyTransaction: function (aRawTransaction) { let proxy = Object.freeze({ - transact() TransactionsManager.transact(this) + transact() { + return TransactionsManager.transact(this); + } }); this.proxifiedToRaw.set(proxy, aRawTransaction); return proxy; @@ -232,8 +236,9 @@ TransactionsHistory.__proto__ = { * @return true if aValue is the proxy object for some transaction, false * otherwise. */ - isProxifiedTransactionObject: - function (aValue) this.proxifiedToRaw.has(aValue), + isProxifiedTransactionObject(aValue) { + return this.proxifiedToRaw.has(aValue); + }, /** * Get the raw transaction for the given proxy. @@ -242,7 +247,9 @@ TransactionsHistory.__proto__ = { * @return the transaction proxified by aProxy; |undefined| is returned if * aProxy is not a proxified transaction. */ - getRawTransaction(aProxy) this.proxifiedToRaw.get(aProxy), + getRawTransaction(aProxy) { + return this.proxifiedToRaw.get(aProxy); + }, /** * Add a transaction either as a new entry, if forced or if there are no undo @@ -340,7 +347,9 @@ let PlacesTransactions = { * @note All undo manager operations are queued. This means that transactions * history may change by the time your request is fulfilled. */ - undo() TransactionsManager.undo(), + undo() { + return TransactionsManager.undo(); + }, /** * Asynchronously redo the transaction immediately before the current undo @@ -351,7 +360,9 @@ let PlacesTransactions = { * @note All undo manager operations are queued. This means that transactions * history may change by the time your request is fulfilled. */ - redo() TransactionsManager.redo(), + redo() { + return TransactionsManager.redo(); + }, /** * Asynchronously clear the undo, redo, or all entries from the transactions @@ -367,13 +378,16 @@ let PlacesTransactions = { * @note All undo manager operations are queued. This means that transactions * history may change by the time your request is fulfilled. */ - clearTransactionsHistory(aUndoEntries = true, aRedoEntries = true) - TransactionsManager.clearTransactionsHistory(aUndoEntries, aRedoEntries), + clearTransactionsHistory(aUndoEntries = true, aRedoEntries = true) { + return TransactionsManager.clearTransactionsHistory(aUndoEntries, aRedoEntries); + }, /** * The numbers of entries in the transactions history. */ - get length() TransactionsHistory.length, + get length() { + return TransactionsHistory.length; + }, /** * Get the transaction history entry at a given index. Each entry consists @@ -400,17 +414,23 @@ let PlacesTransactions = { * Entries past this point * Entries at and past this point are redo entries. */ - get undoPosition() TransactionsHistory.undoPosition, + get undoPosition() { + return TransactionsHistory.undoPosition; + }, /** * Shortcut for accessing the top undo entry in the transaction history. */ - get topUndoEntry() TransactionsHistory.topUndoEntry, + get topUndoEntry() { + return TransactionsHistory.topUndoEntry; + }, /** * Shortcut for accessing the top redo entry in the transaction history. */ - get topRedoEntry() TransactionsHistory.topRedoEntry + get topRedoEntry() { + return TransactionsHistory.topRedoEntry; + } }; /** @@ -461,7 +481,9 @@ Enqueuer.prototype = { /** * The promise for this queue. */ - get promise() this._promise + get promise() { + return this._promise; + } }; let TransactionsManager = { diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm index 0d8d49679b..49eda1ee72 100644 --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -395,8 +395,9 @@ this.PlacesUtils = { * the URL to generate a rev host for. * @return the reversed host string. */ - getReversedHost(url) - url.host.split("").reverse().join("") + ".", + getReversedHost(url) { + return url.host.split("").reverse().join("") + "."; + }, /** * String-wraps a result node according to the rules of the specified