Bug 1088489 - Implement ruby pseudo box generation. r=bz

--HG--
extra : rebase_source : 3d722db5a9f896a7083d385e65f0d1d7b694d9e2
extra : source : c7f276c6f764aa28e8b178230f0a9f12372f1db0
This commit is contained in:
Xidorn Quan 2014-12-08 21:45:56 +11:00
Родитель de448565ef
Коммит 56d367a884
2 изменённых файлов: 350 добавлений и 116 удалений

Просмотреть файл

@ -9611,6 +9611,248 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
} while (!iter.IsDone()); } while (!iter.IsDone());
} }
/* static */ nsCSSFrameConstructor::RubyWhitespaceType
nsCSSFrameConstructor::ComputeRubyWhitespaceType(uint_fast8_t aPrevDisplay,
uint_fast8_t aNextDisplay)
{
MOZ_ASSERT(nsStyleDisplay::IsRubyDisplayType(aPrevDisplay) &&
nsStyleDisplay::IsRubyDisplayType(aNextDisplay));
if (aPrevDisplay == aNextDisplay &&
(aPrevDisplay == NS_STYLE_DISPLAY_RUBY_BASE ||
aPrevDisplay == NS_STYLE_DISPLAY_RUBY_TEXT)) {
return eRubyInterLeafWhitespace;
}
if (aNextDisplay == NS_STYLE_DISPLAY_RUBY_TEXT ||
aNextDisplay == NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER) {
return eRubyInterLevelWhitespace;
}
return eRubyInterSegmentWhitespace;
}
/**
* This function checks the content from |aStartIter| to |aEndIter|,
* determines whether it contains only whitespace, and if yes,
* interprets the type of whitespace. This method does not change
* any of the iters.
*/
/* static */ nsCSSFrameConstructor::RubyWhitespaceType
nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
const FCItemIterator& aStartIter,
const FCItemIterator& aEndIter)
{
if (!aStartIter.item().IsWhitespace(aState)) {
return eRubyNotWhitespace;
}
FCItemIterator spaceEndIter(aStartIter);
spaceEndIter.SkipWhitespace(aState);
if (spaceEndIter != aEndIter) {
return eRubyNotWhitespace;
}
// Any leading or trailing whitespace in non-pseudo ruby box
// should have been trimmed, hence there should not be any
// whitespace at the start or the end.
MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
FCItemIterator prevIter(aStartIter);
prevIter.Prev();
return ComputeRubyWhitespaceType(
prevIter.item().mStyleContext->StyleDisplay()->mDisplay,
aEndIter.item().mStyleContext->StyleDisplay()->mDisplay);
}
/**
* This function eats up consecutive items which do not want the current
* parent into either a ruby base box or a ruby text box. When it
* returns, |aIter| points to the first item it doesn't wrap.
*/
void
nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
FCItemIterator& aIter,
nsStyleContext* aParentStyle, nsIContent* aParentContent)
{
uint_fast8_t parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
ParentType parentType, wrapperType;
if (parentDisplay == NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER) {
parentType = eTypeRubyTextContainer;
wrapperType = eTypeRubyText;
} else {
MOZ_ASSERT(parentDisplay == NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER);
parentType = eTypeRubyBaseContainer;
wrapperType = eTypeRubyBase;
}
MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
"Should point to something needs to be wrapped.");
FCItemIterator endIter(aIter);
endIter.SkipItemsNotWantingParentType(parentType);
WrapItemsInPseudoParent(aParentContent, aParentStyle,
wrapperType, aIter, endIter);
}
/**
* This function eats up consecutive items into a ruby level container.
* It may create zero or one level container. When it returns, |aIter|
* points to the first item it doesn't wrap.
*/
void
nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
nsFrameConstructorState& aState, FCItemIterator& aIter,
nsStyleContext* aParentStyle, nsIContent* aParentContent)
{
MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
"Pointing to a level container?");
FrameConstructionItem& firstItem = aIter.item();
ParentType wrapperType = firstItem.DesiredParentType();
if (wrapperType != eTypeRubyTextContainer) {
// If the first item is not ruby text,
// it should be in a base container.
wrapperType = eTypeRubyBaseContainer;
}
FCItemIterator endIter(aIter);
do {
if (endIter.SkipItemsWantingParentType(wrapperType) ||
// If the skipping above stops at some item which wants a
// different ruby parent, then we have finished.
IsRubyParentType(endIter.item().DesiredParentType())) {
// No more items need to be wrapped in this level container.
break;
}
FCItemIterator contentEndIter(endIter);
contentEndIter.SkipItemsNotWantingRubyParent();
// endIter must be on something doesn't want a ruby parent.
MOZ_ASSERT(contentEndIter != endIter);
// InterpretRubyWhitespace depends on the fact that any leading or
// trailing whitespace described in the spec have been trimmed at
// this point. With this precondition, it is safe not to check
// whether contentEndIter has been done.
RubyWhitespaceType whitespaceType =
InterpretRubyWhitespace(aState, endIter, contentEndIter);
if (whitespaceType == eRubyInterLevelWhitespace) {
// Remove inter-level whitespace.
bool atStart = (aIter == endIter);
endIter.DeleteItemsTo(contentEndIter);
if (atStart) {
aIter = endIter;
}
} else if (whitespaceType == eRubyInterSegmentWhitespace) {
// If this level container starts with inter-segment whitespaces,
// wrap them. Break at contentEndIter. Otherwise, leave it here.
// Break at endIter. They will be wrapped when we are here again.
if (aIter == endIter) {
MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
"Inter-segment whitespace should be wrapped in rbc");
endIter = contentEndIter;
}
break;
} else if (wrapperType == eTypeRubyTextContainer &&
whitespaceType != eRubyInterLeafWhitespace) {
// Misparented inline content that's not inter-annotation
// whitespace doesn't belong in a pseudo ruby text container.
// Break at endIter.
break;
} else {
endIter = contentEndIter;
}
} while (!endIter.IsDone());
// It is possible that everything our parent wants us to wrap is
// simply an inter-level whitespace, which has been trimmed, or
// an inter-segment whitespace, which will be wrapped later.
// In those cases, don't create anything.
if (aIter != endIter) {
WrapItemsInPseudoParent(aParentContent, aParentStyle,
wrapperType, aIter, endIter);
}
}
/**
* This function trims leading and trailing whitespaces
* in the given item list.
*/
void
nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
nsFrameConstructorState& aState,
FrameConstructionItemList& aItems)
{
FCItemIterator iter(aItems);
if (!iter.IsDone() &&
iter.item().IsWhitespace(aState)) {
FCItemIterator spaceEndIter(iter);
spaceEndIter.SkipWhitespace(aState);
iter.DeleteItemsTo(spaceEndIter);
}
iter.SetToEnd();
if (!iter.AtStart()) {
FCItemIterator spaceEndIter(iter);
do {
iter.Prev();
if (iter.AtStart()) {
// It's fine to not check the first item, because we
// should have trimmed leading whitespaces above.
break;
}
} while (iter.item().IsWhitespace(aState));
iter.Next();
if (iter != spaceEndIter) {
iter.DeleteItemsTo(spaceEndIter);
}
}
}
/**
* This function walks through the child list (aItems) and creates
* needed pseudo ruby boxes to wrap misparented children.
*/
void
nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
nsFrameConstructorState& aState,
FrameConstructionItemList& aItems,
nsIFrame* aParentFrame)
{
const ParentType ourParentType = GetParentType(aParentFrame);
if (!IsRubyParentType(ourParentType) ||
aItems.AllWantParentType(ourParentType)) {
return;
}
nsStyleContext* parentStyle = aParentFrame->StyleContext();
if (!parentStyle->GetPseudo()) {
// Normally, pseudo frames start from and end at some elements,
// which means they don't have leading and trailing whitespaces at
// all. But there are two cases where they do actually have leading
// or trailing whitespaces:
// 1. It is an inter-segment whitespace which in an individual ruby
// base container.
// 2. The pseudo frame starts from or ends at consecutive inline
// content, which is not pure whitespace, but includes some.
// In either case, the whitespaces are not the leading or trailing
// whitespaces defined in the spec, and thus should not be trimmed.
TrimLeadingAndTrailingWhitespaces(aState, aItems);
}
FCItemIterator iter(aItems);
nsIContent* parentContent = aParentFrame->GetContent();
while (!iter.IsDone()) {
if (!iter.SkipItemsWantingParentType(ourParentType)) {
if (ourParentType == eTypeRuby) {
WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
parentContent);
} else {
WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
}
}
}
}
/* /*
* This function works as follows: we walk through the child list (aItems) and * This function works as follows: we walk through the child list (aItems) and
* find items that cannot have aParentFrame as their parent. We wrap * find items that cannot have aParentFrame as their parent. We wrap
@ -9629,7 +9871,8 @@ nsCSSFrameConstructor::CreateNeededPseudoContainers(
nsIFrame* aParentFrame) nsIFrame* aParentFrame)
{ {
ParentType ourParentType = GetParentType(aParentFrame); ParentType ourParentType = GetParentType(aParentFrame);
if (aItems.AllWantParentType(ourParentType)) { if (IsRubyParentType(ourParentType) ||
aItems.AllWantParentType(ourParentType)) {
// Nothing to do here // Nothing to do here
return; return;
} }
@ -9678,70 +9921,18 @@ nsCSSFrameConstructor::CreateNeededPseudoContainers(
!aParentFrame->IsGeneratedContentFrame() && !aParentFrame->IsGeneratedContentFrame() &&
spaceEndIter.item().IsWhitespace(aState)) { spaceEndIter.item().IsWhitespace(aState)) {
bool trailingSpaces = spaceEndIter.SkipWhitespace(aState); bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
int nextDisplay = -1;
int prevDisplay = -1;
if (!endIter.AtStart() &&
(IsRubyParentType(ourParentType) ||
IsRubyParentType(groupingParentType))) {
FCItemIterator prevItemIter(endIter);
prevItemIter.Prev();
prevDisplay =
prevItemIter.item().mStyleContext->StyleDisplay()->mDisplay;
}
// We only need to compute nextDisplay for testing for ruby white
// space.
if (!spaceEndIter.IsDone() &&
(IsRubyParentType(ourParentType) ||
IsRubyParentType(groupingParentType))) {
nextDisplay =
spaceEndIter.item().mStyleContext->StyleDisplay()->mDisplay;
}
if (ourParentType == eTypeRubyBaseContainer &&
prevDisplay == -1 && nextDisplay == -1) {
if (aParentFrame->StyleContext()->GetPseudo()) {
// We are in a pseudo ruby base container, which has
// whitespaces only. This is a special case to handle
// inter-segment spaces.
endIter = spaceEndIter;
break;
}
}
// We drop the whitespace in the following cases: // We drop the whitespace in the following cases:
// 1) If these are not trailing spaces and the next item wants a table // 1) If these are not trailing spaces and the next item wants a table
// or table-part parent // or table-part parent
// 2) If these are trailing spaces and aParentFrame is a // 2) If these are trailing spaces and aParentFrame is a
// tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1. // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
// (Being a tabular container pretty much means // (Being a tabular container pretty much means ourParentType is
// IsTableParentType(ourParentType) besides the eTypeColGroup case, // not eTypeBlock besides the eTypeColGroup case, which won't
// which won't reach here.) // reach here.)
// 3) The whitespace is leading or trailing inside a ruby box, ruby
// base container box, or ruby text container box.
// 4) The whitespace is classified as inter-level intra-ruby
// whitespace according to the spec for CSS ruby.
bool isRubyLeadingTrailingParentType =
ourParentType == eTypeRuby ||
ourParentType == eTypeRubyBaseContainer ||
ourParentType == eTypeRubyTextContainer;
bool isRubyLeading =
prevDisplay == -1 && isRubyLeadingTrailingParentType;
bool isRubyTrailing =
nextDisplay == -1 && isRubyLeadingTrailingParentType;
// There's an implicit condition that we are in a ruby parent or
// we are grouping a ruby parent here, because nextDisplay and
// prevDisplay are only set if that is true.
bool isRubyInterLevel =
(nextDisplay == NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER) ||
(nextDisplay == NS_STYLE_DISPLAY_RUBY_TEXT &&
prevDisplay != NS_STYLE_DISPLAY_RUBY_TEXT);
if ((!trailingSpaces && if ((!trailingSpaces &&
IsTableParentType(spaceEndIter.item().DesiredParentType())) || IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
(trailingSpaces && IsTableParentType(ourParentType)) || (trailingSpaces && ourParentType != eTypeBlock)) {
isRubyLeading || isRubyTrailing || isRubyInterLevel) {
bool updateStart = (iter == endIter); bool updateStart = (iter == endIter);
endIter.DeleteItemsTo(spaceEndIter); endIter.DeleteItemsTo(spaceEndIter);
NS_ASSERTION(trailingSpaces == endIter.IsDone(), NS_ASSERTION(trailingSpaces == endIter.IsDone(),
@ -9770,23 +9961,16 @@ nsCSSFrameConstructor::CreateNeededPseudoContainers(
// what it means that this is the group end), so it's OK. // what it means that this is the group end), so it's OK.
// However, when we are grouping a ruby parent, and endIter points to // However, when we are grouping a ruby parent, and endIter points to
// a non-droppable whitespace, if the next non-whitespace item also // a non-droppable whitespace, if the next non-whitespace item also
// wants a ruby parent which is not our parent, the whitespace should // wants a ruby parent, the whitespace should also be included into
// also be included into the current ruby parent. // the current ruby container.
prevParentType = endIter.item().DesiredParentType(); prevParentType = endIter.item().DesiredParentType();
if (prevParentType == ourParentType) { if (prevParentType == ourParentType &&
if (endIter == spaceEndIter || (endIter == spaceEndIter ||
// not grouping a ruby parent spaceEndIter.IsDone() ||
!IsRubyParentType(groupingParentType) || !IsRubyParentType(groupingParentType) ||
spaceEndIter.IsDone()) { !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
// End the group at endIter. // End the group at endIter.
break; break;
}
ParentType nextParentType = spaceEndIter.item().DesiredParentType();
if (nextParentType == ourParentType ||
!IsRubyParentType(nextParentType)) {
// End the group at endIter.
break;
}
} }
if (ourParentType == eTypeTable && if (ourParentType == eTypeTable &&
@ -9797,31 +9981,6 @@ nsCSSFrameConstructor::CreateNeededPseudoContainers(
break; break;
} }
// Break from the boundary between a ruby base container and a
// ruby text container, or the boundary between an inter-segment
// whitespace and the next ruby segment.
if (ourParentType == eTypeRuby) {
if ((prevParentType == eTypeRubyBaseContainer &&
groupingParentType == eTypeRubyTextContainer) ||
(prevParentType == eTypeRubyTextContainer &&
groupingParentType == eTypeRubyBaseContainer)) {
// Don't group ruby base boxes and
// ruby annotation boxes together.
break;
} else if (groupingParentType == eTypeBlock &&
endIter != spaceEndIter) {
// We are on inter-segment whitespaces, which we want to
// create an independent ruby base container for.
endIter = spaceEndIter;
break;
}
// The only case where prevParentType is different from
// groupingParentType but we still want to continue, is that
// we are on an inter-base or inter-annotation whitespace.
MOZ_ASSERT(groupingParentType == prevParentType ||
prevParentType == eTypeBlock);
}
// If we have some whitespace that we were not able to drop and there is // If we have some whitespace that we were not able to drop and there is
// an item after the whitespace that is already properly parented, then // an item after the whitespace that is already properly parented, then
// make sure to include the spaces in our group but stop the group after // make sure to include the spaces in our group but stop the group after
@ -9865,23 +10024,6 @@ nsCSSFrameConstructor::CreateNeededPseudoContainers(
break; break;
case eTypeColGroup: case eTypeColGroup:
MOZ_CRASH("Colgroups should be suppresing non-col child items"); MOZ_CRASH("Colgroups should be suppresing non-col child items");
case eTypeRuby:
if (groupingParentType == eTypeRubyTextContainer) {
wrapperType = eTypeRubyTextContainer;
} else {
NS_ASSERTION(groupingParentType == eTypeRubyBaseContainer ||
groupingParentType == eTypeBlock,
"It should be either a ruby base container, "
"or an inter-segment whitespace");
wrapperType = eTypeRubyBaseContainer;
}
break;
case eTypeRubyBaseContainer:
wrapperType = eTypeRubyBase;
break;
case eTypeRubyTextContainer:
wrapperType = eTypeRubyText;
break;
default: default:
NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type"); NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
if (IsRubyParentType(groupingParentType)) { if (IsRubyParentType(groupingParentType)) {
@ -10015,6 +10157,7 @@ nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aSta
{ {
CreateNeededPseudoContainers(aState, aItems, aParentFrame); CreateNeededPseudoContainers(aState, aItems, aParentFrame);
CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame); CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
CreateNeededPseudoSiblings(aState, aItems, aParentFrame); CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
aItems.SetTriedConstructingFrames(); aItems.SetTriedConstructingFrames();
@ -12058,6 +12201,20 @@ Iterator::SkipItemsWantingParentType(ParentType aParentType)
return false; return false;
} }
inline bool
nsCSSFrameConstructor::FrameConstructionItemList::
Iterator::SkipItemsNotWantingParentType(ParentType aParentType)
{
NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
while (item().DesiredParentType() != aParentType) {
Next();
if (IsDone()) {
return true;
}
}
return false;
}
bool bool
nsCSSFrameConstructor::FrameConstructionItem:: nsCSSFrameConstructor::FrameConstructionItem::
NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState) NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState)
@ -12109,6 +12266,20 @@ Iterator::SkipItemsThatDontNeedAnonFlexOrGridItem(
return false; return false;
} }
inline bool
nsCSSFrameConstructor::FrameConstructionItemList::
Iterator::SkipItemsNotWantingRubyParent()
{
NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
while (!IsRubyParentType(item().DesiredParentType())) {
Next();
if (IsDone()) {
return true;
}
}
return false;
}
inline bool inline bool
nsCSSFrameConstructor::FrameConstructionItemList:: nsCSSFrameConstructor::FrameConstructionItemList::
Iterator::SkipWhitespace(nsFrameConstructorState& aState) Iterator::SkipWhitespace(nsFrameConstructorState& aState)

Просмотреть файл

@ -919,10 +919,15 @@ private:
} }
void SetToEnd() { mCurrent = mEnd; } void SetToEnd() { mCurrent = mEnd; }
// Skip over all items that want the given parent type. Return whether
// the iterator is done after doing that. The iterator must not be done
// when this is called.
inline bool SkipItemsWantingParentType(ParentType aParentType);
// Skip over all items that want a parent type different from the given // Skip over all items that want a parent type different from the given
// one. Return whether the iterator is done after doing that. The // one. Return whether the iterator is done after doing that. The
// iterator must not be done when this is called. // iterator must not be done when this is called.
inline bool SkipItemsWantingParentType(ParentType aParentType); inline bool SkipItemsNotWantingParentType(ParentType aParentType);
// Skip over non-replaced inline frames and positioned frames. // Skip over non-replaced inline frames and positioned frames.
// Return whether the iterator is done after doing that. // Return whether the iterator is done after doing that.
@ -936,6 +941,11 @@ private:
inline bool SkipItemsThatDontNeedAnonFlexOrGridItem( inline bool SkipItemsThatDontNeedAnonFlexOrGridItem(
const nsFrameConstructorState& aState); const nsFrameConstructorState& aState);
// Skip over all items that do not want a ruby parent. Return whether
// the iterator is done after doing that. The iterator must not be done
// when this is called.
inline bool SkipItemsNotWantingRubyParent();
// Skip over whitespace. Return whether the iterator is done after doing // Skip over whitespace. Return whether the iterator is done after doing
// that. The iterator must not be done, and must be pointing to a // that. The iterator must not be done, and must be pointing to a
// whitespace item when this is called. // whitespace item when this is called.
@ -1166,6 +1176,59 @@ private:
FrameConstructionItemList& aItems, FrameConstructionItemList& aItems,
nsIFrame* aParentFrame); nsIFrame* aParentFrame);
enum RubyWhitespaceType
{
eRubyNotWhitespace,
eRubyInterLevelWhitespace,
// Includes inter-base and inter-annotation whitespace
eRubyInterLeafWhitespace,
eRubyInterSegmentWhitespace
};
/**
* Function to compute the whitespace type according to the display
* values of the previous and the next elements.
*/
static inline RubyWhitespaceType ComputeRubyWhitespaceType(
uint_fast8_t aPrevDisplay, uint_fast8_t aNextDisplay);
/**
* Function to interpret the type of whitespace between
* |aStartIter| and |aEndIter|.
*/
static inline RubyWhitespaceType InterpretRubyWhitespace(
nsFrameConstructorState& aState,
const FCItemIterator& aStartIter, const FCItemIterator& aEndIter);
/**
* Function to wrap consecutive misparented inline content into
* a ruby base box or a ruby text box.
*/
void WrapItemsInPseudoRubyLeafBox(FCItemIterator& aIter,
nsStyleContext* aParentStyle,
nsIContent* aParentContent);
/**
* Function to wrap consecutive misparented items
* into a ruby level container.
*/
inline void WrapItemsInPseudoRubyLevelContainer(
nsFrameConstructorState& aState, FCItemIterator& aIter,
nsStyleContext* aParentStyle, nsIContent* aParentContent);
/**
* Function to trim leading and trailing whitespaces.
*/
inline void TrimLeadingAndTrailingWhitespaces(
nsFrameConstructorState& aState, FrameConstructionItemList& aItems);
/**
* Function to create internal ruby boxes.
*/
inline void CreateNeededPseudoInternalRubyBoxes(
nsFrameConstructorState& aState,
FrameConstructionItemList& aItems, nsIFrame* aParentFrame);
/** /**
* Function to create the pseudo intermediate containers we need. * Function to create the pseudo intermediate containers we need.
* @param aItems the child frame construction items before pseudo creation * @param aItems the child frame construction items before pseudo creation