Bug 616436 - (For feedback only) Column-span implementation. draft
authorNeerja Pancholi <npancholi@mozilla.com>
Fri, 13 Oct 2017 17:56:23 -0700
changeset 680408 74680f284b289b7699489de25a9de08509b59cf4
parent 679509 98247fbf95c260786361e12ad924c4370885f686
child 735851 f277e7a8a20d01b59f57b6b368270ecc56a37869
push id84499
push userbmo:npancholi@mozilla.com
push dateSat, 14 Oct 2017 01:03:07 +0000
bugs616436
milestone58.0a1
Bug 616436 - (For feedback only) Column-span implementation. This is an implementation of the column-span property based on an approach similar to IB-Splitting. The high level algorithm is to break up content under a multicol into ColumnSetFrames and column-span wrappers such that the column-span wrappers are always direct children of the multicol wrapper itself and so take up all available inline space just like any other block level element. This new multicol wrapper that wraps both ColumnSetFrames and column-span wrapper frames is called the 'ColumnSetWrapper'. Some known problems with this implementation are: 1. [FIXED]Crash when using the inspector with any of the test cases. This is currently preventing the debugging of other issues so is highest priority. 2. [Most fixed, in process] Some margin related bugs. 3. [Most fixed, in process] Test failures on try need to be cleaned up. Because of these problems, this patch is not yet ready for review. MozReview-Commit-ID: 7SxGHwE9csv *** AvailableSize, placeholder and continuing frames related crashes fixed MozReview-Commit-ID: DsSDqhazk8M
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/FrameTypeList.h
layout/generic/moz.build
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowContext.cpp
layout/generic/nsColumnSetWrapperFrame.cpp
layout/generic/nsColumnSetWrapperFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrameIdList.h
layout/generic/nsHTMLParts.h
layout/generic/nsIFrame.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsSplittableFrame.cpp
layout/painting/nsCSSRendering.cpp
layout/style/nsCSSAnonBoxList.h
layout/style/res/ua.css
modules/libpref/init/all.js
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -627,16 +627,38 @@ GetIBContainingBlockFor(nsIFrame* aFrame
   // post-conditions
   NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame "
                             "in GetIBContainingBlockFor");
   NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
 
   return parentFrame;
 }
 
+static nsIFrame*
+GetMulticolContainingBlockFor(nsIFrame* aFrame)
+{
+  NS_PRECONDITION(aFrame->HasMulticolAncestor(),
+                  "GetMulticolContainingBlockFor() should only be called on"
+                  "frames inside a multicol element!");
+
+  nsIFrame* currFrame = aFrame->GetParent();
+  while (currFrame){
+    // Find the first non-multicol, non-pseudo styled parent frame
+    if (!currFrame->HasMulticolAncestor() &&
+        !currFrame->StyleContext()->GetPseudo()) {
+      break;
+    }
+    currFrame = currFrame->GetParent();
+  }
+
+  MOZ_ASSERT(currFrame, "No valid ColumnSetWrapper in multicol hierarchy!");
+
+  return currFrame;
+}
+
 // This is a bit slow, but sometimes we need it.
 static bool
 ParentIsWrapperAnonBox(nsIFrame* aParent)
 {
   nsIFrame* maybeAnonBox = aParent;
   if (maybeAnonBox->StyleContext()->GetPseudo() ==
         nsCSSAnonBoxes::cellContent) {
     // The thing that would maybe be a wrapper anon box is the cell.
@@ -678,16 +700,42 @@ FindFirstNonBlock(const nsFrameList& aLi
   for (; !link.AtEnd(); link.Next()) {
     if (link.NextFrame()->IsInlineOutside()) {
       break;
     }
   }
   return link;
 }
 
+static nsFrameList::FrameLinkEnumerator
+FindFirstColumnSpan(const nsFrameList& aList)
+{
+  nsFrameList::FrameLinkEnumerator link(aList);
+  for (; !link.AtEnd(); link.Next()) {
+    if (link.NextFrame()->StyleContext()->StyleColumn()->mColumnSpan
+          == NS_STYLE_COLUMN_SPAN_ALL) {
+      break;
+    }
+  }
+  return link;
+}
+
+static nsFrameList::FrameLinkEnumerator
+FindFirstNonColumnSpan(const nsFrameList& aList)
+{
+  nsFrameList::FrameLinkEnumerator link(aList);
+  for (; !link.AtEnd(); link.Next()) {
+    if (link.NextFrame()->StyleContext()->StyleColumn()->mColumnSpan
+          != NS_STYLE_COLUMN_SPAN_ALL) {
+      break;
+    }
+  }
+  return link;
+}
+
 inline void
 SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame)
 {
   NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
   nsFrameList temp(aFrame, aFrame);
   aParent->SetInitialChildList(kPrincipalList, temp);
 }
 
@@ -9320,16 +9368,19 @@ nsCSSFrameConstructor::CreateContinuingF
     newFrame = NS_NewXULLabelFrame(shell, styleContext);
     newFrame->Init(content, aParentFrame, aFrame);
 #endif
   } else if (LayoutFrameType::ColumnSet == frameType) {
     MOZ_ASSERT(!aFrame->IsTableCaption(),
                "no support for fragmenting table captions yet");
     newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0));
     newFrame->Init(content, aParentFrame, aFrame);
+  } else if (LayoutFrameType::ColumnSetWrapper == frameType) {
+    newFrame = NS_NewColumnSetWrapperFrame(shell, styleContext, nsFrameState(0));
+    newFrame->Init(content, aParentFrame, aFrame);
   } else if (LayoutFrameType::Page == frameType) {
     nsContainerFrame* canvasFrame;
     newFrame = ConstructPageFrame(shell, aParentFrame, aFrame, canvasFrame);
   } else if (LayoutFrameType::TableWrapper == frameType) {
     newFrame =
       CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame,
                                       content, styleContext);
 
@@ -9751,16 +9802,21 @@ FindPreviousNonWhitespaceSibling(nsIFram
 bool
 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "Must have a frame");
   NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
   NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
                   "aFrame not the result of GetPrimaryFrame()?");
 
+  if (aFrame->HasMulticolAncestor()) {
+    ReframeContainingBlock(aFrame);
+    return true;
+  }
+
   if (IsFramePartOfIBSplit(aFrame)) {
     // The removal functions can't handle removal of an {ib} split directly; we
     // need to rebuild the containing block.
 #ifdef DEBUG
     if (gNoisyContentUpdates) {
       printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
              "frame=");
       nsFrame::ListTag(stdout, aFrame);
@@ -12409,68 +12465,321 @@ nsCSSFrameConstructor::ConstructBlock(ns
   // Create column wrapper if necessary
   nsContainerFrame* blockFrame = *aNewFrame;
   NS_ASSERTION((blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame()),
                "not a block frame nor a details frame?");
   nsContainerFrame* parent = aParentFrame;
   RefPtr<nsStyleContext> blockStyle = aStyleContext;
   const nsStyleColumn* columns = aStyleContext->StyleColumn();
 
+  // Something to let us know that we are constructing blocks inside a multicol
+  // parent.
+  blockFrame->SetHasMulticolAncestor(parent->HasMulticolAncestor());
+
   if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
       || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
-    nsContainerFrame* columnSetFrame =
-      NS_NewColumnSetFrame(mPresShell, aStyleContext,
-                           nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
-
-    InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
+
+    nsContainerFrame* columnSetWrapper =
+      NS_NewColumnSetWrapperFrame(mPresShell,
+                                  aStyleContext,
+                                  nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
+    InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper);
+
     blockStyle = mPresShell->StyleSet()->
       ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
                                          aStyleContext);
-    parent = columnSetFrame;
-    *aNewFrame = columnSetFrame;
+
+    parent = columnSetWrapper;
+    *aNewFrame = columnSetWrapper;
     if (aPositionedFrameForAbsPosContainer == blockFrame) {
-      aPositionedFrameForAbsPosContainer = columnSetFrame;
-    }
-
-    SetInitialSingleChild(columnSetFrame, blockFrame);
+      aPositionedFrameForAbsPosContainer = columnSetWrapper;
+    }
+    blockFrame->SetHasMulticolAncestor(true);
+
+  } else if (parent->HasMulticolAncestor() &&
+             columns->mColumnSpan == NS_STYLE_COLUMN_SPAN_ALL) {
+    // Column span *inside a multicol parent* must be a BFC, therefore recreate
+    // it as such.
+    nsContainerFrame* spanner =
+      NS_NewBlockFormattingContext(mPresShell, blockStyle);
+    *aNewFrame = spanner;
+    blockFrame->Destroy();
+    blockFrame = spanner;
+    blockFrame->SetHasMulticolAncestor(true);
   }
 
   blockFrame->SetStyleContextWithoutNotification(blockStyle);
   InitAndRestoreFrame(aState, aContent, parent, blockFrame);
 
-  aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext,
-                  aContentParentFrame ? aContentParentFrame :
-                                        aParentFrame);
   if (!mRootElementFrame) {
     // The frame we're constructing will be the root element frame.
     // Set mRootElementFrame before processing children.
     mRootElementFrame = *aNewFrame;
   }
 
+  aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext,
+                  aContentParentFrame ? aContentParentFrame : aParentFrame);
   // We should make the outer frame be the absolute containing block,
   // if one is required. We have to do this because absolute
   // positioning must be computed with respect to the CSS dimensions
   // of the element, which are the dimensions of the outer block. But
   // we can't really do that because only blocks can have absolute
   // children. So use the block and try to compensate with hacks
   // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
   nsFrameConstructorSaveState absoluteSaveState;
   (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
   if (aPositionedFrameForAbsPosContainer) {
-    //    NS_ASSERTION(aRelPos, "should have made area frame for this");
-    aState.PushAbsoluteContainingBlock(*aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
+    aState.PushAbsoluteContainingBlock(*aNewFrame,
+                                       aPositionedFrameForAbsPosContainer,
+                                       absoluteSaveState);
   }
 
   // Process the child content
   nsFrameItems childItems;
   ProcessChildren(aState, aContent, aStyleContext, blockFrame, true,
                   childItems, true, aPendingBinding);
 
-  // Set the frame's initial child list
-  blockFrame->SetInitialChildList(kPrincipalList, childItems);
+  if (!DoChildrenNeedSplitting(blockFrame, childItems)) {
+    // Set the frame's initial child list
+    (*aNewFrame)->SetInitialChildList(kPrincipalList, childItems);
+
+    if ((*aNewFrame)->Type() == LayoutFrameType::ColumnSetWrapper) {
+      // Edge case where we have an empty column set wrapper. This can only
+      // happen when NewFrame and blockFrame are different and NewFrame is the
+      // wrapper and it's temporary child blockFrame has no children.
+      // In this case, we don't need the temporary child blockFrame anymore
+      // so just destroy it.
+      // Note: An empty column set wrapper is ok to have since it represents
+      // the case of an empty multicol element.
+      MOZ_ASSERT((*aNewFrame) != blockFrame,
+                 "NewFrame and blockFrame must be different here!");
+      MOZ_ASSERT(childItems.IsEmpty(),
+                 "NewFrame can only be columnSetWrapper when blockFrame's "
+                 "childItems are empty!");
+      (*aNewFrame)->RemoveStateBits(NS_FRAME_OWNS_ANON_BOXES);
+      blockFrame->Destroy();
+    }
+    return;
+  }
+
+  ProcessColumnSpan(aState, blockFrame, aNewFrame, childItems, aFrameItems,
+                    aContentParentFrame ? aContentParentFrame : aParentFrame,
+                    aPositionedFrameForAbsPosContainer);
+}
+
+bool
+nsCSSFrameConstructor::DoChildrenNeedSplitting(nsContainerFrame* aFrame,
+                                               nsFrameItems&     aChildItems)
+{
+  if (aFrame->StyleContext()->StyleColumn()->mColumnSpan
+        != NS_STYLE_COLUMN_SPAN_ALL &&
+      aChildItems.NotEmpty() &&
+      aFrame->HasMulticolAncestor() &&
+      !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
+    // The childen of a column-span will never need to be split because
+    // even if there is a nested column-span inside it, the nested column-span
+    // is not inside the BFC of a multicol ancestor since it's parent
+    // column-span sets up its own BFC. Therefore, the nested column-span
+    // is the same case as a column-span outside of a multicol.
+    // A frame with no children doesn't need splitting because there are
+    // no children to split.
+    // Anything under a multicol parent (other than column-spans themselves and
+    // frames with no childen as above) will need to be 'split' whether it
+    // has column-span children or not because we must recreate the frame
+    // hierarchy even for non column-span containing branches of the frame tree.
+    return true;
+  }
+
+  return false;
+}
+
+void
+nsCSSFrameConstructor::
+ProcessColumnSpan(nsFrameConstructorState& aState,
+                  nsContainerFrame*        aOldParent,
+                  nsContainerFrame**       aNewFrame,
+                  nsFrameItems&            aChildItems,
+                  nsFrameItems&            aFrameItems,
+                  nsContainerFrame*        aFinalParent,
+                  nsIFrame*                aPositionedFrameForAbsPosContainer)
+{
+  MOZ_ASSERT(aChildItems.NotEmpty(), "Child items cannot be empty here!");
+
+  nsContainerFrame* grandParent = aOldParent->GetParent();
+  MOZ_ASSERT(grandParent, "Parent cannot be empty here!");
+  nsIContent* content = aOldParent->GetContent();
+  nsStyleContext* styleContext = aOldParent->StyleContext();
+
+  if ((*aNewFrame)->Type() != LayoutFrameType::ColumnSetWrapper) {
+    MOZ_ASSERT(grandParent->Type() != LayoutFrameType::ColumnSetWrapper,
+                "Grandparent cannot be columnSetWrapper! In that case newFrame==gparent");
+
+    nsFrameItems splitChildren;
+    SplitBlocks(aState, aOldParent, aChildItems, splitChildren);
+    MOZ_ASSERT(splitChildren.NotEmpty(), "Child items cannot be empty here!");
+
+    // Clean up the aOldParent and its placeholder, if present
+    aFrameItems.RemoveFrame(aOldParent);
+    //aOldParent is guaranteed to be a block here
+    nsBlockFrame* oldBlockParent = static_cast<nsBlockFrame*>(aOldParent);
+    oldBlockParent->TransferFloats();
+    aOldParent->Destroy();
+
+    *aNewFrame = static_cast<nsContainerFrame*>(splitChildren.FirstChild());
+    while (splitChildren.NotEmpty()) {
+      nsContainerFrame* currChild =
+          static_cast<nsContainerFrame*>(splitChildren.RemoveFirstChild());
+      aState.AddChild(currChild, aFrameItems, content, styleContext, aFinalParent);
+
+      // We should make the outer frame be the absolute containing block,
+      // if one is required. We have to do this because absolute
+      // positioning must be computed with respect to the CSS dimensions
+      // of the element, which are the dimensions of the outer block. But
+      // we can't really do that because only blocks can have absolute
+      // children. So use the block and try to compensate with hacks
+      // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
+      nsFrameConstructorSaveState absoluteSaveState;
+      currChild->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+      if (aPositionedFrameForAbsPosContainer) {
+        aState.PushAbsoluteContainingBlock(currChild,
+                                           aPositionedFrameForAbsPosContainer,
+                                           absoluteSaveState);
+      }
+    }
+  } else {
+    nsFrameItems finalChildItems;
+    WrapNonSpannerChildrenInColumnSets(aState, aOldParent, aChildItems,
+                                       finalChildItems);
+    MOZ_ASSERT(finalChildItems.NotEmpty(), "Child items cannot be empty here!");
+
+    // aOldParent is guaranteed to be a block here
+    nsBlockFrame* oldBlockParent = static_cast<nsBlockFrame*>(aOldParent);
+    oldBlockParent->TransferFloats();
+
+    MoveChildrenTo(aOldParent, grandParent, finalChildItems);
+
+    // Clean up the aOldParent
+    // Note: In this case aOldParent was never added to aFrameItems
+    aOldParent->Destroy();
+  }
+}
+
+void
+nsCSSFrameConstructor::SplitBlocks(nsFrameConstructorState& aState,
+                                   nsContainerFrame*        aOldParent,
+                                   nsFrameList&             aUnsplitChildItems,
+                                   nsFrameItems&            aSplitChildItems,
+                                   nsStyleContext*          aNonSpannerSC)
+{
+  MOZ_ASSERT(aUnsplitChildItems.NotEmpty(), "Child items cannot be empty here!");
+
+  nsBlockFrame* previousSplitBlock = nullptr;
+  while (aUnsplitChildItems.NotEmpty()) {
+    nsContainerFrame* grandParent = aOldParent->GetParent();
+    MOZ_ASSERT(grandParent, "Parent cannot be empty here!");
+    nsIContent* content = aOldParent->GetContent();
+    nsStyleContext* styleContext = aOldParent->StyleContext();
+
+    nsBlockFrame* splitBlock = NS_NewBlockFrame(mPresShell, styleContext);
+    InitAndRestoreFrame(aState, content, grandParent, splitBlock, false);
+    splitBlock->SetHasMulticolAncestor(aOldParent->HasMulticolAncestor());
+
+    nsFrameList::
+    FrameLinkEnumerator firstColumnSpan = FindFirstColumnSpan(aUnsplitChildItems);
+    nsFrameList splitBlockChildren = aUnsplitChildItems.ExtractHead(firstColumnSpan);
+
+    if (splitBlockChildren.IsEmpty()) {
+      // If the extracted child item list is empty, this means that the spanner
+      // is the first child/ first few children inside childItems so create
+      // a block that only contains spanners (as opposed to only non spanners)
+
+      RefPtr<nsStyleContext> spannerSC = mPresShell->StyleSet()->
+      ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::mozColumnSpanWrapper,
+                                         styleContext);
+      splitBlock->SetStyleContextWithoutNotification(spannerSC);
+
+      nsFrameList::
+      FrameLinkEnumerator firstNonColumnSpan = FindFirstNonColumnSpan(aUnsplitChildItems);
+      splitBlockChildren = aUnsplitChildItems.ExtractHead(firstNonColumnSpan);
+    } else if (aNonSpannerSC) {
+      splitBlock->SetStyleContextWithoutNotification(aNonSpannerSC);
+    }
+
+    MoveChildrenTo(aOldParent, splitBlock, splitBlockChildren);
+    aSplitChildItems.AddChild(splitBlock);
+
+    if (previousSplitBlock) {
+      SetFrameIsIBSplit(previousSplitBlock, splitBlock);
+    }
+    previousSplitBlock = splitBlock;
+  }
+  SetFrameIsIBSplit(previousSplitBlock, nullptr);
+}
+
+void
+nsCSSFrameConstructor::
+WrapNonSpannerChildrenInColumnSets(nsFrameConstructorState& aState,
+                                   nsContainerFrame*        aOldParent,
+                                   nsFrameItems&            aInitialChildItems,
+                                   nsFrameItems&            aFinalChildItems)
+{
+  nsIFrame* parent = aOldParent->GetParent();
+  MOZ_ASSERT(parent, "Parent cannot be null!");
+
+  nsContainerFrame* columnSetWrapper = static_cast<nsContainerFrame*>(parent);
+  MOZ_ASSERT(columnSetWrapper->Type() == LayoutFrameType::ColumnSetWrapper,
+             "The grandparent must be a columnSetWrapper here!");
+
+  nsIContent* content = columnSetWrapper->GetContent();
+  nsStyleContext* styleContext = columnSetWrapper->StyleContext();
+
+  RefPtr<nsStyleContext> columnSetStyle = mPresShell->StyleSet()->
+  ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::mozColumnSet,
+                                     styleContext);
+
+  RefPtr<nsStyleContext> blockStyle = mPresShell->StyleSet()->
+  ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
+                                     styleContext);
+
+  while (aInitialChildItems.NotEmpty()) {
+    nsFrameList::
+    FrameLinkEnumerator firstColumnSpan = FindFirstColumnSpan(aInitialChildItems);
+    nsFrameList children = aInitialChildItems.ExtractHead(firstColumnSpan);
+
+    if (children.IsEmpty()) {
+      // If the extracted child item list is empty, this means that the spanner
+      // is the first child/ first few children inside childItems
+      nsFrameList::FrameLinkEnumerator firstNonColumnSpan =
+        FindFirstNonColumnSpan(aInitialChildItems);
+      children = aInitialChildItems.ExtractHead(firstNonColumnSpan);
+      aFinalChildItems.AppendFrames(columnSetWrapper, children);
+      continue;
+    }
+
+    nsContainerFrame* columnSetFrame =
+      NS_NewColumnSetFrame(mPresShell, columnSetStyle,
+                           nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
+    InitAndRestoreFrame(aState, content, columnSetWrapper, columnSetFrame);
+
+    nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockStyle);
+    InitAndRestoreFrame(aState, content, columnSetFrame, blockFrame);
+    blockFrame->AddStateBits(aOldParent->GetStateBits());
+
+    MoveChildrenTo(aOldParent, blockFrame, children);
+    SetInitialSingleChild(columnSetFrame, blockFrame);
+
+    // Setting parent to columnSetWrapper is unnecessary here because
+    // MoveChildrenTo (later on) will reparent the finalchildList
+    aFinalChildItems.AppendFrame(columnSetWrapper, columnSetFrame);
+  }
+
+  // Note: Because of this function, column-span wrappers and ColumnSetFrame
+  // wrappers are not ib-siblings of each other since we create columnSetFrames
+  // as one additional wrapper around non-spanning elements and this would upset
+  // the IB-sibling chain.
 }
 
 nsIFrame*
 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
                                        FrameConstructionItem&   aItem,
                                        nsContainerFrame*        aParentFrame,
                                        const nsStyleDisplay*    aDisplay,
                                        nsFrameItems&            aFrameItems)
@@ -12554,16 +12863,19 @@ nsCSSFrameConstructor::ConstructInline(n
 
   newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
   if (positioned) {
     // Relatively positioned frames becomes a container for child
     // frames that are positioned
     aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
   }
 
+  // Mark if this inline is under a multicol element
+  newFrame->SetHasMulticolAncestor(aParentFrame->HasMulticolAncestor());
+
   // Process the child content
   nsFrameItems childItems;
   ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
                               /* aParentIsWrapperAnonBox = */ false,
                               childItems);
 
   nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
   if (!aItem.mIsAllInline) {
@@ -12603,17 +12915,17 @@ nsCSSFrameConstructor::CreateIBSiblings(
                                         bool aIsPositioned,
                                         nsFrameItems& aChildItems,
                                         nsFrameItems& aSiblings)
 {
   nsIContent* content = aInitialInline->GetContent();
   nsStyleContext* styleContext = aInitialInline->StyleContext();
   nsContainerFrame* parentFrame = aInitialInline->GetParent();
 
-  // Resolve the right style context for our anonymous blocks.
+  // Resolve the right style context for our non-spanner anonymous blocks.
   // The distinction in styles is needed because of CSS 2.1, section
   // 9.2.1.1, which says:
   //   When such an inline box is affected by relative positioning, any
   //   resulting translation also affects the block-level box contained
   //   in the inline box.
   RefPtr<nsStyleContext> blockSC = mPresShell->StyleSet()->
     ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::mozBlockInsideInlineWrapper,
                                        styleContext);
@@ -12622,32 +12934,35 @@ nsCSSFrameConstructor::CreateIBSiblings(
     static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
   do {
     // On entry to this loop aChildItems is not empty and the first frame in it
     // is block-level.
     NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
     NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
                     "Must have list starting with block");
 
-    // The initial run of blocks belongs to an anonymous block that we create
-    // right now. The anonymous block will be the parent of these block
-    // children of the inline.
-    nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
-    InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
-
     // Find the first non-block child which defines the end of our block kids
     // and the start of our next inline's kids
     nsFrameList::FrameLinkEnumerator firstNonBlock =
       FindFirstNonBlock(aChildItems);
     nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
 
-    MoveChildrenTo(aInitialInline, blockFrame, blockKids);
-
-    SetFrameIsIBSplit(lastNewInline, blockFrame);
-    aSiblings.AddChild(blockFrame);
+    // The initial run of blocks need to be split into runs of blocks with
+    // and without column-span:all and wrapped with anonymous blocks.
+    // This is needed because blocks with column-span:all must be
+    // given an anonymous block wrapper with a pseudo style to distinguish
+    // them from regular blocks so we can perform column-span
+    // related splitting later, if needed.
+    nsFrameItems splitBlocks;
+    SplitBlocks(aState, aInitialInline, blockKids, splitBlocks, blockSC);
+    MOZ_ASSERT(splitBlocks.NotEmpty(), "List of split blocks cannot be empty!");
+
+    SetFrameIsIBSplit(lastNewInline,
+                      static_cast<nsContainerFrame*>(splitBlocks.FirstChild()));
+    aSiblings.AppendFrames(parentFrame, splitBlocks);
 
     // Now grab the initial inlines in aChildItems and put them into an inline
     // frame.
     nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
     InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
     inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
                               NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
     if (aIsPositioned) {
@@ -12657,17 +12972,19 @@ nsCSSFrameConstructor::CreateIBSiblings(
     if (aChildItems.NotEmpty()) {
       nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
       FindFirstBlock(firstBlock);
       nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
 
       MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
     }
 
-    SetFrameIsIBSplit(blockFrame, inlineFrame);
+    inlineFrame->SetHasMulticolAncestor(aInitialInline->HasMulticolAncestor());
+    SetFrameIsIBSplit(static_cast<nsContainerFrame*>(aSiblings.LastChild()),
+                      inlineFrame);
     aSiblings.AddChild(inlineFrame);
     lastNewInline = inlineFrame;
   } while (aChildItems.NotEmpty());
 
   SetFrameIsIBSplit(lastNewInline, nullptr);
 }
 
 void
@@ -13063,16 +13380,21 @@ nsCSSFrameConstructor::WipeContainingBlo
       RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
       return true;
     }
   }
 
   // Now we have several cases involving {ib} splits.  Put them all in a
   // do/while with breaks to take us to the "go and reconstruct" code.
   do {
+    if (aFrame->HasMulticolAncestor() ||
+        aFrame->Type() == LayoutFrameType::ColumnSetWrapper) {
+      // Need to go ahead and reconstruct.
+      break;
+    }
     if (IsInlineFrame(aFrame)) {
       if (aItems.AreAllItemsInline()) {
         // We can just put the kids in.
         return false;
       }
 
       if (!IsFramePartOfIBSplit(aFrame)) {
         // Need to go ahead and reconstruct.
@@ -13125,21 +13447,22 @@ nsCSSFrameConstructor::WipeContainingBlo
   // enforces that the root is display:none, display:table, or display:block.
   // Note that walking up "too far" is OK in terms of correctness, even if it
   // might be a little inefficient.  This is why we walk out of all
   // pseudo-frames -- telling which ones are or are not OK to walk out of is
   // too hard (and I suspect that we do in fact need to walk out of all of
   // them).
   while (IsFramePartOfIBSplit(aContainingBlock) ||
          aContainingBlock->IsInlineOutside() ||
-         aContainingBlock->StyleContext()->GetPseudo()) {
+         aContainingBlock->StyleContext()->GetPseudo() ||
+         aContainingBlock->HasMulticolAncestor()) {
     aContainingBlock = aContainingBlock->GetParent();
     NS_ASSERTION(aContainingBlock,
-                 "Must have non-inline, non-ib-split, non-pseudo frame as "
-                 "root (or child of root, for a table root)!");
+                 "Must have non-inline, non-ib-split, non-pseudo, non-multicol"
+                 " frame as root (or child of root, for a table root)!");
   }
 
   // Tell parent of the containing block to reformulate the
   // entire block. This is painful and definitely not optimal
   // but it will *always* get the right answer.
 
   nsIContent* blockContent = aContainingBlock->GetContent();
 #ifdef DEBUG
@@ -13172,17 +13495,22 @@ nsCSSFrameConstructor::ReframeContaining
   if (mPresShell->IsReflowLocked()) {
     // don't ReframeContainingBlock, this will result in a crash
     // if we remove a tree that's in reflow - see bug 121368 for testcase
     NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
     return;
   }
 
   // Get the first "normal" ancestor of the target frame.
-  nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
+  nsIFrame* containingBlock = nullptr;
+  if (aFrame->HasMulticolAncestor()) {
+    containingBlock = GetMulticolContainingBlockFor(aFrame);
+  } else {
+    containingBlock = GetIBContainingBlockFor(aFrame);
+  }
   if (containingBlock) {
     // From here we look for the containing block in case the target
     // frame is already a block (which can happen when an inline frame
     // wraps some of its content in an anonymous block; see
     // ConstructInline)
 
     // NOTE: We used to get the FloatContainingBlock here, but it was often wrong.
     // GetIBContainingBlock works much better and provides the correct container in all cases
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1914,16 +1914,63 @@ private:
   bool ShouldHaveFirstLineStyle(nsIContent*      aContent,
                                   nsStyleContext*  aStyleContext);
 
   void ShouldHaveSpecialBlockStyle(nsIContent*      aContent,
                                    nsStyleContext*  aStyleContext,
                                    bool*          aHaveFirstLetterStyle,
                                    bool*          aHaveFirstLineStyle);
 
+  /**
+   * This method includes all column-span related implementation
+   */
+  void ProcessColumnSpan(nsFrameConstructorState& aState,
+                         nsContainerFrame*        aOldParent,
+                         nsContainerFrame**       aNewFrame,
+                         nsFrameItems&            aChildItems,
+                         nsFrameItems&            aFrameItems,
+                         nsContainerFrame*        aFinalParent,
+                         nsIFrame*                aPositionedFrameForAbsPosContainer);
+
+  /**
+   * Checks to see if aFrame is part of a multicol frame heirarchy. If yes,
+   * and aFrame is not a colum-span then returns true since aChildItems will
+   * need to be split via nsCSSFrameConstructor::SplitBlocks (Unless aChildItems
+   * is an empty list in which case we return false)
+   */
+  bool DoChildrenNeedSplitting(nsContainerFrame* aFrame,
+                               nsFrameItems& aChildItems);
+
+  /**
+   * Takes aUnsplitChildItems and reparents consecutive runs of column-span and
+   * non-column span blocks into an anonymous block wrapper. The parent of this
+   * wrapper becomes the original block's grand parent (i.e. it is pushed one
+   * level up in the frame hierarchy).
+   * Each column-span wrapper is given an appropriate style context and each
+   * non-column-span wrapper is either given the parent's style context or
+   * aNonSpannerSC if it exists. It also sets IB-split bits here.
+   * We refer to this process as 'splitting'. The list of wrapped children
+   * is returned as aSplitChildItems.
+   */
+  void SplitBlocks(nsFrameConstructorState& aState,
+                   nsContainerFrame*        aOldParent,
+                   nsFrameList&             aUnsplitChildItems,
+                   nsFrameItems&            aSplitChildItems,
+                   nsStyleContext*          aNonSpannerSC = nullptr);
+
+  /**
+   * Create ColumnSetFrames around any consecutive non-spanning elements
+   * present in aInitialChildItems. Let the spanning items stay as is and return
+   * this new list in aFinalChildItems.
+   */
+  void WrapNonSpannerChildrenInColumnSets(nsFrameConstructorState& aState,
+                                          nsContainerFrame*        aOldParent,
+                                          nsFrameItems&            aInitialChildItems,
+                                          nsFrameItems&            aFinalChildItems);
+
   // |aContentParentFrame| should be null if it's really the same as
   // |aParentFrame|.
   // @param aFrameItems where we want to put the block in case it's in-flow.
   // @param aNewFrame an in/out parameter. On input it is the block to be
   // constructed. On output it is reset to the outermost
   // frame constructed (e.g. if we need to wrap the block in an
   // nsColumnSetFrame.
   // @param aParentFrame is the desired parent for the (possibly wrapped)
--- a/layout/generic/FrameTypeList.h
+++ b/layout/generic/FrameTypeList.h
@@ -12,16 +12,17 @@ FRAME_TYPE(Block)
 FRAME_TYPE(Box)
 FRAME_TYPE(Br)
 FRAME_TYPE(Bullet)
 FRAME_TYPE(BCTableCell)
 FRAME_TYPE(Canvas)
 FRAME_TYPE(CheckboxRadio)
 FRAME_TYPE(ColorControl)
 FRAME_TYPE(ColumnSet)
+FRAME_TYPE(ColumnSetWrapper)
 FRAME_TYPE(ComboboxControl)
 FRAME_TYPE(ComboboxDisplay)
 FRAME_TYPE(Deck)
 FRAME_TYPE(DateTimeControl)
 FRAME_TYPE(Details)
 FRAME_TYPE(FieldSet)
 FRAME_TYPE(FlexContainer)
 FRAME_TYPE(FrameSet)
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -132,16 +132,17 @@ UNIFIED_SOURCES += [
     'MathMLTextRunFactory.cpp',
     'nsAbsoluteContainingBlock.cpp',
     'nsBackdropFrame.cpp',
     'nsBlockFrame.cpp',
     'nsBlockReflowContext.cpp',
     'nsBulletFrame.cpp',
     'nsCanvasFrame.cpp',
     'nsColumnSetFrame.cpp',
+    'nsColumnSetWrapperFrame.cpp',
     'nsContainerFrame.cpp',
     'nsFirstLetterFrame.cpp',
     'nsFlexContainerFrame.cpp',
     'nsFloatManager.cpp',
     'nsFontInflationData.cpp',
     'nsFrame.cpp',
     'nsFrameList.cpp',
     'nsFrameSelection.cpp',
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -951,16 +951,52 @@ nsBlockFrame::GetPrefWidthTightBounds(gf
       }
     }
   }
   data.ForceBreak();
 
   return NS_OK;
 }
 
+void
+nsBlockFrame::UpdateStyleOfOwnedAnonBoxesForColumnSpanSplit(
+  mozilla::ServoRestyleState& aRestyleState) {
+  MOZ_ASSERT(StyleContext()->StyleColumn()->mColumnSpan ==
+    NS_STYLE_COLUMN_SPAN_ALL, "Why call this if we are not the column span?");
+
+  nsIFrame* blockFrame = GetProperty(nsIFrame::IBSplitPrevSibling());
+  if (!blockFrame) {
+    // If this column-span is not an IB-split sibling then no need to restyle it?
+    return;
+  }
+
+  // The later blocks need to get original parent's style.
+  ServoStyleContext* originalStyle = blockFrame->StyleContext()->AsServo();
+
+  // The anonymous block's style inherits from the original parent
+  RefPtr<ServoStyleContext> newContext =
+  aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
+    nsCSSAnonBoxes::mozColumnSpanWrapper, originalStyle);
+
+  MOZ_ASSERT(!GetPrevContinuation(), "Must be first continuation");
+  MOZ_ASSERT(StyleContext()->GetPseudo() == nsCSSAnonBoxes::mozColumnSpanWrapper,
+              "Unexpected kind of style context");
+
+  for (nsIFrame* cont = this; cont; cont = cont->GetNextContinuation()) {
+    cont->SetStyleContext(newContext);
+  }
+
+  nsIFrame* nextSibling = GetProperty(nsIFrame::IBSplitSibling());
+  if (nextSibling) {
+    for (nsIFrame* cont = nextSibling; cont; cont = cont->GetNextContinuation()) {
+      cont->SetStyleContext(originalStyle);
+    }
+  }
+}
+
 /**
  * Return whether aNewAvailableSpace is smaller *on either side*
  * (inline-start or inline-end) than aOldAvailableSpace, so that we know
  * if we need to redo layout on an line, replaced block, or block
  * formatting context, because its height (which we used to compute
  * aNewAvailableSpace) caused it to intersect additional floats.
  */
 static bool
@@ -5679,16 +5715,54 @@ nsBlockFrame::UpdateFirstLetterStyle(Ser
     aRestyleState.StyleSet().ResolveStyleForText(textFrame->GetContent(),
                                                  firstLetterStyle->AsServo());
   textFrame->SetStyleContext(firstTextStyle);
 
   // We don't need to update style for textFrame's continuations: it's already
   // set up to inherit from parentStyle, which is what we want.
 }
 
+void
+nsBlockFrame::TransferFloats()
+{
+  // Remember the next sibling here because RemoveFloat() later resets
+  // the sibling chain.
+  nsIFrame* currFloat = mFloats.FirstChild();
+  nsIFrame* nextFloat = nullptr;
+  if (currFloat) {
+    nextFloat = currFloat->GetNextSibling();
+  }
+  while (currFloat) {
+    nsIFrame* placeholder = currFloat->GetPlaceholderFrame();
+    if (placeholder) {
+      // Find the topmost block parent this placeholder belongs to
+      nsIFrame* finalParent = placeholder->GetParent();
+      while (!finalParent->IsFloatContainingBlock()) {
+        finalParent = finalParent->GetParent();
+      }
+
+      RemoveFloat(currFloat);
+
+      // Add this float to the float list of its new and final parent
+      nsBlockFrame* finalBlockParent = static_cast<nsBlockFrame*>(finalParent);
+      currFloat->SetParent(finalBlockParent);
+      nsFrameList temp(currFloat, currFloat);
+      finalBlockParent->AppendFrames(nsIFrame::kFloatList, temp);
+
+      currFloat = nextFloat;
+      if (nextFloat) {
+        nextFloat = nextFloat->GetNextSibling();
+      }
+    }
+    else {
+      NS_WARNING("An OOF exists with a null placeholder!");
+    }
+  }
+}
+
 static nsIFrame*
 FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
 {
   NS_ASSERTION(aFrame, "must have frame");
   nsIFrame* child;
   while (true) {
     nsIFrame* block = aFrame;
     do {
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -289,16 +289,21 @@ public:
   nscoord GetPrefISize(gfxContext *aRenderingContext) override;
 
   nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
 
   nsresult GetPrefWidthTightBounds(gfxContext* aContext,
                                    nscoord* aX,
                                    nscoord* aXMost) override;
 
+  // Restyles the block wrappers around our column spans.
+  // This will only be called when such wrappers in fact exist.
+  void UpdateStyleOfOwnedAnonBoxesForColumnSpanSplit(
+    mozilla::ServoRestyleState& aRestyleState);
+
   /**
    * Compute the final block size of this frame.
    *
    * @param aReflowInput Data structure passed from parent during reflow.
    * @param aReflowStatus A pointer to the reflow status for when we're finished
    *        doing reflow. this will get set appropriately if the block-size
    *        causes us to exceed the current available (page) block-size.
    * @param aContentBSize The block-size of content, precomputed outside of this
@@ -411,16 +416,18 @@ public:
    * etc, but _not_ first-letter).
    */
   void UpdatePseudoElementStyles(mozilla::ServoRestyleState& aRestyleState);
 
   // Update our first-letter styles during stylo post-traversal.  This needs to
   // be done at a slightly different time than our other pseudo-elements.
   void UpdateFirstLetterStyle(mozilla::ServoRestyleState& aRestyleState);
 
+  void TransferFloats();
+
 protected:
   explicit nsBlockFrame(nsStyleContext* aContext, ClassID aID = kClassID)
     : nsContainerFrame(aContext, aID)
     , mMinWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
     , mPrefWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
   {
 #ifdef DEBUG
   InitDebugFlags();
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -290,16 +290,20 @@ nsBlockReflowContext::ReflowBlock(const 
                       mSpace.BSize(mWritingMode) -
                       usedMargin.BStartEnd(mWritingMode));
     tI = space.LineLeft(mWritingMode, mContainerSize);
     tB = mBCoord;
 
     if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
       aFrameRI.mBlockDelta =
         mOuterReflowInput.mBlockDelta + mBCoord - aLine->BStart();
+
+    // XXXneerja - temporary fix for margin related bug
+    // Parent doesn't pass correct ISize!!
+    aFrameRI.AvailableISize() = space.ISize(mWritingMode);
   }
 
 #ifdef DEBUG
   mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef);
   mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef);
 #endif
 
   mOuterReflowInput.mFloatManager->Translate(tI, tB);
new file mode 100644
--- /dev/null
+++ b/layout/generic/nsColumnSetWrapperFrame.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* rendering wrapper object for css3 multi-column layout */
+
+
+#include "nsColumnSetWrapperFrame.h"
+
+using namespace mozilla;
+
+nsContainerFrame*
+NS_NewColumnSetWrapperFrame(nsIPresShell*   aPresShell,
+                            nsStyleContext* aContext,
+                            nsFrameState    aStateFlags)
+{
+  nsColumnSetWrapperFrame* frame =
+    new (aPresShell) nsColumnSetWrapperFrame(aContext);
+  frame->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT);
+  return frame;
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetWrapperFrame)
+
+nsColumnSetWrapperFrame::nsColumnSetWrapperFrame(nsStyleContext* aContext)
+: nsContainerFrame(aContext, kClassID)
+{
+}
+
+nscoord
+nsColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext)
+{
+  nscoord minISize = 0;
+  DISPLAY_MIN_WIDTH(this, minISize);
+
+  for (nsIFrame* childFrame : mFrames) {
+    nscoord childMinISize =
+    nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+                                         nsLayoutUtils::MIN_ISIZE);
+    minISize = std::max(minISize, childMinISize);
+  }
+  return minISize;
+}
+
+nscoord
+nsColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext)
+{
+  nscoord prefISize = 0;
+  DISPLAY_PREF_WIDTH(this, prefISize);
+
+  for (nsIFrame* childFrame : mFrames) {
+    nscoord childPrefISize =
+    nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+                                         nsLayoutUtils::PREF_ISIZE);
+    prefISize = std::max(prefISize, childPrefISize);
+  }
+  return prefISize;
+}
+
+void
+nsColumnSetWrapperFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                                          const nsDisplayListSet& aLists)
+{
+  DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+  // Our children won't have backgrounds so it doesn't matter where we put them.
+  for (nsIFrame* childFrame : mFrames) {
+    BuildDisplayListForChild(aBuilder, childFrame, aLists);
+  }
+}
+
+void
+nsColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
+{
+  // xxxneerja
+  // Seems like we only append the first child, is this correct?
+  if (mFrames.NotEmpty()) {
+    aResult.AppendElement(OwnedAnonBox(mFrames.FirstChild()));
+  }
+}
+
+void
+nsColumnSetWrapperFrame::Reflow(nsPresContext*     aPresContext,
+                                ReflowOutput&      aDesiredSize,
+                                const ReflowInput& aReflowInput,
+                                nsReflowStatus&    aStatus)
+{
+  MarkInReflow();
+  DO_GLOBAL_REFLOW_COUNT("nsColumnSetWrapperFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+  MOZ_ASSERT(aStatus.IsEmpty(), "Expecting outparam to be initially empty");
+
+  if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
+    return;
+  }
+
+  WritingMode wm = aReflowInput.GetWritingMode();
+  LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
+
+  nscoord availableISize = NS_UNCONSTRAINEDSIZE;
+  nscoord availableBSize = NS_UNCONSTRAINEDSIZE;
+  if (aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE) {
+    availableISize = aReflowInput.AvailableISize() - borderPadding.IStartEnd(wm);
+  }
+  if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
+    availableBSize = aReflowInput.AvailableISize() - borderPadding.IStartEnd(wm);
+  }
+  LogicalSize availableSpace(wm, availableISize, availableBSize);
+  nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
+
+  aDesiredSize.ClearSize();
+
+  RenumberList();
+
+  LogicalPoint childOrigin(wm,
+                           borderPadding.IStart(wm),
+                           borderPadding.BStart(wm));
+
+  nscoord columnSpanBottomMargin = 0;
+
+  for (nsIFrame* childFrame : mFrames) {
+    nsReflowStatus childStatus;
+    WritingMode childWm = childFrame->GetWritingMode();
+    ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
+                                 availableSpace);
+    ReflowOutput childDesiredSize(childWm, aDesiredSize.mFlags);
+
+    if (childFrame->StyleContext()->StyleColumn()->mColumnSpan
+          == NS_STYLE_COLUMN_SPAN_ALL) {
+      LogicalMargin computedMargin = childReflowInput.ComputedLogicalMargin();
+      childOrigin.B(childWm) += computedMargin.BStart(childWm);
+      columnSpanBottomMargin = computedMargin.BEnd(childWm);
+      aDesiredSize.BSize(wm) += computedMargin.BStartEnd(childWm);
+    } else if (columnSpanBottomMargin > 0) {
+      childOrigin.B(childWm) += columnSpanBottomMargin;
+      columnSpanBottomMargin = 0;
+    }
+
+    ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
+                childWm, childOrigin, containerSize, 0, childStatus);
+
+    FinishReflowChild(childFrame, aPresContext, childDesiredSize,
+                      &childReflowInput, childWm, childOrigin, containerSize, 0);
+
+    aDesiredSize.ISize(wm) = std::max(aDesiredSize.ISize(wm),
+                                      childDesiredSize.ISize(childWm));
+    aDesiredSize.BSize(wm) += childDesiredSize.BSize(childWm);
+    aStatus.MergeCompletionStatusFrom(childStatus);
+
+    childOrigin.B(childWm) += childDesiredSize.BSize(childWm);
+  }
+
+//  if (aDesiredSize.ISize(wm) == 0) {
+//    aDesiredSize.ISize(wm) = availableISize;
+//  }
+
+  aDesiredSize.ISize(wm) += borderPadding.IStartEnd(wm);
+  aDesiredSize.BSize(wm) += borderPadding.BStartEnd(wm);
+
+//  if (aReflowInput.ComputedBSize() != NS_AUTOHEIGHT) {
+//    aDesiredSize.BSize(wm) = aReflowInput.ComputedBSize() +
+//                               borderPadding.BStartEnd(wm);
+//  }
+
+  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize)
+}
+
+void
+nsColumnSetWrapperFrame::AppendFrames(ChildListID     aListID,
+                                      nsFrameList&    aFrameList)
+{
+  MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetWrapperFrame::InsertFrames(ChildListID     aListID,
+                                      nsIFrame*       aPrevFrame,
+                                      nsFrameList&    aFrameList)
+{
+  MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetWrapperFrame::RemoveFrame(ChildListID     aListID,
+                                     nsIFrame*       aOldFrame)
+{
+  MOZ_CRASH("unsupported operation");
+}
new file mode 100644
--- /dev/null
+++ b/layout/generic/nsColumnSetWrapperFrame.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* rendering wrapper object for css3 multi-column layout */
+
+
+#ifndef nsColumnSetWrapperFrame_h___
+#define nsColumnSetWrapperFrame_h___
+
+#include "nsContainerFrame.h"
+
+/* This class is a wrapper for nsColumnSetFrames and column-span elements i.e.
+ * spanners. Essentially, we divide the *original* nsColumnSetFrame
+ * into multiple nsColumnSetFrames on the basis of the number and position of
+ * spanning elements.
+ * This wrapper is necessary for implementing column-span as it allows us to
+ * maintain each nsColumnSetFrame as an independent set of columns and each
+ * spanning element then becomes just a block level element.
+ */
+class nsColumnSetWrapperFrame final : public nsContainerFrame
+{
+public:
+  NS_DECL_FRAMEARENA_HELPERS(nsColumnSetWrapperFrame)
+
+  friend nsContainerFrame* NS_NewColumnSetWrapperFrame(nsIPresShell* aPresShell,
+                                                       nsStyleContext* aContext,
+                                                       nsFrameState aStateFlags);
+  void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                        const nsDisplayListSet& aLists) override;
+
+  void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
+
+#ifdef DEBUG_FRAME_DUMP
+  nsresult GetFrameName(nsAString& aResult) const override {
+    return MakeFrameName(NS_LITERAL_STRING("ColumnSetWrapper"), aResult);
+  }
+#endif
+
+  nscoord GetMinISize(gfxContext* aRenderingContext) override;
+  nscoord GetPrefISize(gfxContext* aRenderingContext) override;
+
+  void Reflow(nsPresContext*     aPresContext,
+              ReflowOutput&      aDesiredSize,
+              const ReflowInput& aReflowInput,
+              nsReflowStatus&    aStatus) override;
+
+  void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
+
+  void InsertFrames(ChildListID  aListID,
+                    nsIFrame*    aPrevFrame,
+                    nsFrameList& aFrameList) override;
+
+  void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
+
+private:
+  explicit nsColumnSetWrapperFrame(nsStyleContext* aContext);
+  ~nsColumnSetWrapperFrame() override
+  {
+  }
+
+};
+
+#endif /* nsColumnSetWrapperFrame_h___ */
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10637,17 +10637,24 @@ nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(
   }
 
   AutoTArray<OwnedAnonBox,4> frames;
   AppendDirectlyOwnedAnonBoxes(frames);
   for (OwnedAnonBox& box : frames) {
     if (box.mUpdateStyleFn) {
       box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
     } else {
-      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
+      if (box.mAnonBoxFrame->StyleContext()->StyleColumn()->mColumnSpan ==
+            NS_STYLE_COLUMN_SPAN_ALL) {
+        nsBlockFrame* blockFrame = static_cast<nsBlockFrame*>(box.mAnonBoxFrame);
+        MOZ_ASSERT(blockFrame, "This must be a valid block frame!");
+        blockFrame->UpdateStyleOfOwnedAnonBoxesForColumnSpanSplit(aRestyleState);
+      } else {
+        UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
+      }
     }
   }
 }
 
 /* virtual */ void
 nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
 {
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES));
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -11,16 +11,17 @@ FRAME_ID(nsBlockFrame, Block, NotLeaf)
 FRAME_ID(nsBox, None, NotLeaf)
 FRAME_ID(nsBoxFrame, Box, NotLeaf)
 FRAME_ID(nsBulletFrame, Bullet, Leaf)
 FRAME_ID(nsButtonBoxFrame, Box, NotLeaf)
 FRAME_ID(nsCanvasFrame, Canvas, NotLeaf)
 FRAME_ID(nsCheckboxRadioFrame, CheckboxRadio, Leaf)
 FRAME_ID(nsColorControlFrame, ColorControl, Leaf)
 FRAME_ID(nsColumnSetFrame, ColumnSet, NotLeaf)
+FRAME_ID(nsColumnSetWrapperFrame, ColumnSetWrapper, NotLeaf)
 FRAME_ID(nsComboboxControlFrame, ComboboxControl, NotLeaf)
 FRAME_ID(nsComboboxDisplayFrame, ComboboxDisplay, NotLeaf)
 FRAME_ID(nsContinuingTextFrame, Text, Leaf)
 FRAME_ID(nsDateTimeControlFrame, DateTimeControl, NotLeaf)
 FRAME_ID(nsDeckFrame, Deck, NotLeaf)
 FRAME_ID(nsDocElementBoxFrame, Box, NotLeaf)
 FRAME_ID(nsFieldSetFrame, FieldSet, NotLeaf)
 FRAME_ID(nsFileControlFrame, Block, Leaf)
--- a/layout/generic/nsHTMLParts.h
+++ b/layout/generic/nsHTMLParts.h
@@ -104,16 +104,18 @@ NS_NewContinuingTextFrame(nsIPresShell* 
 nsIFrame*
 NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 inline nsIFrame*
 NS_NewWBRFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) {
   return NS_NewEmptyFrame(aPresShell, aContext);
 }
 
 nsContainerFrame*
+NS_NewColumnSetWrapperFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags);
+nsContainerFrame*
 NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags);
 
 class nsSimplePageSequenceFrame;
 nsSimplePageSequenceFrame*
 NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 class nsPageFrame;
 nsPageFrame*
 NS_NewPageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -619,16 +619,17 @@ public:
     , mClass(aID)
     , mMayHaveRoundedCorners(false)
     , mHasImageRequest(false)
     , mHasFirstLetterChild(false)
     , mParentIsWrapperAnonBox(false)
     , mIsWrapperBoxNeedingRestyle(false)
     , mReflowRequestedForCharDataChange(false)
     , mIsPrimaryFrame(false)
+    , mHasMulticolAncestor(false)
   {
     mozilla::PodZero(&mOverflow);
   }
 
   nsPresContext* PresContext() const {
     return StyleContext()->PresContext();
   }
 
@@ -2024,16 +2025,34 @@ public:
   /**
    * Return true if this frame is the primary frame for mContent.
    */
   bool IsPrimaryFrame() const { return mIsPrimaryFrame; }
 
   void SetIsPrimaryFrame(bool aIsPrimary) { mIsPrimaryFrame = aIsPrimary; }
 
   /**
+   * Returns if the current frame has an ancestor with column-count or
+   * column-width set.
+   */
+   bool HasMulticolAncestor() const
+   {
+     return mHasMulticolAncestor;
+   }
+
+   /**
+    * Set if the current frame has an ancestor with column-count or
+    * column-width set.
+    */
+    void SetHasMulticolAncestor(bool aHasMulticolAncestor)
+    {
+      mHasMulticolAncestor = aHasMulticolAncestor;
+    }
+
+  /**
    * This call is invoked on the primary frame for a character data content
    * node, when it is changed in the content tree.
    */
   virtual nsresult  CharacterDataChanged(CharacterDataChangeInfo* aInfo) = 0;
 
   /**
    * This call is invoked when the value of a content objects's attribute
    * is changed.
@@ -4238,18 +4257,23 @@ protected:
 
 private:
   /**
    * True if this is the primary frame for mContent.
    */
   bool mIsPrimaryFrame : 1;
 
 protected:
-
-  // There is a 9-bit gap left here.
+  /**
+   * True if the frame has an ancestor that has either column-count or
+   * column-width defined.
+   */
+  bool mHasMulticolAncestor : 1;
+
+  // There is an 8-bit gap left here.
 
   // Helpers
   /**
    * Can we stop inside this frame when we're skipping non-rendered whitespace?
    * @param  aForward [in] Are we moving forward (or backward) in content order.
    * @param  aOffset [in/out] At what offset into the frame to start looking.
    *         on output - what offset was reached (whether or not we found a place to stop).
    * @return STOP: An appropriate offset was found within this frame,
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -997,17 +997,19 @@ nsInlineFrame::UpdateStyleOfOwnedAnonBox
   // changehint being in aChangeList is good enough.  So we don't need to touch
   // aChangeList at all here.
 
   while (blockFrame) {
     MOZ_ASSERT(!blockFrame->GetPrevContinuation(),
                "Must be first continuation");
 
     MOZ_ASSERT(blockFrame->StyleContext()->GetPseudo() ==
-               nsCSSAnonBoxes::mozBlockInsideInlineWrapper,
+               nsCSSAnonBoxes::mozBlockInsideInlineWrapper ||
+               blockFrame->StyleContext()->GetPseudo() ==
+               nsCSSAnonBoxes::mozColumnSpanWrapper,
                "Unexpected kind of style context");
 
     // We don't want to just walk through using GetNextContinuationWithSameStyle
     // here, because we want to set updated style contexts on both our
     // ib-sibling blocks and inlines.
     for (nsIFrame* cont = blockFrame; cont; cont = cont->GetNextContinuation()) {
       cont->SetStyleContext(newContext);
     }
--- a/layout/generic/nsSplittableFrame.cpp
+++ b/layout/generic/nsSplittableFrame.cpp
@@ -276,16 +276,36 @@ nsSplittableFrame::GetLogicalSkipSides(c
     }
   } else {
     nsIFrame* nif = GetNextInFlow();
     if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
       skip |= eLogicalSideBitsBEnd;
     }
   }
 
+  if (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
+    // All but the last part of an column-span split should skip the "bottom"
+    // side (as determined by this frame's direction) and all but the first
+    // part of such a split should skip the "top" side.
+    // But figuring out which part of the split we are involves getting
+    // our first continuation, which might be expensive. So don't bother if
+    // we already have the relevant bits set.
+    if (skip != LogicalSides(eLogicalSideBitsBBoth)) {
+      // We're missing one of the skip bits, so check whether we need to set it.
+      // Only get the first continuation once, as an optimization.
+      nsIFrame* firstContinuation = FirstContinuation();
+      if (firstContinuation->FrameIsNonLastInIBSplit()) {
+        skip |= eLogicalSideBitsBEnd;
+      }
+      if (firstContinuation->FrameIsNonFirstInIBSplit()) {
+        skip |= eLogicalSideBitsBStart;
+      }
+    }
+  }
+
  return skip;
 }
 
 LogicalSides
 nsSplittableFrame::PreReflowBlockLevelLogicalSkipSides() const
 {
   if (MOZ_UNLIKELY(IS_TRUE_OVERFLOW_CONTAINER(this))) {
     return LogicalSides(mozilla::eLogicalSideBitsBBoth);
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -522,26 +522,26 @@ JoinBoxesForBlockAxisSlice(nsIFrame* aFr
 {
   // Inflate the block-axis size as if our continuations were laid out
   // adjacent in that axis.  Note that we don't touch the inline size.
   nsRect borderArea = aBorderArea;
   nscoord bSize = 0;
   auto wm = aFrame->GetWritingMode();
   nsIFrame* f = aFrame->GetNextContinuation();
   for (; f; f = f->GetNextContinuation()) {
-    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
-               "anonymous ib-split block shouldn't have border/background");
+//    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
+//               "anonymous ib-split block shouldn't have border/background");
     bSize += f->BSize(wm);
   }
   (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
   bSize = 0;
   f = aFrame->GetPrevContinuation();
   for (; f; f = f->GetPrevContinuation()) {
-    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
-               "anonymous ib-split block shouldn't have border/background");
+//    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
+//               "anonymous ib-split block shouldn't have border/background");
     bSize += f->BSize(wm);
   }
   (wm.IsVertical() ? borderArea.x : borderArea.y) -= bSize;
   (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
   return borderArea;
 }
 
 /**
--- a/layout/style/nsCSSAnonBoxList.h
+++ b/layout/style/nsCSSAnonBoxList.h
@@ -56,16 +56,18 @@ CSS_ANON_BOX(mozText, ":-moz-text")
 // placeholder frames for out of flows.  Note that :-moz-placeholder is used for
 // the pseudo-element that represents the placeholder text in <input
 // placeholder="foo">, so we need a different string here.
 CSS_NON_INHERITING_ANON_BOX(oofPlaceholder, ":-moz-oof-placeholder")
 // nsFirstLetterFrames for content outside the ::first-letter.
 CSS_ANON_BOX(firstLetterContinuation, ":-moz-first-letter-continuation")
 
 CSS_ANON_BOX(mozBlockInsideInlineWrapper, ":-moz-block-inside-inline-wrapper")
+CSS_ANON_BOX(mozColumnSpanWrapper, ":-moz-column-span-wrapper")
+CSS_ANON_BOX(mozColumnSet, ":-moz-column-set")
 CSS_WRAPPER_ANON_BOX(mozMathMLAnonymousBlock, ":-moz-mathml-anonymous-block")
 CSS_ANON_BOX(mozXULAnonymousBlock, ":-moz-xul-anonymous-block")
 
 // Framesets
 CSS_NON_INHERITING_ANON_BOX(horizontalFramesetBorder, ":-moz-hframeset-border")
 CSS_NON_INHERITING_ANON_BOX(verticalFramesetBorder, ":-moz-vframeset-border")
 
 CSS_ANON_BOX(mozLineFrame, ":-moz-line-frame")
--- a/layout/style/res/ua.css
+++ b/layout/style/res/ua.css
@@ -153,17 +153,17 @@
 *|*::-moz-cell-content {
   display: block !important;
   position: static !important;
   unicode-bidi: inherit;
   text-overflow: inherit;
   overflow-clip-box: inherit;
 }
 
-*|*::-moz-block-inside-inline-wrapper {
+*|*::-moz-block-inside-inline-wrapper, *|*::-moz-column-span-wrapper {
   display: block !important;
   /* we currently inherit from the inline that is split */
   position: inherit; /* static or relative or sticky */
   outline: inherit;
   outline-offset: inherit;
   clip-path: inherit;
   filter: inherit;
   mask: inherit;
@@ -178,16 +178,32 @@
      to always inherit them. */
   top: inherit;
   left: inherit;
   bottom: inherit;
   right: inherit;
   z-index: inherit;
 }
 
+*|*::-moz-column-span-wrapper {
+  opacity: unset;
+  column-span: all;
+  margin-top: inherit;
+  margin-bottom: inherit;
+}
+
+*|*::-moz-column-set {
+  /* Prevent inheriting of borders on ColumnSetFrames since they are now
+     inherited by ColumnSetWrapper frame instead*/
+  display: inherit !important;
+  column-count: inherit;
+  column-width: inherit;
+  column-rule: inherit;
+}
+
 *|*::-moz-xul-anonymous-block {
   display: block ! important;
   position: static ! important;
   float: none ! important;
   -moz-box-ordinal-group: inherit !important;
   text-overflow: inherit;
   overflow-clip-box: inherit;
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3089,17 +3089,17 @@ pref("layout.css.font-loading-api.enable
 // Should stray control characters be rendered visibly?
 #ifdef RELEASE_OR_BETA
 pref("layout.css.control-characters.visible", false);
 #else
 pref("layout.css.control-characters.visible", true);
 #endif
 
 // Is support for column-span enabled?
-pref("layout.css.column-span.enabled", false);
+pref("layout.css.column-span.enabled", true);
 
 // Are inter-character ruby annotations enabled?
 pref("layout.css.ruby.intercharacter.enabled", false);
 
 // pref for which side vertical scrollbars should be on
 // 0 = end-side in UI direction
 // 1 = end-side in document/content direction
 // 2 = right