Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: Source/core/css/resolver/StyleResolver.cpp

Issue 23875044: Web Animations: Correctly handle incomplete keyframes in CSS animations (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Handle end keyframes first Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/css/resolver/StyleResolver.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t orchmobile.com/) 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t orchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
76 #include "core/rendering/style/KeyframeList.h" 76 #include "core/rendering/style/KeyframeList.h"
77 #include "core/rendering/style/StyleCustomFilterProgramCache.h" 77 #include "core/rendering/style/StyleCustomFilterProgramCache.h"
78 #include "core/svg/SVGDocumentExtensions.h" 78 #include "core/svg/SVGDocumentExtensions.h"
79 #include "core/svg/SVGElement.h" 79 #include "core/svg/SVGElement.h"
80 #include "core/svg/SVGFontFaceElement.h" 80 #include "core/svg/SVGFontFaceElement.h"
81 #include "wtf/StdLibExtras.h" 81 #include "wtf/StdLibExtras.h"
82 #include "wtf/Vector.h" 82 #include "wtf/Vector.h"
83 83
84 using namespace std; 84 using namespace std;
85 85
86 namespace {
87
88 using namespace WebCore;
89
90 PassRefPtr<TimingFunction> generateTimingFunction(const KeyframeAnimationEffect: :KeyframeVector keyframes, const HashMap<double, RefPtr<TimingFunction> > perKey frameTimingFunctions)
91 {
92 // Generate the chained timing function. Note that timing functions apply
93 // from the keyframe in which they're specified to the next keyframe.
94 bool isTimingFunctionLinearThroughout = true;
95 RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction: :create();
96 for (size_t i = 0; i < keyframes.size() - 1; ++i) {
97 double lowerBound = keyframes[i]->offset();
98 ASSERT(lowerBound >=0 && lowerBound < 1);
99 double upperBound = keyframes[i + 1]->offset();
100 ASSERT(upperBound > 0 && upperBound <= 1);
101 TimingFunction* timingFunction = perKeyframeTimingFunctions.get(lowerBou nd);
102 isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunc tion::LinearFunction;
103 chainedTimingFunction->appendSegment(upperBound, timingFunction);
104 }
105 if (isTimingFunctionLinearThroughout)
106 return LinearTimingFunction::create();
107 return chainedTimingFunction;
108 }
109
110 } // namespace
111
86 namespace WebCore { 112 namespace WebCore {
87 113
88 using namespace HTMLNames; 114 using namespace HTMLNames;
89 115
90 RenderStyle* StyleResolver::s_styleNotYetAvailable; 116 RenderStyle* StyleResolver::s_styleNotYetAvailable;
91 117
92 static StylePropertySet* leftToRightDeclaration() 118 static StylePropertySet* leftToRightDeclaration()
93 { 119 {
94 DEFINE_STATIC_LOCAL(RefPtr<MutableStylePropertySet>, leftToRightDecl, (Mutab leStylePropertySet::create())); 120 DEFINE_STATIC_LOCAL(RefPtr<MutableStylePropertySet>, leftToRightDecl, (Mutab leStylePropertySet::create()));
95 if (leftToRightDecl->isEmpty()) 121 if (leftToRightDecl->isEmpty())
(...skipping 730 matching lines...) Expand 10 before | Expand all | Expand 10 after
826 hundredPercentKeyframe = StyleKeyframe::create().leakRef(); 852 hundredPercentKeyframe = StyleKeyframe::create().leakRef();
827 hundredPercentKeyframe->setKeyText("100%"); 853 hundredPercentKeyframe->setKeyText("100%");
828 } 854 }
829 KeyframeValue keyframeValue(1, 0); 855 KeyframeValue keyframeValue(1, 0);
830 keyframeValue.setStyle(styleForKeyframe(e, elementStyle, hundredPercentK eyframe)); 856 keyframeValue.setStyle(styleForKeyframe(e, elementStyle, hundredPercentK eyframe));
831 keyframeValue.addProperties(hundredPercentKeyframe->properties()); 857 keyframeValue.addProperties(hundredPercentKeyframe->properties());
832 list.insert(keyframeValue); 858 list.insert(keyframeValue);
833 } 859 }
834 } 860 }
835 861
836 void StyleResolver::resolveKeyframes(const Element* element, const RenderStyle* style, const AtomicString& name, TimingFunction* defaultTimingFunction, Keyframe AnimationEffect::KeyframeVector& keyframes, RefPtr<TimingFunction>& timingFuncti on) 862 void StyleResolver::resolveKeyframes(const Element* element, const RenderStyle* style, const AtomicString& name, TimingFunction* defaultTimingFunction, Vector<s td::pair<KeyframeAnimationEffect::KeyframeVector, RefPtr<TimingFunction> > >& ke yframesAndTimingFunctions)
837 { 863 {
838 ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); 864 ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled());
839 const StyleRuleKeyframes* keyframesRule = matchScopedKeyframesRule(element, name.impl()); 865 const StyleRuleKeyframes* keyframesRule = matchScopedKeyframesRule(element, name.impl());
840 if (!keyframesRule) 866 if (!keyframesRule)
841 return; 867 return;
842 868
869 const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyfra mes();
870 if (styleKeyframes.isEmpty())
871 return;
872
843 // Construct and populate the style for each keyframe 873 // Construct and populate the style for each keyframe
844 HashMap<double, RefPtr<TimingFunction> > timingFunctions; 874 KeyframeAnimationEffect::KeyframeVector keyframes;
845 const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyfra mes(); 875 HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions;
846 for (size_t i = 0; i < styleKeyframes.size(); ++i) { 876 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
847 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get(); 877 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
848 RefPtr<RenderStyle> keyframeStyle = styleForKeyframe(0, style, styleKeyf rame); 878 RefPtr<RenderStyle> keyframeStyle = styleForKeyframe(0, style, styleKeyf rame);
849 RefPtr<Keyframe> keyframe = Keyframe::create(); 879 RefPtr<Keyframe> keyframe = Keyframe::create();
850 const Vector<double>& offsets = styleKeyframe->keys(); 880 const Vector<double>& offsets = styleKeyframe->keys();
851 ASSERT(!offsets.isEmpty()); 881 ASSERT(!offsets.isEmpty());
852 keyframe->setOffset(offsets[0]); 882 keyframe->setOffset(offsets[0]);
853 TimingFunction* timingFunction = defaultTimingFunction; 883 TimingFunction* timingFunction = defaultTimingFunction;
854 const StylePropertySet* properties = styleKeyframe->properties(); 884 const StylePropertySet* properties = styleKeyframe->properties();
855 for (unsigned j = 0; j < properties->propertyCount(); j++) { 885 for (unsigned j = 0; j < properties->propertyCount(); j++) {
856 CSSPropertyID property = properties->propertyAt(j).id(); 886 CSSPropertyID property = properties->propertyAt(j).id();
857 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) { 887 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
858 // FIXME: This sometimes gets the wrong timing function. See crb ug.com/288540. 888 // FIXME: This sometimes gets the wrong timing function. See crb ug.com/288540.
859
860 timingFunction = KeyframeValue::timingFunction(keyframeStyle.get (), name); 889 timingFunction = KeyframeValue::timingFunction(keyframeStyle.get (), name);
861 } else if (CSSAnimations::isAnimatableProperty(property)) { 890 } else if (CSSAnimations::isAnimatableProperty(property)) {
862 keyframe->setPropertyValue(property, CSSAnimatableValueFactory:: create(property, keyframeStyle.get()).get()); 891 keyframe->setPropertyValue(property, CSSAnimatableValueFactory:: create(property, keyframeStyle.get()).get());
863 } 892 }
864 } 893 }
865 keyframes.append(keyframe); 894 keyframes.append(keyframe);
866 // The last keyframe specified at a given offset is used. 895 // The last keyframe specified at a given offset is used.
867 timingFunctions.set(offsets[0], timingFunction); 896 perKeyframeTimingFunctions.set(offsets[0], timingFunction);
868 for (size_t j = 1; j < offsets.size(); ++j) { 897 for (size_t j = 1; j < offsets.size(); ++j) {
869 keyframes.append(keyframe->cloneWithOffset(offsets[j])); 898 keyframes.append(keyframe->cloneWithOffset(offsets[j]));
870 timingFunctions.set(offsets[j], timingFunction); 899 perKeyframeTimingFunctions.set(offsets[j], timingFunction);
871 } 900 }
872 } 901 }
902 ASSERT(!keyframes.isEmpty());
873 903
874 if (keyframes.isEmpty()) 904 if (!perKeyframeTimingFunctions.contains(0))
875 return; 905 perKeyframeTimingFunctions.set(0, defaultTimingFunction);
876 906
877 // Remove duplicate keyframes. In CSS the last keyframe at a given offset ta kes priority. 907 // Remove duplicate keyframes. In CSS the last keyframe at a given offset ta kes priority.
878 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffset s); 908 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffset s);
879 size_t targetIndex = 0; 909 size_t targetIndex = 0;
880 for (size_t i = 1; i < keyframes.size(); i++) { 910 for (size_t i = 1; i < keyframes.size(); i++) {
881 if (keyframes[i]->offset() != keyframes[targetIndex]->offset()) 911 if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
882 targetIndex++; 912 targetIndex++;
883 if (targetIndex != i) 913 if (targetIndex != i)
884 keyframes[targetIndex] = keyframes[i]; 914 keyframes[targetIndex] = keyframes[i];
885 } 915 }
886 keyframes.shrink(targetIndex + 1); 916 keyframes.shrink(targetIndex + 1);
887 917
888 // Add 0% and 100% keyframes if absent. 918 // Add 0% and 100% keyframes if absent.
889 RefPtr<Keyframe> startKeyframe = keyframes[0]; 919 RefPtr<Keyframe> startKeyframe = keyframes[0];
890 if (startKeyframe->offset()) { 920 if (startKeyframe->offset()) {
891 startKeyframe = Keyframe::create(); 921 startKeyframe = Keyframe::create();
892 startKeyframe->setOffset(0); 922 startKeyframe->setOffset(0);
893 keyframes.prepend(startKeyframe); 923 keyframes.prepend(startKeyframe);
894 } 924 }
895 RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1]; 925 RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1];
896 if (endKeyframe->offset() != 1) { 926 if (endKeyframe->offset() != 1) {
897 endKeyframe = Keyframe::create(); 927 endKeyframe = Keyframe::create();
898 endKeyframe->setOffset(1); 928 endKeyframe->setOffset(1);
899 keyframes.append(endKeyframe); 929 keyframes.append(endKeyframe);
900 } 930 }
901 ASSERT(keyframes.size() >= 2); 931 ASSERT(keyframes.size() >= 2);
902 ASSERT(!keyframes.first()->offset()); 932 ASSERT(!keyframes.first()->offset());
903 ASSERT(keyframes.last()->offset() == 1); 933 ASSERT(keyframes.last()->offset() == 1);
904 934
905 // Generate the chained timing function. Note that timing functions apply
906 // from the keyframe in which they're specified to the next keyframe.
907 // FIXME: Handle keyframe sets where some keyframes don't specify all
908 // properties. In this case, timing functions apply between the keyframes
909 // which specify a particular property, so we'll need a separate chained
910 // timing function (and therefore animation) for each property. See
911 // LayoutTests/animations/missing-keyframe-properties-timing-function.html
912 if (!timingFunctions.contains(0))
913 timingFunctions.set(0, defaultTimingFunction);
914 bool isTimingFunctionLinearThroughout = true;
915 RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction: :create();
916 for (size_t i = 0; i < keyframes.size() - 1; ++i) {
917 double lowerBound = keyframes[i]->offset();
918 ASSERT(lowerBound >=0 && lowerBound < 1);
919 double upperBound = keyframes[i + 1]->offset();
920 ASSERT(upperBound > 0 && upperBound <= 1);
921 TimingFunction* timingFunction = timingFunctions.get(lowerBound);
922 ASSERT(timingFunction);
923 isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunc tion::LinearFunction;
924 chainedTimingFunction->appendSegment(upperBound, timingFunction);
925 }
926 if (isTimingFunctionLinearThroughout)
927 timingFunction = LinearTimingFunction::create();
928 else
929 timingFunction = chainedTimingFunction;
930
931 // Snapshot current property values for 0% and 100% if missing. 935 // Snapshot current property values for 0% and 100% if missing.
932 PropertySet allProperties; 936 PropertySet allProperties;
933 for (size_t i = 0; i < keyframes.size(); i++) { 937 size_t numKeyframes = keyframes.size();
938 for (size_t i = 0; i < numKeyframes; i++) {
934 const PropertySet& keyframeProperties = keyframes[i]->properties(); 939 const PropertySet& keyframeProperties = keyframes[i]->properties();
935 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter) 940 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
936 allProperties.add(*iter); 941 allProperties.add(*iter);
937 } 942 }
938 const PropertySet& startKeyframeProperties = startKeyframe->properties(); 943 const PropertySet& startKeyframeProperties = startKeyframe->properties();
939 const PropertySet& endKeyframeProperties = endKeyframe->properties(); 944 const PropertySet& endKeyframeProperties = endKeyframe->properties();
940 bool missingStartValues = startKeyframeProperties.size() < allProperties.siz e(); 945 bool missingStartValues = startKeyframeProperties.size() < allProperties.siz e();
941 bool missingEndValues = endKeyframeProperties.size() < allProperties.size(); 946 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
942 if (!missingStartValues && !missingEndValues) 947 if (missingStartValues || missingEndValues) {
943 return; 948 for (PropertySet::const_iterator iter = allProperties.begin(); iter != a llProperties.end(); ++iter) {
944 for (PropertySet::const_iterator iter = allProperties.begin(); iter != allPr operties.end(); ++iter) { 949 const CSSPropertyID property = *iter;
945 const CSSPropertyID property = *iter; 950 bool startNeedsValue = missingStartValues && !startKeyframePropertie s.contains(property);
946 bool startNeedsValue = missingStartValues && !startKeyframeProperties.co ntains(property); 951 bool endNeedsValue = missingEndValues && !endKeyframeProperties.cont ains(property);
947 bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains (property); 952 if (!startNeedsValue && !endNeedsValue)
948 if (!startNeedsValue && !endNeedsValue) 953 continue;
949 continue; 954 RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::c reate(property, style);
950 RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::creat e(property, style); 955 if (startNeedsValue)
951 if (startNeedsValue) 956 startKeyframe->setPropertyValue(property, snapshotValue.get());
952 startKeyframe->setPropertyValue(property, snapshotValue.get()); 957 if (endNeedsValue)
953 if (endNeedsValue) 958 endKeyframe->setPropertyValue(property, snapshotValue.get());
954 endKeyframe->setPropertyValue(property, snapshotValue.get()); 959 }
955 } 960 }
956 ASSERT(startKeyframe->properties().size() == allProperties.size()); 961 ASSERT(startKeyframe->properties().size() == allProperties.size());
957 ASSERT(endKeyframe->properties().size() == allProperties.size()); 962 ASSERT(endKeyframe->properties().size() == allProperties.size());
963
964 // Determine how many keyframes specify each property. Note that this must
965 // be done after we've filled in end keyframes.
966 typedef HashCountedSet<CSSPropertyID> PropertyCountedSet;
967 PropertyCountedSet propertyCounts;
968 for (size_t i = 0; i < numKeyframes; ++i) {
969 const PropertySet& properties = keyframes[i]->properties();
970 for (PropertySet::const_iterator iter = properties.begin(); iter != prop erties.end(); ++iter)
971 propertyCounts.add(*iter);
972 }
973
974 // Split keyframes into groups, where each group contains only keyframes
975 // which specify all properties used in that group. Each group is animated
976 // in a separate animation, to allow per-keyframe timing functions to be
977 // applied correctly.
978 for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) {
979 const CSSPropertyID property = iter->key;
980 const size_t count = iter->value;
981 ASSERT(count <= numKeyframes);
982 if (count == numKeyframes)
983 continue;
984 KeyframeAnimationEffect::KeyframeVector splitOutKeyframes;
985 for (size_t i = 0; i < numKeyframes; i++) {
986 Keyframe* keyframe = keyframes[i].get();
987 if (!keyframe->properties().contains(property)) {
988 ASSERT(i && i != numKeyframes - 1);
989 continue;
990 }
991 RefPtr<Keyframe> clonedKeyframe = Keyframe::create();
992 clonedKeyframe->setOffset(keyframe->offset());
993 clonedKeyframe->setComposite(keyframe->composite());
994 clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(p roperty));
995 splitOutKeyframes.append(clonedKeyframe);
996 // Note that it's OK if this keyframe ends up having no
997 // properties. This can only happen when none of the properties
998 // are specified in all keyframes, in which case we won't animate
999 // anything with these keyframes.
1000 keyframe->clearPropertyValue(property);
1001 }
1002 ASSERT(!splitOutKeyframes.first()->offset());
1003 ASSERT(splitOutKeyframes.last()->offset() == 1);
1004 #ifndef NDEBUG
1005 for (size_t j = 0; j < splitOutKeyframes.size(); ++j)
1006 ASSERT(splitOutKeyframes[j]->properties().size() == 1);
1007 #endif
1008 keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, gen erateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions)));
1009 }
1010
1011 size_t numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties( ).size();
1012 #ifndef NDEBUG
1013 for (size_t i = 1; i < numKeyframes; ++i)
1014 ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllK eyframes);
1015 #endif
1016
1017 // If the animation specifies any keyframes, we always provide at least one
1018 // vector of resolved keyframes, even if no properties are animated.
1019 if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEm pty())
1020 keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTim ingFunction(keyframes, perKeyframeTimingFunctions)));
958 } 1021 }
959 1022
960 PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element* e, const P seudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle) 1023 PassRefPtr<RenderStyle> StyleResolver::pseudoStyleForElement(Element* e, const P seudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle)
961 { 1024 {
962 ASSERT(document().frame()); 1025 ASSERT(document().frame());
963 ASSERT(documentSettings()); 1026 ASSERT(documentSettings());
964 ASSERT(parentStyle); 1027 ASSERT(parentStyle);
965 if (!e) 1028 if (!e)
966 return 0; 1029 return 0;
967 1030
(...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after
1605 m_matchedPropertiesSearches, m_matchedPropertiesHit, m_matchedProperties SharedInheritedHit, m_matchedPropertiesToCache, m_matchedPropertiesEnteredIntoCa che); 1668 m_matchedPropertiesSearches, m_matchedPropertiesHit, m_matchedProperties SharedInheritedHit, m_matchedPropertiesToCache, m_matchedPropertiesEnteredIntoCa che);
1606 1669
1607 fprintf(stderr, "Total:\n"); 1670 fprintf(stderr, "Total:\n");
1608 printStyleStats(m_totalSearches, m_totalElementsEligibleForSharing, m_totalS tylesShared, m_totalSearchFoundSiblingForSharing, m_totalSearchesMissedSharing, 1671 printStyleStats(m_totalSearches, m_totalElementsEligibleForSharing, m_totalS tylesShared, m_totalSearchFoundSiblingForSharing, m_totalSearchesMissedSharing,
1609 m_totalMatchedPropertiesSearches, m_totalMatchedPropertiesHit, m_totalMa tchedPropertiesSharedInheritedHit, m_totalMatchedPropertiesToCache, m_totalMatch edPropertiesEnteredIntoCache); 1672 m_totalMatchedPropertiesSearches, m_totalMatchedPropertiesHit, m_totalMa tchedPropertiesSharedInheritedHit, m_totalMatchedPropertiesToCache, m_totalMatch edPropertiesEnteredIntoCache);
1610 fprintf(stderr, "----------------------------------------------------------- ---------------------\n"); 1673 fprintf(stderr, "----------------------------------------------------------- ---------------------\n");
1611 } 1674 }
1612 #endif 1675 #endif
1613 1676
1614 } // namespace WebCore 1677 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/css/resolver/StyleResolver.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698