diff options
Diffstat (limited to 'layout/generic/RubyUtils.cpp')
-rw-r--r-- | layout/generic/RubyUtils.cpp | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/layout/generic/RubyUtils.cpp b/layout/generic/RubyUtils.cpp new file mode 100644 index 000000000..f340663bc --- /dev/null +++ b/layout/generic/RubyUtils.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RubyUtils.h" +#include "nsRubyFrame.h" +#include "nsRubyBaseFrame.h" +#include "nsRubyTextFrame.h" +#include "nsRubyBaseContainerFrame.h" +#include "nsRubyTextContainerFrame.h" + +using namespace mozilla; + +NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord) + +/* static */ void +RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + aFrame->Properties().Set(ReservedISize(), aISize); +} + +/* static */ void +RubyUtils::ClearReservedISize(nsIFrame* aFrame) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + aFrame->Properties().Remove(ReservedISize()); +} + +/* static */ nscoord +RubyUtils::GetReservedISize(nsIFrame* aFrame) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + return aFrame->Properties().Get(ReservedISize()); +} + +AutoRubyTextContainerArray::AutoRubyTextContainerArray( + nsRubyBaseContainerFrame* aBaseContainer) +{ + for (nsIFrame* frame = aBaseContainer->GetNextSibling(); + frame && frame->GetType() == nsGkAtoms::rubyTextContainerFrame; + frame = frame->GetNextSibling()) { + AppendElement(static_cast<nsRubyTextContainerFrame*>(frame)); + } +} + +nsIFrame* +RubyColumn::Iterator::operator*() const +{ + nsIFrame* frame; + if (mIndex == -1) { + frame = mColumn.mBaseFrame; + } else { + frame = mColumn.mTextFrames[mIndex]; + } + MOZ_ASSERT(frame, "Frame here cannot be null"); + return frame; +} + +void +RubyColumn::Iterator::SkipUntilExistingFrame() +{ + if (mIndex == -1) { + if (mColumn.mBaseFrame) { + return; + } + ++mIndex; + } + int32_t numTextFrames = mColumn.mTextFrames.Length(); + for (; mIndex < numTextFrames; ++mIndex) { + if (mColumn.mTextFrames[mIndex]) { + break; + } + } +} + +RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame) +{ + nsIFrame* frame = aRubyFrame->PrincipalChildList().FirstChild(); + MOZ_ASSERT(!frame || + frame->GetType() == nsGkAtoms::rubyBaseContainerFrame); + mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame); +} + +void +RubySegmentEnumerator::Next() +{ + MOZ_ASSERT(mBaseContainer); + nsIFrame* frame = mBaseContainer->GetNextSibling(); + while (frame && frame->GetType() != nsGkAtoms::rubyBaseContainerFrame) { + frame = frame->GetNextSibling(); + } + mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame); +} + +RubyColumnEnumerator::RubyColumnEnumerator( + nsRubyBaseContainerFrame* aBaseContainer, + const AutoRubyTextContainerArray& aTextContainers) + : mAtIntraLevelWhitespace(false) +{ + const uint32_t rtcCount = aTextContainers.Length(); + mFrames.SetCapacity(rtcCount + 1); + + nsIFrame* rbFrame = aBaseContainer->PrincipalChildList().FirstChild(); + MOZ_ASSERT(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame); + mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rbFrame)); + for (uint32_t i = 0; i < rtcCount; i++) { + nsRubyTextContainerFrame* container = aTextContainers[i]; + // If the container is for span, leave a nullptr here. + // Spans do not take part in pairing. + nsIFrame* rtFrame = !container->IsSpanContainer() ? + container->PrincipalChildList().FirstChild() : nullptr; + MOZ_ASSERT(!rtFrame || rtFrame->GetType() == nsGkAtoms::rubyTextFrame); + mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rtFrame)); + } + + // We have to init mAtIntraLevelWhitespace to be correct for the + // first column. There are two ways we could end up with intra-level + // whitespace in our first colum: + // 1. The current segment itself is an inter-segment whitespace; + // 2. If our ruby segment is split across multiple lines, and some + // intra-level whitespace happens to fall right after a line-break. + // Each line will get its own nsRubyBaseContainerFrame, and the + // container right after the line-break will end up with its first + // column containing that intra-level whitespace. + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* frame = mFrames[i]; + if (frame && frame->IsIntraLevelWhitespace()) { + mAtIntraLevelWhitespace = true; + break; + } + } +} + +void +RubyColumnEnumerator::Next() +{ + bool advancingToIntraLevelWhitespace = false; + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* frame = mFrames[i]; + // If we've got intra-level whitespace frames at some levels in the + // current ruby column, we "faked" an anonymous box for all other + // levels for this column. So when we advance off this column, we + // don't advance any of the frames in those levels, because we're + // just advancing across the "fake" frames. + if (frame && (!mAtIntraLevelWhitespace || + frame->IsIntraLevelWhitespace())) { + nsIFrame* nextSibling = frame->GetNextSibling(); + MOZ_ASSERT(!nextSibling || nextSibling->GetType() == frame->GetType(), + "Frame type should be identical among a level"); + mFrames[i] = frame = static_cast<nsRubyContentFrame*>(nextSibling); + if (!advancingToIntraLevelWhitespace && + frame && frame->IsIntraLevelWhitespace()) { + advancingToIntraLevelWhitespace = true; + } + } + } + MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace, + "Should never have adjacent intra-level whitespace columns"); + mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace; +} + +bool +RubyColumnEnumerator::AtEnd() const +{ + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + if (mFrames[i]) { + return false; + } + } + return true; +} + +nsRubyContentFrame* +RubyColumnEnumerator::GetFrameAtLevel(uint32_t aIndex) const +{ + // If the current ruby column is for intra-level whitespaces, we + // return nullptr for any levels that do not have an actual intra- + // level whitespace frame in this column. This nullptr represents + // an anonymous empty intra-level whitespace box. (In this case, + // it's important that we NOT return mFrames[aIndex], because it's + // really part of the next column, not the current one.) + nsRubyContentFrame* frame = mFrames[aIndex]; + return !mAtIntraLevelWhitespace || + (frame && frame->IsIntraLevelWhitespace()) ? frame : nullptr; +} + +void +RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const +{ + nsRubyContentFrame* rbFrame = GetFrameAtLevel(0); + MOZ_ASSERT(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame); + aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(rbFrame); + aColumn.mTextFrames.ClearAndRetainStorage(); + for (uint32_t i = 1, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* rtFrame = GetFrameAtLevel(i); + MOZ_ASSERT(!rtFrame || rtFrame->GetType() == nsGkAtoms::rubyTextFrame); + aColumn.mTextFrames.AppendElement(static_cast<nsRubyTextFrame*>(rtFrame)); + } + aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace; +} |