Bug 1547759 - Add a flag to allow FinishReflowChild to handle relative positioning...
[gecko.git] / layout / generic / RubyUtils.cpp
blob4cdd4b65b3677d1edb18b0c376763e22e8f4ea7f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "RubyUtils.h"
8 #include "nsRubyFrame.h"
9 #include "nsRubyBaseFrame.h"
10 #include "nsRubyTextFrame.h"
11 #include "nsRubyBaseContainerFrame.h"
12 #include "nsRubyTextContainerFrame.h"
14 using namespace mozilla;
16 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord)
18 /* static */
19 void RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize) {
20 MOZ_ASSERT(IsExpandableRubyBox(aFrame));
21 aFrame->SetProperty(ReservedISize(), aISize);
24 /* static */
25 void RubyUtils::ClearReservedISize(nsIFrame* aFrame) {
26 MOZ_ASSERT(IsExpandableRubyBox(aFrame));
27 aFrame->RemoveProperty(ReservedISize());
30 /* static */
31 nscoord RubyUtils::GetReservedISize(nsIFrame* aFrame) {
32 MOZ_ASSERT(IsExpandableRubyBox(aFrame));
33 return aFrame->GetProperty(ReservedISize());
36 AutoRubyTextContainerArray::AutoRubyTextContainerArray(
37 nsRubyBaseContainerFrame* aBaseContainer) {
38 for (nsIFrame* frame = aBaseContainer->GetNextSibling();
39 frame && frame->IsRubyTextContainerFrame();
40 frame = frame->GetNextSibling()) {
41 AppendElement(static_cast<nsRubyTextContainerFrame*>(frame));
45 nsIFrame* RubyColumn::Iterator::operator*() const {
46 nsIFrame* frame;
47 if (mIndex == -1) {
48 frame = mColumn.mBaseFrame;
49 } else {
50 frame = mColumn.mTextFrames[mIndex];
52 MOZ_ASSERT(frame, "Frame here cannot be null");
53 return frame;
56 void RubyColumn::Iterator::SkipUntilExistingFrame() {
57 if (mIndex == -1) {
58 if (mColumn.mBaseFrame) {
59 return;
61 ++mIndex;
63 int32_t numTextFrames = mColumn.mTextFrames.Length();
64 for (; mIndex < numTextFrames; ++mIndex) {
65 if (mColumn.mTextFrames[mIndex]) {
66 break;
71 RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame) {
72 nsIFrame* frame = aRubyFrame->PrincipalChildList().FirstChild();
73 MOZ_ASSERT(!frame || frame->IsRubyBaseContainerFrame());
74 mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
77 void RubySegmentEnumerator::Next() {
78 MOZ_ASSERT(mBaseContainer);
79 nsIFrame* frame = mBaseContainer->GetNextSibling();
80 while (frame && !frame->IsRubyBaseContainerFrame()) {
81 frame = frame->GetNextSibling();
83 mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
86 RubyColumnEnumerator::RubyColumnEnumerator(
87 nsRubyBaseContainerFrame* aBaseContainer,
88 const AutoRubyTextContainerArray& aTextContainers)
89 : mAtIntraLevelWhitespace(false) {
90 const uint32_t rtcCount = aTextContainers.Length();
91 mFrames.SetCapacity(rtcCount + 1);
93 nsIFrame* rbFrame = aBaseContainer->PrincipalChildList().FirstChild();
94 MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame());
95 mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rbFrame));
96 for (uint32_t i = 0; i < rtcCount; i++) {
97 nsRubyTextContainerFrame* container = aTextContainers[i];
98 // If the container is for span, leave a nullptr here.
99 // Spans do not take part in pairing.
100 nsIFrame* rtFrame = !container->IsSpanContainer()
101 ? container->PrincipalChildList().FirstChild()
102 : nullptr;
103 MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame());
104 mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rtFrame));
107 // We have to init mAtIntraLevelWhitespace to be correct for the
108 // first column. There are two ways we could end up with intra-level
109 // whitespace in our first colum:
110 // 1. The current segment itself is an inter-segment whitespace;
111 // 2. If our ruby segment is split across multiple lines, and some
112 // intra-level whitespace happens to fall right after a line-break.
113 // Each line will get its own nsRubyBaseContainerFrame, and the
114 // container right after the line-break will end up with its first
115 // column containing that intra-level whitespace.
116 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
117 nsRubyContentFrame* frame = mFrames[i];
118 if (frame && frame->IsIntraLevelWhitespace()) {
119 mAtIntraLevelWhitespace = true;
120 break;
125 void RubyColumnEnumerator::Next() {
126 bool advancingToIntraLevelWhitespace = false;
127 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
128 nsRubyContentFrame* frame = mFrames[i];
129 // If we've got intra-level whitespace frames at some levels in the
130 // current ruby column, we "faked" an anonymous box for all other
131 // levels for this column. So when we advance off this column, we
132 // don't advance any of the frames in those levels, because we're
133 // just advancing across the "fake" frames.
134 if (frame &&
135 (!mAtIntraLevelWhitespace || frame->IsIntraLevelWhitespace())) {
136 nsIFrame* nextSibling = frame->GetNextSibling();
137 MOZ_ASSERT(!nextSibling || nextSibling->Type() == frame->Type(),
138 "Frame type should be identical among a level");
139 mFrames[i] = frame = static_cast<nsRubyContentFrame*>(nextSibling);
140 if (!advancingToIntraLevelWhitespace && frame &&
141 frame->IsIntraLevelWhitespace()) {
142 advancingToIntraLevelWhitespace = true;
146 MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace,
147 "Should never have adjacent intra-level whitespace columns");
148 mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace;
151 bool RubyColumnEnumerator::AtEnd() const {
152 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
153 if (mFrames[i]) {
154 return false;
157 return true;
160 nsRubyContentFrame* RubyColumnEnumerator::GetFrameAtLevel(
161 uint32_t aIndex) const {
162 // If the current ruby column is for intra-level whitespaces, we
163 // return nullptr for any levels that do not have an actual intra-
164 // level whitespace frame in this column. This nullptr represents
165 // an anonymous empty intra-level whitespace box. (In this case,
166 // it's important that we NOT return mFrames[aIndex], because it's
167 // really part of the next column, not the current one.)
168 nsRubyContentFrame* frame = mFrames[aIndex];
169 return !mAtIntraLevelWhitespace || (frame && frame->IsIntraLevelWhitespace())
170 ? frame
171 : nullptr;
174 void RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const {
175 nsRubyContentFrame* rbFrame = GetFrameAtLevel(0);
176 MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame());
177 aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(rbFrame);
178 aColumn.mTextFrames.ClearAndRetainStorage();
179 for (uint32_t i = 1, iend = mFrames.Length(); i < iend; i++) {
180 nsRubyContentFrame* rtFrame = GetFrameAtLevel(i);
181 MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame());
182 aColumn.mTextFrames.AppendElement(static_cast<nsRubyTextFrame*>(rtFrame));
184 aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace;