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

Side by Side Diff: Source/core/css/parser/BisonCSSParser-in.cpp

Issue 149373004: [CSS Grid Layout] Implementation of the grid-template shorthand. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@grid-template-working
Patch Set: Created 6 years, 10 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
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2003 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 3 * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> 5 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
6 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> 6 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
7 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/) 7 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/)
8 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. 8 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
9 * Copyright (C) 2012 Intel Corporation. All rights reserved. 9 * Copyright (C) 2012 Intel Corporation. All rights reserved.
10 * 10 *
(...skipping 2470 matching lines...) Expand 10 before | Expand all | Expand 10 after
2481 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2481 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled())
2482 return false; 2482 return false;
2483 return parseGridAreaShorthand(important); 2483 return parseGridAreaShorthand(important);
2484 2484
2485 case CSSPropertyGridTemplateAreas: 2485 case CSSPropertyGridTemplateAreas:
2486 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2486 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled())
2487 return false; 2487 return false;
2488 parsedValue = parseGridTemplateAreas(); 2488 parsedValue = parseGridTemplateAreas();
2489 break; 2489 break;
2490 2490
2491 case CSSPropertyGridTemplate:
2492 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled())
2493 return false;
2494 return parseGridTemplateShorthand(important);
2495
2491 case CSSPropertyWebkitMarginCollapse: { 2496 case CSSPropertyWebkitMarginCollapse: {
2492 if (num == 1) { 2497 if (num == 1) {
2493 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2498 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse);
2494 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], imp ortant)) 2499 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], imp ortant))
2495 return false; 2500 return false;
2496 CSSValue* value = m_parsedProperties.last().value(); 2501 CSSValue* value = m_parsedProperties.last().value();
2497 addProperty(webkitMarginCollapseShorthand().properties()[1], value, important); 2502 addProperty(webkitMarginCollapseShorthand().properties()[1], value, important);
2498 return true; 2503 return true;
2499 } 2504 }
2500 else if (num == 2) { 2505 else if (num == 2) {
(...skipping 2210 matching lines...) Expand 10 before | Expand all | Expand 10 after
4711 return false; 4716 return false;
4712 } else { 4717 } else {
4713 endValue = gridMissingGridPositionValue(startValue.get()); 4718 endValue = gridMissingGridPositionValue(startValue.get());
4714 } 4719 }
4715 4720
4716 addProperty(shorthand.properties()[0], startValue, important); 4721 addProperty(shorthand.properties()[0], startValue, important);
4717 addProperty(shorthand.properties()[1], endValue, important); 4722 addProperty(shorthand.properties()[1], endValue, important);
4718 return true; 4723 return true;
4719 } 4724 }
4720 4725
4726 bool BisonCSSParser::parseGridTemplateRowsAndAreas(bool important)
4727 {
4728 NamedGridAreaMap gridAreaMap;
4729 size_t rowCount = 0;
4730 size_t columnCount = 0;
4731 bool traillingAdded = false;
4732 RefPtr<CSSValueList> templateRows = CSSValueList::createSpaceSeparated();
4733 CSSParserValue* currentValue = m_valueList->current();
4734
4735 // There must be either template-areas or template-rows.
4736 if (!currentValue)
4737 return false;
4738
4739 do {
4740 // Handle leading/trailling <ident>*.
4741 if (currentValue->unit == CSSParserValue::ValueList)
4742 parseGridLineNames(m_valueList.get(), *templateRows, traillingAdded) ;
4743
4744 // Handle a template-area's row.
4745 if (parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount))
4746 ++rowCount;
4747
4748 // If no template-areas, template-rows must be defined.
4749 if (!((currentValue = m_valueList->current()) || rowCount))
4750 return false;
4751
4752 // Handle template-rows's track-size.
4753 if (currentValue && currentValue->unit != CSSParserValue::ValueList) {
4754 RefPtr<CSSValue> value = parseGridTrackSize(*m_valueList);
4755 if (!(value || rowCount))
4756 return false;
4757 templateRows->append(value);
4758 } else {
4759 templateRows->append(cssValuePool().createIdentifierValue(CSSValueAu to));
4760 }
4761
4762 // This will handle the trailing <ident>* in the grammar.
4763 traillingAdded = false;
4764 if (m_valueList->current() && m_valueList->current()->unit == CSSParserV alue::ValueList) {
4765 parseGridLineNames(m_valueList.get(), *templateRows);
4766 traillingAdded = true;
4767 }
4768 } while ((currentValue = m_valueList->current()));
4769
4770 if (!templateRows->length())
4771 addProperty(CSSPropertyGridTemplateRows, cssValuePool().createIdentifier Value(CSSValueNone), important);
4772 else
4773 addProperty(CSSPropertyGridTemplateRows, templateRows.release(), importa nt);
4774 if (!rowCount) {
4775 addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createIdentifie rValue(CSSValueNone), important);
4776 } else {
4777 RefPtr<CSSValue> templateAreas = CSSGridTemplateAreasValue::create(gridA reaMap, rowCount, columnCount);
4778 addProperty(CSSPropertyGridTemplateAreas, templateAreas.release(), impor tant);
4779 }
4780
4781 return true;
4782 }
4783
4784 bool BisonCSSParser::parseGridTemplateShorthand(bool important)
4785 {
4786 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
4787
4788 ShorthandScope scope(this, CSSPropertyGridTemplate);
4789 const StylePropertyShorthand& shorthand = gridTemplateShorthand();
4790 ASSERT_UNUSED(shorthand, shorthand.length() == 3);
4791
4792 RefPtr<CSSValue> columnsValue = cssValuePool().createIdentifierValue(CSSValu eNone);
4793 RefPtr<CSSValue> rowsValue = cssValuePool().createIdentifierValue(CSSValueNo ne);
4794 RefPtr<CSSValue> areasValue = cssValuePool().createIdentifierValue(CSSValueN one);
4795
4796 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
4797 CSSParserValue* current = m_valueList->current();
4798
4799 if (!current)
4800 return false;
svillar 2014/02/07 12:02:49 Better do this before creating all the above objec
4801
4802 NamedGridAreaMap gridAreaMap;
4803 size_t rowCount = 0;
4804 size_t columnCount = 0;
4805 bool forwardOp = false;
svillar 2014/02/07 12:02:49 Use a more descriptive name like seenForwardSlashO
4806 bool traillingAdded = false;
svillar 2014/02/07 12:02:49 trailing. Also traillingAdded is a very confusing
4807 bool simpleForm = true; // template-columns / template-rows
svillar 2014/02/07 12:02:49 Use a more descriptive name for this variable, we
4808 do {
4809 if (isForwardSlashOperator(current)) {
4810 if (forwardOp)
4811 return false; // Slash not allowed in the second clause.
4812 if (!values->length())
4813 return false; // First clause is mandatory.
4814 if (!(current = m_valueList->next()))
4815 return false; // Second clause is mandatory.
svillar 2014/02/07 12:02:49 I guess you could put these three in a single row
4816 forwardOp = true;
4817 columnsValue = values.release();
4818 values = CSSValueList::createSpaceSeparated();
4819 }
4820
4821 // Handle leading/trailling <ident>*.
svillar 2014/02/07 12:02:49 trailing
4822 if (current->unit == CSSParserValue::ValueList) {
4823 if (traillingAdded && simpleForm) // Not allowed in simple form.
4824 return false;
4825 parseGridLineNames(m_valueList.get(), *values, traillingAdded);
4826 if (!(current = m_valueList->current()))
4827 break;
4828 }
4829
4830 // Handle a template-area's row.
4831 if (current->unit == CSSPrimitiveValue::CSS_STRING) {
4832 if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount))
4833 return false;
4834 ++rowCount;
4835 simpleForm = false;
4836 if (!(current = m_valueList->current()))
4837 break;
4838 }
4839
4840 // Handle template-{columns/rows}'s track-size, repeat or auto.
4841 if (current->unit == CSSParserValue::Function && equalIgnoringCase(curre nt->function->name, "repeat(")) {
4842 if (!simpleForm)
4843 return false; // Not allowed for template-row in complex form.
4844 if (!parseGridTrackRepeatFunction(*values))
4845 return false;
4846 } else if (current->unit != CSSParserValue::ValueList) {
4847 RefPtr<CSSValue> value = parseGridTrackSize(*m_valueList);
4848 if (!value && simpleForm) // Mandatory in template-columns, "auto" i n 2nd form template-rows.
4849 return false;
4850 values->append(value);
4851 } else {
4852 if (simpleForm)
4853 return false; // track-size mandatory in template-columns
4854 values->append(cssValuePool().createIdentifierValue(CSSValueAuto));
4855 }
4856
4857 if (!(current = m_valueList->current()))
4858 break;
4859
4860 // This will handle the trailing <ident>* in the grammar.
4861 traillingAdded = false;
4862 if (current && current->unit == CSSParserValue::ValueList) {
4863 parseGridLineNames(m_valueList.get(), *values);
4864 traillingAdded = true;
4865 current = m_valueList->current();
4866 }
4867 } while ((current = m_valueList->current()));
4868
4869 if (rowCount)
4870 areasValue = CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, co lumnCount);
4871
4872 if (forwardOp) {
4873 if (values->length() > 0)
4874 rowsValue = values;
4875 } else {
4876 if (simpleForm)
4877 return false; // 2 mandatory clauses in simple form.
svillar 2014/02/07 12:02:49 It looks like this should be an ASSERT but haven't
4878 if (values->length() > 0)
4879 columnsValue = values;
4880 }
4881
4882 addProperty(CSSPropertyGridTemplateColumns, columnsValue, important);
4883 addProperty(CSSPropertyGridTemplateRows, rowsValue, important);
4884 addProperty(CSSPropertyGridTemplateAreas, areasValue, important);
4885
4886 return true;
4887 }
4888
4721 bool BisonCSSParser::parseGridAreaShorthand(bool important) 4889 bool BisonCSSParser::parseGridAreaShorthand(bool important)
4722 { 4890 {
4723 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4891 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
4724 4892
4725 ShorthandScope scope(this, CSSPropertyGridArea); 4893 ShorthandScope scope(this, CSSPropertyGridArea);
4726 const StylePropertyShorthand& shorthand = gridAreaShorthand(); 4894 const StylePropertyShorthand& shorthand = gridAreaShorthand();
4727 ASSERT_UNUSED(shorthand, shorthand.length() == 4); 4895 ASSERT_UNUSED(shorthand, shorthand.length() == 4);
4728 4896
4729 RefPtr<CSSValue> rowStartValue = parseGridPosition(); 4897 RefPtr<CSSValue> rowStartValue = parseGridPosition();
4730 if (!rowStartValue) 4898 if (!rowStartValue)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
4766 if (!isForwardSlashOperator(m_valueList->current())) 4934 if (!isForwardSlashOperator(m_valueList->current()))
4767 return false; 4935 return false;
4768 4936
4769 if (!m_valueList->next()) 4937 if (!m_valueList->next())
4770 return false; 4938 return false;
4771 4939
4772 property = parseGridPosition(); 4940 property = parseGridPosition();
4773 return true; 4941 return true;
4774 } 4942 }
4775 4943
4776 void BisonCSSParser::parseGridLineNames(CSSParserValueList* parserValueList, CSS ValueList& valueList) 4944 void BisonCSSParser::parseGridLineNames(CSSParserValueList* parserValueList, CSS ValueList& valueList, bool concat)
4777 { 4945 {
4778 ASSERT(parserValueList->current() && parserValueList->current()->unit == CSS ParserValue::ValueList); 4946 ASSERT(parserValueList->current() && parserValueList->current()->unit == CSS ParserValue::ValueList);
4779 4947
4780 CSSParserValueList* identList = parserValueList->current()->valueList; 4948 CSSParserValueList* identList = parserValueList->current()->valueList;
4781 if (!identList->size()) { 4949 if (!identList->size()) {
4782 parserValueList->next(); 4950 parserValueList->next();
4783 return; 4951 return;
4784 } 4952 }
4785 4953
4786 RefPtr<CSSGridLineNamesValue> lineNames = CSSGridLineNamesValue::create(); 4954 RefPtr<CSSGridLineNamesValue> lineNames = concat && valueList.length() > 0 ? static_cast< CSSGridLineNamesValue* >(valueList.item(valueList.length() - 1)) : CSSGridLineNamesValue::create();
4787 while (CSSParserValue* identValue = identList->current()) { 4955 while (CSSParserValue* identValue = identList->current()) {
4788 ASSERT(identValue->unit == CSSPrimitiveValue::CSS_IDENT); 4956 ASSERT(identValue->unit == CSSPrimitiveValue::CSS_IDENT);
4789 RefPtr<CSSPrimitiveValue> lineName = createPrimitiveStringValue(identVal ue); 4957 RefPtr<CSSPrimitiveValue> lineName = createPrimitiveStringValue(identVal ue);
4790 lineNames->append(lineName.release()); 4958 lineNames->append(lineName.release());
4791 identList->next(); 4959 identList->next();
4792 } 4960 }
4793 valueList.append(lineNames.release()); 4961 if (!concat)
4962 valueList.append(lineNames.release());
4794 4963
svillar 2014/02/07 12:02:49 So instead of doing this, I think it's better to p
4795 parserValueList->next(); 4964 parserValueList->next();
4796 } 4965 }
4797 4966
4798 bool BisonCSSParser::parseGridTrackList(CSSPropertyID propId, bool important) 4967 bool BisonCSSParser::parseGridTrackList(CSSPropertyID propId, bool important)
4799 { 4968 {
4800 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4969 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
4801 4970
4802 CSSParserValue* value = m_valueList->current(); 4971 CSSParserValue* value = m_valueList->current();
4803 if (value->id == CSSValueNone) { 4972 if (value->id == CSSValueNone) {
4804 if (m_valueList->next()) 4973 if (m_valueList->next())
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
4928 5097
4929 return cssValuePool().createValue(flexValue, CSSPrimitiveValue::CSS_FR); 5098 return cssValuePool().createValue(flexValue, CSSPrimitiveValue::CSS_FR);
4930 } 5099 }
4931 5100
4932 if (!validUnit(currentValue, FNonNeg | FLength | FPercent)) 5101 if (!validUnit(currentValue, FNonNeg | FLength | FPercent))
4933 return 0; 5102 return 0;
4934 5103
4935 return createPrimitiveNumericValue(currentValue); 5104 return createPrimitiveNumericValue(currentValue);
4936 } 5105 }
4937 5106
5107 bool BisonCSSParser::parseGridTemplateAreasRow(NamedGridAreaMap& gridAreaMap, co nst size_t rowCount, size_t& columnCount)
5108 {
5109 CSSParserValue* currentValue = m_valueList->current();
5110 if (!currentValue || currentValue->unit != CSSPrimitiveValue::CSS_STRING)
5111 return false;
5112
5113 String gridRowNames = currentValue->string;
5114 if (!gridRowNames.length())
5115 return false;
5116
5117 Vector<String> columnNames;
5118 gridRowNames.split(' ', columnNames);
5119
5120 if (!columnCount) {
5121 columnCount = columnNames.size();
5122 ASSERT(columnCount);
5123 } else if (columnCount != columnNames.size()) {
5124 // The declaration is invalid is all the rows don't have the number of c olumns.
5125 return false;
5126 }
5127
5128 for (size_t currentCol = 0; currentCol < columnCount; ++currentCol) {
5129 const String& gridAreaName = columnNames[currentCol];
5130
5131 // Unamed areas are always valid (we consider them to be 1x1).
5132 if (gridAreaName == ".")
5133 continue;
5134
5135 // We handle several grid areas with the same name at once to simplify t he validation code.
5136 size_t lookAheadCol;
5137 for (lookAheadCol = currentCol; lookAheadCol < (columnCount - 1); ++look AheadCol) {
5138 if (columnNames[lookAheadCol + 1] != gridAreaName)
5139 break;
5140 }
5141
5142 NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
5143 if (gridAreaIt == gridAreaMap.end()) {
5144 gridAreaMap.add(gridAreaName, GridCoordinate(GridSpan(rowCount, rowC ount), GridSpan(currentCol, lookAheadCol)));
5145 } else {
5146 GridCoordinate& gridCoordinate = gridAreaIt->value;
5147
5148 // The following checks test that the grid area is a single filled-i n rectangle.
5149 // 1. The new row is adjacent to the previously parsed row.
5150 if (rowCount != gridCoordinate.rows.initialPositionIndex + 1)
5151 return false;
5152
5153 // 2. The new area starts at the same position as the previously par sed area.
5154 if (currentCol != gridCoordinate.columns.initialPositionIndex)
5155 return false;
5156
5157 // 3. The new area ends at the same position as the previously parse d area.
5158 if (lookAheadCol != gridCoordinate.columns.finalPositionIndex)
5159 return false;
5160
5161 ++gridCoordinate.rows.finalPositionIndex;
5162 }
5163 currentCol = lookAheadCol;
5164 }
5165
5166 m_valueList->next();
5167 return true;
5168 }
5169
4938 PassRefPtr<CSSValue> BisonCSSParser::parseGridTemplateAreas() 5170 PassRefPtr<CSSValue> BisonCSSParser::parseGridTemplateAreas()
4939 { 5171 {
4940 NamedGridAreaMap gridAreaMap; 5172 NamedGridAreaMap gridAreaMap;
4941 size_t rowCount = 0; 5173 size_t rowCount = 0;
4942 size_t columnCount = 0; 5174 size_t columnCount = 0;
4943 5175
4944 while (CSSParserValue* currentValue = m_valueList->current()) { 5176 while (m_valueList->current()) {
4945 if (currentValue->unit != CSSPrimitiveValue::CSS_STRING) 5177 if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount))
4946 return 0; 5178 return 0;
4947
4948 String gridRowNames = currentValue->string;
4949 if (!gridRowNames.length())
4950 return 0;
4951
4952 Vector<String> columnNames;
4953 gridRowNames.split(' ', columnNames);
4954
4955 if (!columnCount) {
4956 columnCount = columnNames.size();
4957 ASSERT(columnCount);
4958 } else if (columnCount != columnNames.size()) {
4959 // The declaration is invalid is all the rows don't have the number of columns.
4960 return 0;
4961 }
4962
4963 for (size_t currentCol = 0; currentCol < columnCount; ++currentCol) {
4964 const String& gridAreaName = columnNames[currentCol];
4965
4966 // Unamed areas are always valid (we consider them to be 1x1).
4967 if (gridAreaName == ".")
4968 continue;
4969
4970 // We handle several grid areas with the same name at once to simpli fy the validation code.
4971 size_t lookAheadCol;
4972 for (lookAheadCol = currentCol; lookAheadCol < (columnCount - 1); ++ lookAheadCol) {
4973 if (columnNames[lookAheadCol + 1] != gridAreaName)
4974 break;
4975 }
4976
4977 NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaNam e);
4978 if (gridAreaIt == gridAreaMap.end()) {
4979 gridAreaMap.add(gridAreaName, GridCoordinate(GridSpan(rowCount, rowCount), GridSpan(currentCol, lookAheadCol)));
4980 } else {
4981 GridCoordinate& gridCoordinate = gridAreaIt->value;
4982
4983 // The following checks test that the grid area is a single fill ed-in rectangle.
4984 // 1. The new row is adjacent to the previously parsed row.
4985 if (rowCount != gridCoordinate.rows.initialPositionIndex + 1)
4986 return 0;
4987
4988 // 2. The new area starts at the same position as the previously parsed area.
4989 if (currentCol != gridCoordinate.columns.initialPositionIndex)
4990 return 0;
4991
4992 // 3. The new area ends at the same position as the previously p arsed area.
4993 if (lookAheadCol != gridCoordinate.columns.finalPositionIndex)
4994 return 0;
4995
4996 ++gridCoordinate.rows.finalPositionIndex;
4997 }
4998 currentCol = lookAheadCol;
4999 }
5000
5001 ++rowCount; 5179 ++rowCount;
5002 m_valueList->next();
5003 } 5180 }
5004 5181
5005 if (!rowCount || !columnCount) 5182 if (!rowCount || !columnCount)
5006 return 0; 5183 return 0;
5007 5184
5008 return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount) ; 5185 return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount) ;
5009 } 5186 }
5010 5187
5011 PassRefPtr<CSSValue> BisonCSSParser::parseCounterContent(CSSParserValueList* arg s, bool counters) 5188 PassRefPtr<CSSValue> BisonCSSParser::parseCounterContent(CSSParserValueList* arg s, bool counters)
5012 { 5189 {
(...skipping 5281 matching lines...) Expand 10 before | Expand all | Expand 10 after
10294 { 10471 {
10295 // The tokenizer checks for the construct of an+b. 10472 // The tokenizer checks for the construct of an+b.
10296 // However, since the {ident} rule precedes the {nth} rule, some of those 10473 // However, since the {ident} rule precedes the {nth} rule, some of those
10297 // tokens are identified as string literal. Furthermore we need to accept 10474 // tokens are identified as string literal. Furthermore we need to accept
10298 // "odd" and "even" which does not match to an+b. 10475 // "odd" and "even" which does not match to an+b.
10299 return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even") 10476 return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even")
10300 || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n"); 10477 || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n");
10301 } 10478 }
10302 10479
10303 } 10480 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698