author | Gurzau Raul <rgurzau@mozilla.com> |
Mon, 16 Jul 2018 23:56:23 +0300 | |
changeset 426851 | 6339739cba9a0099d6bbefa294ce22989935ab69 |
parent 426850 | d55a38a6be534e3f7c62da7ae36a65d8526ea0a4 |
child 426852 | 2b4612def0fe62def3b633c91c942c354d183c42 |
push id | 34287 |
push user | ccoroiu@mozilla.com |
push date | Tue, 17 Jul 2018 09:42:00 +0000 |
treeherder | mozilla-central@547144f5596c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1476054 |
milestone | 63.0a1 |
backs out | f4941fe345ade851ebd49fecea9f89aa3bfbc79f cc571c618e4c256cedbdd8859ef0d9f7e97d0afd 0a712d7bcb66d19a94ba05d020c28b845071aee6 62293a989ed23d16194315a37bed2b19dd6aec46 ef2a43e3fc6c6acd0f7dea8424837b18edd15a95 a2bb0089cf1d69cf713913250b89c1b828ab1e77 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/layout/generic/BRFrame.cpp +++ b/layout/generic/BRFrame.cpp @@ -153,17 +153,17 @@ BRFrame::Reflow(nsPresContext* aPresCont // XXX This also fixes bug 10036! // Warning: nsTextControlFrame::CalculateSizeStandard depends on // the following line, see bug 228752. // The code below in AddInlinePrefISize also adds 1 appunit to width finalSize.ISize(wm) = 1; } // Return our reflow status - StyleClear breakType = aReflowInput.mStyleDisplay->mBreakType; + StyleClear breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm); if (StyleClear::None == breakType) { breakType = StyleClear::Line; } aStatus.SetInlineLineBreakAfter(breakType); ll->SetLineEndsInBR(true); }
--- a/layout/generic/BlockReflowInput.cpp +++ b/layout/generic/BlockReflowInput.cpp @@ -737,17 +737,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF // ``above'' another float that preceded it in the flow. mBCoord = std::max(FloatManager()->GetLowestFloatTop(), mBCoord); // See if the float should clear any preceding floats... // XXX We need to mark this float somehow so that it gets reflowed // when floats are inserted before it. if (StyleClear::None != floatDisplay->mBreakType) { // XXXldb Does this handle vertical margins correctly? - mBCoord = ClearFloats(mBCoord, floatDisplay->mBreakType); + mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm)); } // Get the band of available space with respect to margin box. nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(mBCoord); LogicalRect adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat); NS_ASSERTION(aFloat->GetParent() == mBlock, @@ -780,17 +780,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF "letter frames and orthogonal floats with auto block-size " "shouldn't break, and if they do now, then they're breaking " "at the wrong point"); } // Find a place to place the float. The CSS2 spec doesn't want // floats overlapping each other or sticking out of the containing // block if possible (CSS2 spec section 9.5.1, see the rule list). - StyleFloat floatStyle = floatDisplay->mFloat; + StyleFloat floatStyle = floatDisplay->PhysicalFloats(wm); MOZ_ASSERT(StyleFloat::Left == floatStyle || StyleFloat::Right == floatStyle, "Invalid float type!"); // Can the float fit here? bool keepFloatOnSameLine = false; // Are we required to place at least part of the float because we're // at the top of the page (to avoid an infinite loop of pushing and @@ -1040,17 +1040,18 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF void BlockReflowInput::PushFloatPastBreak(nsIFrame *aFloat) { // This ensures that we: // * don't try to place later but smaller floats (which CSS says // must have their tops below the top of this float) // * don't waste much time trying to reflow this float again until // after the break - StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat; + StyleFloat floatStyle = + aFloat->StyleDisplay()->PhysicalFloats(mReflowInput.GetWritingMode()); if (floatStyle == StyleFloat::Left) { FloatManager()->SetPushedLeftFloatPastBreak(); } else { MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float value!"); FloatManager()->SetPushedRightFloatPastBreak(); } // Put the float on the pushed floats list, even though it
--- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -2182,16 +2182,42 @@ nsStylePosition::MinBSizeDependsOnContai } inline bool nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const { return aWM.IsVertical() ? MaxWidthDependsOnContainer() : MaxHeightDependsOnContainer(); } +inline mozilla::StyleFloat +nsStyleDisplay::PhysicalFloats(mozilla::WritingMode aWM) const +{ + using StyleFloat = mozilla::StyleFloat; + if (mFloat == StyleFloat::InlineStart) { + return aWM.IsBidiLTR() ? StyleFloat::Left : StyleFloat::Right; + } + if (mFloat == StyleFloat::InlineEnd) { + return aWM.IsBidiLTR() ? StyleFloat::Right : StyleFloat::Left; + } + return mFloat; +} + +inline mozilla::StyleClear +nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const +{ + using StyleClear = mozilla::StyleClear; + if (mBreakType == StyleClear::InlineStart) { + return aWM.IsBidiLTR() ? StyleClear::Left : StyleClear::Right; + } + if (mBreakType == StyleClear::InlineEnd) { + return aWM.IsBidiLTR() ? StyleClear::Right : StyleClear::Left; + } + return mBreakType; +} + inline bool nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const { return mMargin.HasBlockAxisAuto(aWM); } inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
--- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -837,17 +837,18 @@ nsBlockFrame::GetPrefISize(gfxContext *a } AutoNoisyIndenter lineindent(gNoisyIntrinsic); #endif if (line->IsBlock()) { StyleClear breakType; if (!data.mLineIsEmpty || BlockCanIntersectFloats(line->mFirstChild)) { breakType = StyleClear::Both; } else { - breakType = line->mFirstChild->StyleDisplay()->mBreakType; + breakType = line->mFirstChild-> + StyleDisplay()->PhysicalBreakType(data.mLineContainerWM); } data.ForceBreak(breakType); data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::PREF_ISIZE); data.ForceBreak(); } else { if (!curFrame->GetPrevContinuation() && line == curFrame->LinesBegin()) { @@ -3220,17 +3221,18 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl if (!frame) { NS_ASSERTION(false, "program error - unexpected empty line"); return; } // Prepare the block reflow engine nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput); - StyleClear breakType = frame->StyleDisplay()->mBreakType; + StyleClear breakType = frame->StyleDisplay()-> + PhysicalBreakType(aState.mReflowInput.GetWritingMode()); if (StyleClear::None != aState.mFloatBreakType) { breakType = nsLayoutUtils::CombineBreakType(breakType, aState.mFloatBreakType); aState.mFloatBreakType = StyleClear::None; } // Clear past floats before the block if the clear style is not none aLine->SetBreakTypeBefore(breakType); @@ -4339,17 +4341,18 @@ nsBlockFrame::SplitFloat(BlockReflowInpu } else { nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aState.mPresContext, aFloat, this); } if (aFloatStatus.IsOverflowIncomplete()) { nextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } - StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat; + StyleFloat floatStyle = + aFloat->StyleDisplay()->PhysicalFloats(aState.mReflowInput.GetWritingMode()); if (floatStyle == StyleFloat::Left) { aState.FloatManager()->SetSplitLeftFloatAcrossBreak(); } else { MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float side!"); aState.FloatManager()->SetSplitRightFloatAcrossBreak(); } aState.AppendPushedFloatChain(nextInFlow);
--- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -201,17 +201,17 @@ nsFloatManager::GetFlowArea(WritingMode // WidthWithinHeight call is at least as narrow on both sides as a // BandFromPoint call beginning at its blockStart. else if (blockStart < floatBEnd && (floatBStart < blockEnd || (floatBStart == blockEnd && blockStart == blockEnd))) { // This float is in our band. // Shrink our band's width if needed. - StyleFloat floatStyle = fi.mFrame->StyleDisplay()->mFloat; + StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM); // When aBandInfoType is BandFromPoint, we're only intended to // consider a point along the y axis rather than a band. const nscoord bandBlockEnd = aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd; if (floatStyle == StyleFloat::Left) { // A left float nscoord lineRightEdge = @@ -278,17 +278,17 @@ nsFloatManager::AddFloat(nsIFrame* aFloa if (HasAnyFloats()) { FloatInfo &tail = mFloats[mFloats.Length() - 1]; info.mLeftBEnd = tail.mLeftBEnd; info.mRightBEnd = tail.mRightBEnd; } else { info.mLeftBEnd = nscoord_MIN; info.mRightBEnd = nscoord_MIN; } - StyleFloat floatStyle = aFloatFrame->StyleDisplay()->mFloat; + StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM); MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right, "Unexpected float style!"); nscoord& sideBEnd = floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd; nscoord thisBEnd = info.BEnd(); if (thisBEnd > sideBEnd) sideBEnd = thisBEnd; @@ -311,17 +311,17 @@ nsFloatManager::CalculateRegionFor(Writi region.Inflate(aWM, aMargin); // Don't store rectangles with negative margin-box width or height in // the float manager; it can't deal with them. if (region.ISize(aWM) < 0) { // Preserve the right margin-edge for left floats and the left // margin-edge for right floats const nsStyleDisplay* display = aFloat->StyleDisplay(); - StyleFloat floatStyle = display->mFloat; + StyleFloat floatStyle = display->PhysicalFloats(aWM); if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) { region.IStart(aWM) = region.IEnd(aWM); } region.ISize(aWM) = 0; } if (region.BSize(aWM) < 0) { region.BSize(aWM) = 0; }
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5352,38 +5352,39 @@ nsIFrame::InlinePrefISizeData::ForceBrea if (mFloats.Length() != 0 && aBreakType != StyleClear::None) { // preferred widths accumulated for floats that have already // been cleared past nscoord floats_done = 0, // preferred widths accumulated for floats that have not yet // been cleared past floats_cur_left = 0, floats_cur_right = 0; + const WritingMode wm = mLineContainerWM; for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) { const FloatInfo& floatInfo = mFloats[i]; const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay(); - StyleClear breakType = floatDisp->mBreakType; + StyleClear breakType = floatDisp->PhysicalBreakType(wm); if (breakType == StyleClear::Left || breakType == StyleClear::Right || breakType == StyleClear::Both) { nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left, floats_cur_right); if (floats_cur > floats_done) { floats_done = floats_cur; } if (breakType != StyleClear::Right) { floats_cur_left = 0; } if (breakType != StyleClear::Left) { floats_cur_right = 0; } } - StyleFloat floatStyle = floatDisp->mFloat; + StyleFloat floatStyle = floatDisp->PhysicalFloats(wm); nscoord& floats_cur = floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right; nscoord floatWidth = floatInfo.Width(); // Negative-width floats don't change the available space so they // shouldn't change our intrinsic line width either. floats_cur = NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth)); } @@ -5408,27 +5409,27 @@ nsIFrame::InlinePrefISizeData::ForceBrea aBreakType == StyleClear::Right, "Other values should have been handled in other branches"); StyleFloat clearFloatType = aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right; // Iterate the array in reverse so that we can stop when there are // no longer any floats we need to keep. See below. for (FloatInfo& floatInfo : Reversed(mFloats)) { const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay(); - if (floatDisp->mFloat != clearFloatType) { + if (floatDisp->PhysicalFloats(wm) != clearFloatType) { newFloats.AppendElement(floatInfo); } else { // This is a float on the side that this break directly clears // which means we're not keeping it in mFloats. However, if // this float clears floats on the opposite side (via a value // of either 'both' or one of 'left'/'right'), any remaining // (earlier) floats on that side would be indirectly cleared // as well. Thus, we should break out of this loop and stop // considering earlier floats to be kept in mFloats. - StyleClear floatBreakType = floatDisp->mBreakType; + StyleClear floatBreakType = floatDisp->PhysicalBreakType(wm); if (floatBreakType != aBreakType && floatBreakType != StyleClear::None) { break; } } } newFloats.Reverse(); mFloats = std::move(newFloats);
--- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2185,29 +2185,32 @@ public: , mSkipWhitespace(true) {} // The line. This may be null if the inlines are not associated with // a block or if we just don't know the line. const nsLineList_iterator* mLine; // The line container. Private, to ensure we always use SetLineContainer - // to update it. + // to update it (so that we have a chance to store the mLineContainerWM). // // Note that nsContainerFrame::DoInlineIntrinsicISize will clear the // |mLine| and |mLineContainer| fields when following a next-in-flow link, // so we must not assume these can always be dereferenced. private: nsIFrame* mLineContainer; // Setter and getter for the lineContainer field: public: void SetLineContainer(nsIFrame* aLineContainer) { mLineContainer = aLineContainer; + if (mLineContainer) { + mLineContainerWM = mLineContainer->GetWritingMode(); + } } nsIFrame* LineContainer() const { return mLineContainer; } // The maximum intrinsic width for all previous lines. nscoord mPrevLines; // The maximum intrinsic width for the current line. At a line // break (mandatory for preferred width; allowed for minimum width), @@ -2218,16 +2221,20 @@ public: // |mCurrentLine|; it is zero if there is no such whitespace. nscoord mTrailingWhitespace; // True if initial collapsable whitespace should be skipped. This // should be true at the beginning of a block, after hard breaks // and when the last text ended with whitespace. bool mSkipWhitespace; + // Writing mode of the line container (stored here so that we don't + // lose track of it if the mLineContainer field is reset). + mozilla::WritingMode mLineContainerWM; + // Floats encountered in the lines. class FloatInfo { public: FloatInfo(const nsIFrame* aFrame, nscoord aWidth) : mFrame(aFrame), mWidth(aWidth) { } const nsIFrame* Frame() const { return mFrame; } nscoord Width() const { return mWidth; }
--- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -205,16 +205,18 @@ ListFloats(FILE* out, const char* aPrefi /* static */ const char* nsLineBox::BreakTypeToString(StyleClear aBreakType) { switch (aBreakType) { case StyleClear::None: return "nobr"; case StyleClear::Left: return "leftbr"; case StyleClear::Right: return "rightbr"; + case StyleClear::InlineStart: return "inlinestartbr"; + case StyleClear::InlineEnd: return "inlineendbr"; case StyleClear::Both: return "leftbr+rightbr"; case StyleClear::Line: return "linebr"; case StyleClear::Max: return "leftbr+rightbr+linebr"; } return "unknown"; } char*
--- a/layout/style/ServoCSSPropList.mako.py +++ b/layout/style/ServoCSSPropList.mako.py @@ -69,22 +69,20 @@ def method(prop): return prop.camel_case # Colors, integers and lengths are easy as well. # # TODO(emilio): This will go away once the rest of the longhands have been # moved or perhaps using a blacklist for the ones with non-layout-dependence # but other non-trivial dependence like scrollbar colors. SERIALIZED_PREDEFINED_TYPES = [ - "Clear", "Color", "Content", "CounterIncrement", "CounterReset", - "Float", "FontFamily", "FontFeatureSettings", "FontLanguageOverride", "FontSize", "FontSizeAdjust", "FontStretch", "FontStyle", "FontSynthesis",
--- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -72,22 +72,21 @@ enum class StyleBoxShadowType : uint8_t Inset, }; // clear enum class StyleClear : uint8_t { None = 0, Left, Right, + InlineStart, + InlineEnd, Both, // StyleClear::Line can be added to one of the other values in layout // so it needs to use a bit value that none of the other values can have. - // - // FIXME(emilio): Doesn't look like we do that anymore, so probably can be - // made a single value instead, and Max removed. Line = 8, Max = 13 // Max = (Both | Line) }; // Counters and generated content. enum class StyleContentType : uint8_t { String = 1, Image = 10, @@ -140,16 +139,18 @@ enum class StyleFillRule : uint8_t { }; // float // https://developer.mozilla.org/en-US/docs/Web/CSS/float enum class StyleFloat : uint8_t { None, Left, Right, + InlineStart, + InlineEnd }; // float-edge enum class StyleFloatEdge : uint8_t { ContentBox, MarginBox, };
--- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2450,16 +2450,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt private: // Helpers for above functions, which do some but not all of the tests // for them (since transform must be tested separately for each). inline bool HasAbsPosContainingBlockStyleInternal() const; inline bool HasFixedPosContainingBlockStyleInternal( mozilla::ComputedStyle&) const; void GenerateCombinedTransform(); +public: + // Return the 'float' and 'clear' properties, with inline-{start,end} values + // resolved to {left,right} according to the given writing mode. These are + // defined in WritingModes.h. + inline mozilla::StyleFloat PhysicalFloats(mozilla::WritingMode aWM) const; + inline mozilla::StyleClear PhysicalBreakType(mozilla::WritingMode aWM) const; }; struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable { explicit nsStyleTable(const nsPresContext* aContext); nsStyleTable(const nsStyleTable& aOther); ~nsStyleTable(); void FinishStyle(nsPresContext*, const nsStyleTable*) {}
--- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -3078,27 +3078,16 @@ fn static_assert() { v: longhands::display::computed_value::T, _is_item_or_root: bool ) { self.gecko.mDisplay = Self::match_display_keyword(v); } <%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"></%call> - <% float_keyword = Keyword("float", "Left Right None", gecko_enum_prefix="StyleFloat") %> - ${impl_keyword('float', 'mFloat', float_keyword)} - - <% clear_keyword = Keyword( - "clear", - "Left Right None Both", - gecko_enum_prefix="StyleClear", - gecko_inexhaustive=True, - ) %> - ${impl_keyword('clear', 'mBreakType', clear_keyword)} - <% overflow_x = data.longhands_by_name["overflow-x"] %> pub fn set_overflow_y(&mut self, v: longhands::overflow_y::computed_value::T) { use properties::longhands::overflow_x::computed_value::T as BaseType; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts self.gecko.mOverflowY = match v { % for value in overflow_x.keyword.values_for('gecko'): BaseType::${to_camel_case(value)} => structs::${overflow_x.keyword.gecko_constant(value)} as u8, % endfor
--- a/servo/components/style/properties/longhands/box.mako.rs +++ b/servo/components/style/properties/longhands/box.mako.rs @@ -49,39 +49,126 @@ spec="Internal (not web-exposed)")} ${helpers.single_keyword("position", "static absolute relative fixed sticky", animation_value_type="discrete", flags="CREATES_STACKING_CONTEXT ABSPOS_CB", spec="https://drafts.csswg.org/css-position/#position-property", servo_restyle_damage="rebuild_and_reflow")} -${helpers.predefined_type( - "float", - "Float", - "computed::Float::None", - initial_specified_value="specified::Float::None", - spec="https://drafts.csswg.org/css-box/#propdef-float", - animation_value_type="discrete", - needs_context=False, - flags="APPLIES_TO_FIRST_LETTER", - servo_restyle_damage="rebuild_and_reflow", +<%helpers:single_keyword + name="float" + values="none left right" + // https://drafts.csswg.org/css-logical-props/#float-clear + extra_specified="inline-start inline-end" + needs_conversion="True" + animation_value_type="discrete" + gecko_enum_prefix="StyleFloat" + gecko_inexhaustive="True" gecko_ffi_name="mFloat" -)} + flags="APPLIES_TO_FIRST_LETTER" + spec="https://drafts.csswg.org/css-box/#propdef-float" + servo_restyle_damage="rebuild_and_reflow" +> + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + let ltr = context.style().writing_mode.is_bidi_ltr(); + // https://drafts.csswg.org/css-logical-props/#float-clear + match *self { + SpecifiedValue::InlineStart => { + context.rule_cache_conditions.borrow_mut() + .set_writing_mode_dependency(context.builder.writing_mode); + if ltr { + computed_value::T::Left + } else { + computed_value::T::Right + } + } + SpecifiedValue::InlineEnd => { + context.rule_cache_conditions.borrow_mut() + .set_writing_mode_dependency(context.builder.writing_mode); + if ltr { + computed_value::T::Right + } else { + computed_value::T::Left + } + } + % for value in "None Left Right".split(): + SpecifiedValue::${value} => computed_value::T::${value}, + % endfor + } + } + #[inline] + fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue { + match *computed { + % for value in "None Left Right".split(): + computed_value::T::${value} => SpecifiedValue::${value}, + % endfor + } + } + } +</%helpers:single_keyword> -${helpers.predefined_type( - "clear", - "Clear", - "computed::Clear::None", - animation_value_type="discrete", - needs_context=False, - gecko_ffi_name="mBreakType", - spec="https://drafts.csswg.org/css-box/#propdef-clear", +<%helpers:single_keyword + name="clear" + values="none left right both" + // https://drafts.csswg.org/css-logical-props/#float-clear + extra_specified="inline-start inline-end" + needs_conversion="True" + gecko_inexhaustive="True" + animation_value_type="discrete" + gecko_enum_prefix="StyleClear" + gecko_ffi_name="mBreakType" + spec="https://drafts.csswg.org/css-box/#propdef-clear" servo_restyle_damage="rebuild_and_reflow" -)} +> + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + let ltr = context.style().writing_mode.is_bidi_ltr(); + // https://drafts.csswg.org/css-logical-props/#float-clear + match *self { + SpecifiedValue::InlineStart => { + context.rule_cache_conditions.borrow_mut() + .set_writing_mode_dependency(context.builder.writing_mode); + if ltr { + computed_value::T::Left + } else { + computed_value::T::Right + } + } + SpecifiedValue::InlineEnd => { + context.rule_cache_conditions.borrow_mut() + .set_writing_mode_dependency(context.builder.writing_mode); + if ltr { + computed_value::T::Right + } else { + computed_value::T::Left + } + } + % for value in "None Left Right Both".split(): + SpecifiedValue::${value} => computed_value::T::${value}, + % endfor + } + } + #[inline] + fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue { + match *computed { + % for value in "None Left Right Both".split(): + computed_value::T::${value} => SpecifiedValue::${value}, + % endfor + } + } + } +</%helpers:single_keyword> ${helpers.predefined_type( "vertical-align", "VerticalAlign", "computed::VerticalAlign::baseline()", animation_value_type="ComputedValue", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align",
--- a/servo/components/style/properties/properties.mako.rs +++ b/servo/components/style/properties/properties.mako.rs @@ -30,17 +30,17 @@ use font_metrics::FontMetricsProvider; #[cfg(feature = "gecko")] use gecko_bindings::bindings; #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID}; #[cfg(feature = "servo")] use logical_geometry::LogicalMargin; #[cfg(feature = "servo")] use computed_values; use logical_geometry::WritingMode; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use media_queries::Device; use parser::ParserContext; -use properties::longhands::system_font::SystemFont; +#[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont; use rule_cache::{RuleCache, RuleCacheConditions}; use selector_parser::PseudoElement; use selectors::parser::SelectorParseErrorKind; #[cfg(feature = "servo")] use servo_config::prefs::PREFS; use shared_lock::StylesheetGuards; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use stylesheets::{CssRuleType, Origin, UrlExtraData};
--- a/servo/components/style/values/computed/box.rs +++ b/servo/components/style/values/computed/box.rs @@ -1,22 +1,21 @@ /* 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/. */ //! Computed types for box properties. -use values::computed::{Context, Number, ToComputedValue}; +use values::computed::Number; use values::computed::length::{LengthOrPercentage, NonNegativeLength}; use values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use values::generics::box_::Perspective as GenericPerspective; use values::generics::box_::VerticalAlign as GenericVerticalAlign; pub use values::specified::box_::{AnimationName, Contain, Display, OverflowClipBox}; -pub use values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat}; pub use values::specified::box_::{OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange}; /// A computed value for the `vertical-align` property. pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>; /// A computed value for the `animation-iteration-count` property. pub type AnimationIterationCount = GenericAnimationIterationCount<Number>; @@ -25,117 +24,8 @@ impl AnimationIterationCount { #[inline] pub fn one() -> Self { GenericAnimationIterationCount::Number(1.0) } } /// A computed value for the `perspective` property. pub type Perspective = GenericPerspective<NonNegativeLength>; - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, - SpecifiedValueInfo, ToCss)] -/// A computed value for the `float` property. -pub enum Float { - Left, - Right, - None -} - -impl ToComputedValue for SpecifiedFloat { - type ComputedValue = Float; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - let ltr = context.style().writing_mode.is_bidi_ltr(); - // https://drafts.csswg.org/css-logical-props/#float-clear - match *self { - SpecifiedFloat::InlineStart => { - context.rule_cache_conditions.borrow_mut() - .set_writing_mode_dependency(context.builder.writing_mode); - if ltr { - Float::Left - } else { - Float::Right - } - }, - SpecifiedFloat::InlineEnd => { - context.rule_cache_conditions.borrow_mut() - .set_writing_mode_dependency(context.builder.writing_mode); - if ltr { - Float::Right - } else { - Float::Left - } - }, - SpecifiedFloat::Left => Float::Left, - SpecifiedFloat::Right => Float::Right, - SpecifiedFloat::None => Float::None - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedFloat { - match *computed { - Float::Left => SpecifiedFloat::Left, - Float::Right => SpecifiedFloat::Right, - Float::None => SpecifiedFloat::None - } - } -} - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, -SpecifiedValueInfo, ToCss)] -/// A computed value for the `clear` property. -pub enum Clear { - None, - Left, - Right, - Both -} - -impl ToComputedValue for SpecifiedClear { - type ComputedValue = Clear; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - let ltr = context.style().writing_mode.is_bidi_ltr(); - // https://drafts.csswg.org/css-logical-props/#float-clear - match *self { - SpecifiedClear::InlineStart => { - context.rule_cache_conditions.borrow_mut() - .set_writing_mode_dependency(context.builder.writing_mode); - if ltr { - Clear::Left - } else { - Clear::Right - } - }, - SpecifiedClear::InlineEnd => { - context.rule_cache_conditions.borrow_mut() - .set_writing_mode_dependency(context.builder.writing_mode); - if ltr { - Clear::Right - } else { - Clear::Left - } - }, - SpecifiedClear::None => Clear::None, - SpecifiedClear::Left => Clear::Left, - SpecifiedClear::Right => Clear::Right, - SpecifiedClear::Both => Clear::Both - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedClear { - match *computed { - Clear::None => SpecifiedClear::None, - Clear::Left => SpecifiedClear::Left, - Clear::Right => SpecifiedClear::Right, - Clear::Both => SpecifiedClear::Both, - } - } -}
--- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -38,17 +38,16 @@ pub use self::angle::Angle; pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::border::{BorderImageRepeat, BorderImageSideWidth, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight}; pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty}; -pub use self::box_::{Clear, Float}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::column::ColumnCount; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
--- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -823,35 +823,8 @@ impl TransitionProperty { } TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(), TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(), TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => return Err(()), }) } } - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, - SpecifiedValueInfo, ToCss)] -pub enum Float { - Left, - Right, - None, - // https://drafts.csswg.org/css-logical-props/#float-clear - InlineStart, - InlineEnd -} - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, - SpecifiedValueInfo, ToCss)] -pub enum Clear { - None, - Left, - Right, - Both, - // https://drafts.csswg.org/css-logical-props/#float-clear - InlineStart, - InlineEnd -}
--- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -33,17 +33,16 @@ pub use self::border::{BorderCornerRadiu pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing}; pub use self::column::ColumnCount; pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight}; pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display}; -pub use self::box_::{Clear, Float}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; #[cfg(feature = "gecko")] pub use self::gecko::ScrollSnapPoint;
--- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -3946,40 +3946,31 @@ pub unsafe extern "C" fn Servo_Declarati pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue( declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID, value: i32 ) { use style::properties::{PropertyDeclaration, LonghandId}; use style::properties::longhands; use style::values::specified::BorderStyle; - use style::values::specified::Float; use style::values::generics::font::FontStyle; let long = get_longhand_from_id!(property); let value = value as u32; let prop = match_wrap_declared! { long, MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value), + // TextEmphasisPosition => FIXME implement text-emphasis-position Direction => longhands::direction::SpecifiedValue::from_gecko_keyword(value), Display => longhands::display::SpecifiedValue::from_gecko_keyword(value), - Float => { - const LEFT: u32 = structs::StyleFloat::Left as u32; - const RIGHT: u32 = structs::StyleFloat::Right as u32; - const NONE: u32 = structs::StyleFloat::None as u32; - match value { - LEFT => Float::Left, - RIGHT => Float::Right, - NONE => Float::None, - _ => unreachable!(), - } - }, + Float => longhands::float::SpecifiedValue::from_gecko_keyword(value), VerticalAlign => longhands::vertical_align::SpecifiedValue::from_gecko_keyword(value), TextAlign => longhands::text_align::SpecifiedValue::from_gecko_keyword(value), TextEmphasisPosition => longhands::text_emphasis_position::SpecifiedValue::from_gecko_keyword(value), + Clear => longhands::clear::SpecifiedValue::from_gecko_keyword(value), FontSize => { // We rely on Gecko passing in font-size values (0...7) here. longhands::font_size::SpecifiedValue::from_html_size(value as u8) }, FontStyle => { let val = if value == structs::NS_FONT_STYLE_ITALIC { FontStyle::Italic } else {
new file mode 100644 --- /dev/null +++ b/servo/tests/unit/style/keyframes.rs @@ -0,0 +1,259 @@ +/* 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/. */ + +use cssparser::SourceLocation; +use servo_arc::Arc; +use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, Importance}; +use style::properties::DeclarationSource; +use style::shared_lock::SharedRwLock; +use style::stylesheets::keyframes_rule::{Keyframe, KeyframesAnimation, KeyframePercentage, KeyframeSelector}; +use style::stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue}; +use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength}; + +macro_rules! longhand_set { + ($($word:ident),+) => {{ + let mut set = LonghandIdSet::new(); + $( + set.insert(LonghandId::$word); + )+ + set + }} +} + + +#[test] +fn test_empty_keyframe() { + let shared_lock = SharedRwLock::new(); + let keyframes = vec![]; + let animation = KeyframesAnimation::from_keyframes(&keyframes, + /* vendor_prefix = */ None, + &shared_lock.read()); + let expected = KeyframesAnimation { + steps: vec![], + properties_changed: LonghandIdSet::new(), + vendor_prefix: None, + }; + + assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected)); +} + +#[test] +fn test_no_property_in_keyframe() { + let shared_lock = SharedRwLock::new(); + let dummy_location = SourceLocation { line: 0, column: 0 }; + let keyframes = vec![ + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]), + block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new())), + source_location: dummy_location, + })), + ]; + let animation = KeyframesAnimation::from_keyframes(&keyframes, + /* vendor_prefix = */ None, + &shared_lock.read()); + let expected = KeyframesAnimation { + steps: vec![], + properties_changed: LonghandIdSet::new(), + vendor_prefix: None, + }; + + assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected)); +} + +#[test] +fn test_missing_property_in_initial_keyframe() { + let shared_lock = SharedRwLock::new(); + let declarations_on_initial_keyframe = + Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one( + PropertyDeclaration::Width( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal + ))); + + let declarations_on_final_keyframe = + Arc::new(shared_lock.wrap({ + let mut block = PropertyDeclarationBlock::new(); + block.push( + PropertyDeclaration::Width( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block.push( + PropertyDeclaration::Height( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block + })); + + let dummy_location = SourceLocation { line: 0, column: 0 }; + let keyframes = vec![ + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]), + block: declarations_on_initial_keyframe.clone(), + source_location: dummy_location, + })), + + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]), + block: declarations_on_final_keyframe.clone(), + source_location: dummy_location, + })), + ]; + let animation = KeyframesAnimation::from_keyframes(&keyframes, + /* vendor_prefix = */ None, + &shared_lock.read()); + let expected = KeyframesAnimation { + steps: vec![ + KeyframesStep { + start_percentage: KeyframePercentage(0.), + value: KeyframesStepValue::Declarations { block: declarations_on_initial_keyframe }, + declared_timing_function: false, + }, + KeyframesStep { + start_percentage: KeyframePercentage(1.), + value: KeyframesStepValue::Declarations { block: declarations_on_final_keyframe }, + declared_timing_function: false, + }, + ], + properties_changed: longhand_set!(Width, Height), + vendor_prefix: None, + }; + + assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected)); +} + +#[test] +fn test_missing_property_in_final_keyframe() { + let shared_lock = SharedRwLock::new(); + let declarations_on_initial_keyframe = + Arc::new(shared_lock.wrap({ + let mut block = PropertyDeclarationBlock::new(); + block.push( + PropertyDeclaration::Width( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block.push( + PropertyDeclaration::Height( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block + })); + + let declarations_on_final_keyframe = + Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one( + PropertyDeclaration::Height( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + ))); + + let dummy_location = SourceLocation { line: 0, column: 0 }; + let keyframes = vec![ + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]), + block: declarations_on_initial_keyframe.clone(), + source_location: dummy_location, + })), + + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]), + block: declarations_on_final_keyframe.clone(), + source_location: dummy_location, + })), + ]; + let animation = KeyframesAnimation::from_keyframes(&keyframes, + /* vendor_prefix = */ None, + &shared_lock.read()); + let expected = KeyframesAnimation { + steps: vec![ + KeyframesStep { + start_percentage: KeyframePercentage(0.), + value: KeyframesStepValue::Declarations { block: declarations_on_initial_keyframe }, + declared_timing_function: false, + }, + KeyframesStep { + start_percentage: KeyframePercentage(1.), + value: KeyframesStepValue::Declarations { block: declarations_on_final_keyframe }, + declared_timing_function: false, + }, + ], + properties_changed: longhand_set!(Width, Height), + vendor_prefix: None, + }; + + assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected)); +} + +#[test] +fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() { + let shared_lock = SharedRwLock::new(); + let declarations = + Arc::new(shared_lock.wrap({ + let mut block = PropertyDeclarationBlock::new(); + block.push( + PropertyDeclaration::Width( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block.push( + PropertyDeclaration::Height( + LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))), + Importance::Normal, + DeclarationSource::Parsing, + ); + block + })); + + let dummy_location = SourceLocation { line: 0, column: 0 }; + let keyframes = vec![ + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]), + block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new())), + source_location: dummy_location, + })), + Arc::new(shared_lock.wrap(Keyframe { + selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.5)]), + block: declarations.clone(), + source_location: dummy_location, + })), + ]; + let animation = KeyframesAnimation::from_keyframes(&keyframes, + /* vendor_prefix = */ None, + &shared_lock.read()); + let expected = KeyframesAnimation { + steps: vec![ + KeyframesStep { + start_percentage: KeyframePercentage(0.), + value: KeyframesStepValue::Declarations { + block: Arc::new(shared_lock.wrap( + // XXX: Should we use ComputedValues in this case? + PropertyDeclarationBlock::new() + )) + }, + declared_timing_function: false, + }, + KeyframesStep { + start_percentage: KeyframePercentage(0.5), + value: KeyframesStepValue::Declarations { block: declarations }, + declared_timing_function: false, + }, + KeyframesStep { + start_percentage: KeyframePercentage(1.), + value: KeyframesStepValue::ComputedValues, + declared_timing_function: false, + } + ], + properties_changed: longhand_set!(Width, Height), + vendor_prefix: None, + }; + + assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected)); +}
--- a/servo/tests/unit/style/lib.rs +++ b/servo/tests/unit/style/lib.rs @@ -20,16 +20,17 @@ extern crate servo_url; #[macro_use] extern crate size_of_test; #[macro_use] extern crate style; extern crate style_traits; extern crate test; mod animated_properties; mod attr; mod custom_properties; +mod keyframes; mod logical_geometry; mod parsing; mod properties; mod rule_tree; mod size_of; mod specified_values; mod str; mod stylesheets;
--- a/servo/tests/unit/style/stylesheets.rs +++ b/servo/tests/unit/style/stylesheets.rs @@ -12,34 +12,34 @@ use servo_atoms::Atom; use servo_config::prefs::{PREFS, PrefValue}; use servo_url::ServoUrl; use std::borrow::ToOwned; use std::cell::RefCell; use std::sync::atomic::AtomicBool; use style::context::QuirksMode; use style::error_reporting::{ParseErrorReporter, ContextualParseError}; use style::media_queries::MediaList; -use style::properties::{CSSWideKeyword, CustomDeclaration, DeclarationPushMode}; +use style::properties::{CSSWideKeyword, CustomDeclaration, DeclarationSource}; use style::properties::{DeclaredValueOwned, Importance}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; use style::properties::longhands::{self, animation_timing_function}; use style::shared_lock::SharedRwLock; use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Stylesheet, StylesheetContents, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule}; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage}; use style::values::{KeyframesName, CustomIdent}; use style::values::computed::Percentage; use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent}; use style::values::specified::transform::TimingFunction; pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock where I: IntoIterator<Item=(PropertyDeclaration, Importance)> { let mut block = PropertyDeclarationBlock::new(); for (d, i) in iterable { - block.push(d, i, DeclarationPushMode::Append); + block.push(d, i, DeclarationSource::CssOm); } block } #[test] fn test_parse_stylesheet() { let css = r" @namespace url(http://www.w3.org/1999/xhtml);