private static IEnumerable <ICSSFragment> Translate(ICSSFragmentBuilderComponent source, IEnumerable <ContainerFragment.SelectorSet> parentSelectors) { if (source == null) { throw new ArgumentNullException("source"); } if (parentSelectors == null) { throw new ArgumentNullException("parentSelectors"); } var parentSelectorsArray = parentSelectors.ToArray(); if (parentSelectorsArray.Any(s => s == null)) { throw new ArgumentException("Null reference encountered in parentSelectors set"); } var styleProperty = source as CSSFragmentBuilderStyleProperty; if (styleProperty != null) { var stylePropertyName = new StylePropertyName(styleProperty.Name, 0); return(new ICSSFragment[] { stylePropertyName, new StylePropertyValue( stylePropertyName, GetValueSections(styleProperty.Value), 0 ) }); } var selectorProperty = source as CSSFragmentBuilderSelector; if (selectorProperty != null) { var selectors = selectorProperty.Selectors; var childFragments = selectorProperty.Children.SelectMany(c => Translate(c, parentSelectors.Concat(new[] { selectorProperty.Selectors }))); ICSSFragment newFragment; if (selectors.First().Value.StartsWith("@media", StringComparison.InvariantCultureIgnoreCase)) { newFragment = new MediaQuery(selectors, parentSelectors, 0, childFragments); } else { newFragment = new Selector(selectors, parentSelectors, 0, childFragments); } return(new[] { newFragment }); } throw new ArgumentException("Unsupported type: " + source.GetType()); }
/// <summary> /// This will perform a case-insensitive match against the property name /// </summary> public static bool HasName(this StylePropertyName source, string value) { if (source == null) { throw new ArgumentNullException("source"); } if (value == null) { throw new ArgumentNullException("value"); } return(source.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase)); }
/// <summary> /// This returns the parsed fragments and the number of lines that were processed from the source in doing so /// </summary> private static Tuple <IEnumerable <ICSSFragment>, int> ParseIntoStructuredData( IEnumerator <CategorisedCharacterString> segmentEnumerator, IEnumerable <Selector.SelectorSet> parentSelectors, int sourceLineIndex, CommentHandlingOptions commentHandling) { if (segmentEnumerator == null) { throw new ArgumentNullException("segmentEnumerator"); } if (parentSelectors == null) { throw new ArgumentNullException("parentSelectors"); } if (sourceLineIndex < 0) { throw new ArgumentNullException("sourceLineIndex", "must be zero or greater"); } if (!Enum.IsDefined(typeof(CommentHandlingOptions), commentHandling)) { throw new ArgumentOutOfRangeException("commentHandling"); } var startingSourceLineIndex = sourceLineIndex; var fragments = new List <ICSSFragment>(); var selectorOrStyleContentBuffer = new StringBuilder(); var selectorOrStyleStartSourceLineIndex = -1; StylePropertyName lastStylePropertyName = null; var stylePropertyValueBuffer = new PropertyValueBuffer(); while (segmentEnumerator.MoveNext()) { var segment = segmentEnumerator.Current; if (segment == null) { throw new ArgumentException("Null reference encountered in segments set"); } switch (segment.CharacterCategorisation) { case CharacterCategorisationOptions.Comment: if (commentHandling == CommentHandlingOptions.Include) { fragments.Add(new Comment(segment.Value, sourceLineIndex)); } sourceLineIndex += GetNumberOfLineReturnsFromContentIfAny(segment.Value); continue; case CharacterCategorisationOptions.Whitespace: sourceLineIndex += GetNumberOfLineReturnsFromContentIfAny(segment.Value); if (selectorOrStyleContentBuffer.Length > 0) { selectorOrStyleContentBuffer.Append(" "); } continue; case CharacterCategorisationOptions.SelectorOrStyleProperty: if (selectorOrStyleContentBuffer.Length == 0) { selectorOrStyleStartSourceLineIndex = sourceLineIndex; } selectorOrStyleContentBuffer.Append(segment.Value); // If we were building up content for a StylePropertyValue then encountering other content means that the value must have terminated // (for valid CSS it should be only a semicolon or close brace that terminates a value but we're not concerned about invalid CSS here) if (stylePropertyValueBuffer.HasContent) { fragments.Add(stylePropertyValueBuffer.ExtractCombinedContentAndClear()); } continue; case CharacterCategorisationOptions.OpenBrace: if (selectorOrStyleContentBuffer.Length == 0) { throw new ArgumentException("Encountered OpenBrace with no preceding selector at line " + (sourceLineIndex + 1)); } // If we were building up content for a StylePropertyValue then encountering other content means that the value must have terminated // (for valid CSS it should be only a semicolon or close brace that terminates a value but we're not concerned about invalid CSS here) if (stylePropertyValueBuffer.HasContent) { fragments.Add(stylePropertyValueBuffer.ExtractCombinedContentAndClear()); } var selectors = GetSelectorSet(selectorOrStyleContentBuffer.ToString()); var parsedNestedContentDetails = ParseIntoStructuredData(segmentEnumerator, parentSelectors.Concat(new[] { selectors }), sourceLineIndex, commentHandling); if (selectors.First().Value.StartsWith("@media", StringComparison.InvariantCultureIgnoreCase)) { fragments.Add(new MediaQuery( selectors, parentSelectors, selectorOrStyleStartSourceLineIndex, parsedNestedContentDetails.Item1 // Item1 are the processed fragments (Item2 is the number of lines processed to extract those fragments) )); } else { fragments.Add(new Selector( selectors, parentSelectors, selectorOrStyleStartSourceLineIndex, parsedNestedContentDetails.Item1 // Item1 are the processed fragments (Item2 is the number of lines processed to extract those fragments) )); } sourceLineIndex += parsedNestedContentDetails.Item2; // Increase sourceLineIndex by the number of lines that the recursive call processed selectorOrStyleContentBuffer.Clear(); continue; case CharacterCategorisationOptions.CloseBrace: // If we were building up content for a StylePropertyValue then encountering other content means that the value must have terminated // (for valid CSS it should be only a semicolon or close brace that terminates a value but we're not concerned about invalid CSS here) if (stylePropertyValueBuffer.HasContent) { fragments.Add(stylePropertyValueBuffer.ExtractCombinedContentAndClear()); } if (selectorOrStyleContentBuffer.Length > 0) { fragments.Add(new StylePropertyName( selectorOrStyleContentBuffer.ToString(), selectorOrStyleStartSourceLineIndex )); } return(Tuple.Create <IEnumerable <ICSSFragment>, int>(fragments, sourceLineIndex - startingSourceLineIndex)); case CharacterCategorisationOptions.StylePropertyColon: case CharacterCategorisationOptions.SemiColon: // If we were building up content for a StylePropertyValue then encountering other content means that the value must have terminated // (for valid CSS it should be only a semicolon or close brace that terminates a value but we're not concerned about invalid CSS here) if (stylePropertyValueBuffer.HasContent) { fragments.Add(stylePropertyValueBuffer.ExtractCombinedContentAndClear()); } if (selectorOrStyleContentBuffer.Length > 0) { var selectorOrStyleContent = selectorOrStyleContentBuffer.ToString(); if (selectorOrStyleContent.StartsWith("@import", StringComparison.InvariantCultureIgnoreCase)) { fragments.Add(new Import( selectorOrStyleContent.Substring("@import".Length).Trim(), sourceLineIndex )); selectorOrStyleContentBuffer.Clear(); continue; } // Note: The SemiColon case here probably suggests invalid content, it should only follow a Value segment (ignoring // Comments and WhiteSpace), so if there is anything in the selectorOrStyleContentBuffer before the SemiColon then // it's probably not correct (but we're not validating for that here, we just don't want to throw anything away!) lastStylePropertyName = new StylePropertyName( selectorOrStyleContentBuffer.ToString(), selectorOrStyleStartSourceLineIndex ); fragments.Add(lastStylePropertyName); selectorOrStyleContentBuffer.Clear(); } continue; case CharacterCategorisationOptions.Value: if (selectorOrStyleContentBuffer.Length > 0) { var selectorOrStyleContent = selectorOrStyleContentBuffer.ToString(); if (selectorOrStyleContent.StartsWith("@import", StringComparison.InvariantCultureIgnoreCase)) { selectorOrStyleContentBuffer.Append(segment.Value); continue; } // This is presumably an error condition, there should be a colon between SelectorOrStyleProperty content and // Value content, but we're not validating here so just lump it all together lastStylePropertyName = new StylePropertyName( selectorOrStyleContentBuffer.ToString(), selectorOrStyleStartSourceLineIndex ); fragments.Add(lastStylePropertyName); selectorOrStyleContentBuffer.Clear(); } if (lastStylePropertyName == null) { throw new Exception("Invalid content, orphan style property value encountered"); } stylePropertyValueBuffer.Add(new StylePropertyValue( lastStylePropertyName, new[] { segment.Value }, selectorOrStyleStartSourceLineIndex )); continue; default: throw new ArgumentException("Unsupported CharacterCategorisationOptions value: " + segment.CharacterCategorisation); } } // If we have any content in the selectorOrStyleContentBuffer and we're hitting a CloseBrace then it's probably invalid content, // but just stash it away and move on! (The purpose of this work isn't to get too nuts about invalid CSS). if (selectorOrStyleContentBuffer.Length > 0) { var selectors = GetSelectorSet(selectorOrStyleContentBuffer.ToString()); if (selectors.First().Value.StartsWith("@media", StringComparison.InvariantCultureIgnoreCase)) { fragments.Add(new MediaQuery( selectors, parentSelectors, sourceLineIndex, new ICSSFragment[0] )); } else { fragments.Add(new Selector( selectors, parentSelectors, sourceLineIndex, new ICSSFragment[0] )); } } // It's very feasible that there will still be some style property value content in the buffer at this point, so ensure it // doesn't get lost if (stylePropertyValueBuffer.HasContent) { fragments.Add(stylePropertyValueBuffer.ExtractCombinedContentAndClear()); } return(Tuple.Create <IEnumerable <ICSSFragment>, int>(fragments, sourceLineIndex - startingSourceLineIndex)); }
private void Set <T>(StylePropertyName styleName, T value) { this.s.Set <T>(styleName, value); }
protected void VisitUrlFunction(PrimitiveTerm term) { string path = (string)term.Value; var response = URIHelpers.ValidateAssetURL(assetPath, path); if (response.hasWarningMessage) { m_Errors.AddValidationWarning(response.warningMessage, m_CurrentLine); } if (response.result != URIValidationResult.OK) { var(_, message) = ConvertErrorCode(response.result); m_Builder.AddValue(path, StyleValueType.MissingAssetReference); m_Errors.AddValidationWarning(string.Format(message, response.errorToken), m_CurrentLine); } else { var projectRelativePath = response.resolvedProjectRelativePath; var subAssetPath = response.resolvedSubAssetPath; var asset = response.resolvedQueryAsset; if (asset) { if (response.isLibraryAsset) { // do not add path dependencies on assets in the Library folder (e.g. built-in resources) m_Builder.AddValue(asset); return; } // explicit asset reference already loaded m_Context?.DependsOnSourceAsset(projectRelativePath); } else { asset = DeclareDependencyAndLoad(projectRelativePath, subAssetPath); } bool isTexture = asset is Texture2D; Sprite spriteAsset = asset as Sprite; if (isTexture && string.IsNullOrEmpty(subAssetPath)) { // Try to load a sprite sub-asset associated with this texture. // Sprites have extra data, such as slices and tight-meshes that // aren't stored in plain textures. spriteAsset = AssetDatabase.LoadAssetAtPath <Sprite>(projectRelativePath); } if (asset != null) { // Looking suffixed images files only if (isTexture) { string hiResImageLocation = URIHelpers.InjectFileNameSuffix(projectRelativePath, "@2x"); if (File.Exists(hiResImageLocation)) { UnityEngine.Object hiResImage = DeclareDependencyAndLoad(hiResImageLocation); if (hiResImage is Texture2D) { m_Builder.AddValue(new ScalableImage() { normalImage = asset as Texture2D, highResolutionImage = hiResImage as Texture2D }); } else { m_Errors.AddSemanticError(StyleSheetImportErrorCode.InvalidHighResolutionImage, string.Format(glossary.invalidHighResAssetType, asset.GetType().Name, projectRelativePath), m_CurrentLine); } return; } // If we didn't find an high res variant, tell ADB we depend on that potential file existing if (spriteAsset != null) { DeclareDependencyAndLoad(hiResImageLocation); } } Object assetToStore = spriteAsset != null ? spriteAsset : asset; m_Builder.AddValue(assetToStore); if (!disableValidation) { var propertyName = new StylePropertyName(m_Builder.currentProperty.name); // Unknown properties (not custom) should beforehand if (propertyName.id == StylePropertyId.Unknown) { return; } var allowed = StylePropertyUtil.GetAllowedAssetTypesForProperty(propertyName.id); // If no types were returned, it means this property doesn't support assets. // Normal syntax validation should cover this. if (!allowed.Any()) { return; } Type assetType = assetToStore.GetType(); // If none of the allowed types are compatible with the asset type, output a warning if (!allowed.Any(t => t.IsAssignableFrom(assetType))) { string allowedTypes = string.Join(", ", allowed.Select(t => t.Name)); m_Errors.AddValidationWarning( string.Format(glossary.invalidAssetType, assetType.Name, projectRelativePath, allowedTypes), m_CurrentLine); } } } else { // Asset is actually missing OR we couldn't load it for some reason; this should result in // response.result != URIValidationResult.OK (above) but if assets are deleted while Unity is // already open, we fall in here instead. var(_, message) = ConvertErrorCode(URIValidationResult.InvalidURIProjectAssetPath); // In case of error, we still want to call AddValue, with parameters to indicate the problem, in order // to keep the full layout from being discarded. We also add appropriate warnings to explain to the // user what is wrong. m_Builder.AddValue(path, StyleValueType.MissingAssetReference); m_Errors.AddValidationWarning(string.Format(message, path), m_CurrentLine); } } }
// https://drafts.csswg.org/css-transitions/#transition-shorthand-property // [ none | <single-transition-property> ] || <time> || <easing-function> || <time> private static void CompileTransition(StylePropertyReader reader, out List <TimeValue> outDelay, out List <TimeValue> outDuration, out List <StylePropertyName> outProperty, out List <EasingFunction> outTimingFunction) { s_TransitionDelayList.Clear(); s_TransitionDurationList.Clear(); s_TransitionPropertyList.Clear(); s_TransitionTimingFunctionList.Clear(); bool isValid = true; bool noneFound = false; var valueCount = reader.valueCount; int transitionCount = 0; int i = 0; do { // If none is present and there are more transitions the shorthand is considered invalid if (noneFound) { isValid = false; break; } var transitionProperty = InitialStyle.transitionProperty[0]; var transitionDuration = InitialStyle.transitionDuration[0]; var transitionDelay = InitialStyle.transitionDelay[0]; var transitionTimingFunction = InitialStyle.transitionTimingFunction[0]; bool durationFound = false; bool delayFound = false; bool propertyFound = false; bool timingFunctionFound = false; bool commaFound = false; for (; i < valueCount && !commaFound; ++i) { var valueType = reader.GetValueType(i); switch (valueType) { case StyleValueType.Keyword: if (reader.IsKeyword(i, StyleValueKeyword.None) && transitionCount == 0) { noneFound = true; propertyFound = true; transitionProperty = new StylePropertyName("none"); } else { isValid = false; } break; case StyleValueType.Dimension: var time = reader.ReadTimeValue(i); if (!durationFound) { // transition-duration durationFound = true; transitionDuration = time; } else if (!delayFound) { // transition-delay delayFound = true; transitionDelay = time; } else { isValid = false; } break; case StyleValueType.Enum: var str = reader.ReadAsString(i); if (!timingFunctionFound && StylePropertyUtil.TryGetEnumIntValue(StyleEnumType.EasingMode, str, out var intValue)) { // transition-timing-function timingFunctionFound = true; transitionTimingFunction = (EasingMode)intValue; } else if (!propertyFound) { // transition-property propertyFound = true; transitionProperty = new StylePropertyName(str); } else { isValid = false; } break; case StyleValueType.CommaSeparator: commaFound = true; ++transitionCount; break; default: isValid = false; break; } } s_TransitionDelayList.Add(transitionDelay); s_TransitionDurationList.Add(transitionDuration); s_TransitionPropertyList.Add(transitionProperty); s_TransitionTimingFunctionList.Add(transitionTimingFunction); }while (i < valueCount && isValid); if (isValid) { outProperty = s_TransitionPropertyList; outDelay = s_TransitionDelayList; outDuration = s_TransitionDurationList; outTimingFunction = s_TransitionTimingFunctionList; } else { outProperty = InitialStyle.transitionProperty; outDelay = InitialStyle.transitionDelay; outDuration = InitialStyle.transitionDuration; outTimingFunction = InitialStyle.transitionTimingFunction; } }