1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/dom/CSSLexer.h"
7 #include "js/Value.h"
8 #include "mozilla/dom/CSSLexerBinding.h"
9 #include "mozilla/dom/ToJSValue.h"
10 
11 namespace mozilla {
12 namespace dom {
13 
14 // Ensure that constants are consistent.
15 
16 #define CHECK(X, Y) \
17   static_assert(static_cast<int>(X) == static_cast<int>(Y),       \
18                 "nsCSSToken and CSSTokenType should have identical values")
19 
20 CHECK(eCSSToken_Whitespace, CSSTokenType::Whitespace);
21 CHECK(eCSSToken_Comment, CSSTokenType::Comment);
22 CHECK(eCSSToken_Ident, CSSTokenType::Ident);
23 CHECK(eCSSToken_Function, CSSTokenType::Function);
24 CHECK(eCSSToken_AtKeyword, CSSTokenType::At);
25 CHECK(eCSSToken_ID, CSSTokenType::Id);
26 CHECK(eCSSToken_Hash, CSSTokenType::Hash);
27 CHECK(eCSSToken_Number, CSSTokenType::Number);
28 CHECK(eCSSToken_Dimension, CSSTokenType::Dimension);
29 CHECK(eCSSToken_Percentage, CSSTokenType::Percentage);
30 CHECK(eCSSToken_String, CSSTokenType::String);
31 CHECK(eCSSToken_Bad_String, CSSTokenType::Bad_string);
32 CHECK(eCSSToken_URL, CSSTokenType::Url);
33 CHECK(eCSSToken_Bad_URL, CSSTokenType::Bad_url);
34 CHECK(eCSSToken_Symbol, CSSTokenType::Symbol);
35 CHECK(eCSSToken_Includes, CSSTokenType::Includes);
36 CHECK(eCSSToken_Dashmatch, CSSTokenType::Dashmatch);
37 CHECK(eCSSToken_Beginsmatch, CSSTokenType::Beginsmatch);
38 CHECK(eCSSToken_Endsmatch, CSSTokenType::Endsmatch);
39 CHECK(eCSSToken_Containsmatch, CSSTokenType::Containsmatch);
40 CHECK(eCSSToken_URange, CSSTokenType::Urange);
41 CHECK(eCSSToken_HTMLComment, CSSTokenType::Htmlcomment);
42 
43 #undef CHECK
44 
CSSLexer(const nsAString & aText)45 CSSLexer::CSSLexer(const nsAString& aText)
46   : mInput(aText)
47   , mScanner(mInput, 1)
48 {
49 }
50 
~CSSLexer()51 CSSLexer::~CSSLexer()
52 {
53 }
54 
55 bool
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto,JS::MutableHandle<JSObject * > aReflector)56 CSSLexer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
57                      JS::MutableHandle<JSObject*> aReflector)
58 {
59   return CSSLexerBinding::Wrap(aCx, this, aGivenProto, aReflector);
60 }
61 
62 uint32_t
LineNumber()63 CSSLexer::LineNumber()
64 {
65   // The scanner uses 1-based line numbers, but our callers expect
66   // 0-based.
67   return mScanner.GetLineNumber() - 1;
68 }
69 
70 uint32_t
ColumnNumber()71 CSSLexer::ColumnNumber()
72 {
73   return mScanner.GetColumnNumber();
74 }
75 
76 void
PerformEOFFixup(const nsAString & aInputString,bool aPreserveBackslash,nsAString & aResult)77 CSSLexer::PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash,
78                           nsAString& aResult)
79 {
80   aResult.Append(aInputString);
81   uint32_t eofChars = mScanner.GetEOFCharacters();
82 
83   if (aPreserveBackslash &&
84       (eofChars & (nsCSSScanner::eEOFCharacters_DropBackslash |
85                    nsCSSScanner::eEOFCharacters_ReplacementChar)) != 0) {
86     eofChars &= ~(nsCSSScanner::eEOFCharacters_DropBackslash |
87                   nsCSSScanner::eEOFCharacters_ReplacementChar);
88     aResult.Append('\\');
89   }
90 
91   if ((eofChars & nsCSSScanner::eEOFCharacters_DropBackslash) != 0 &&
92       aResult.Length() > 0 && aResult.Last() == '\\') {
93     aResult.Truncate(aResult.Length() - 1);
94   }
95 
96   nsCSSScanner::AppendImpliedEOFCharacters(nsCSSScanner::EOFCharacters(eofChars),
97                                            aResult);
98 }
99 
100 void
NextToken(Nullable<CSSToken> & aResult)101 CSSLexer::NextToken(Nullable<CSSToken>& aResult)
102 {
103   nsCSSToken token;
104   if (!mScanner.Next(token, eCSSScannerExclude_None)) {
105     return;
106   }
107 
108   CSSToken& resultToken(aResult.SetValue());
109 
110   resultToken.mTokenType = static_cast<CSSTokenType>(token.mType);
111   resultToken.mStartOffset = mScanner.GetTokenOffset();
112   resultToken.mEndOffset = mScanner.GetTokenEndOffset();
113 
114   switch (token.mType) {
115     case eCSSToken_Whitespace:
116       break;
117 
118     case eCSSToken_Ident:
119     case eCSSToken_Function:
120     case eCSSToken_AtKeyword:
121     case eCSSToken_ID:
122     case eCSSToken_Hash:
123       resultToken.mText.Construct(token.mIdent);
124       break;
125 
126     case eCSSToken_Dimension:
127       resultToken.mText.Construct(token.mIdent);
128       MOZ_FALLTHROUGH;
129     case eCSSToken_Number:
130     case eCSSToken_Percentage:
131       resultToken.mNumber.Construct(token.mNumber);
132       resultToken.mHasSign.Construct(token.mHasSign);
133       resultToken.mIsInteger.Construct(token.mIntegerValid);
134       break;
135 
136     case eCSSToken_String:
137     case eCSSToken_Bad_String:
138     case eCSSToken_URL:
139     case eCSSToken_Bad_URL:
140       resultToken.mText.Construct(token.mIdent);
141       /* Don't bother emitting the delimiter, as it is readily extracted
142          from the source string when needed.  */
143       break;
144 
145     case eCSSToken_Symbol:
146       resultToken.mText.Construct(nsString(&token.mSymbol, 1));
147       break;
148 
149     case eCSSToken_Includes:
150     case eCSSToken_Dashmatch:
151     case eCSSToken_Beginsmatch:
152     case eCSSToken_Endsmatch:
153     case eCSSToken_Containsmatch:
154     case eCSSToken_URange:
155       break;
156 
157     case eCSSToken_Comment:
158     case eCSSToken_HTMLComment:
159       /* The comment text is easily extracted from the source string,
160          and is rarely useful.  */
161       break;
162   }
163 }
164 
165 } // namespace dom
166 } // namespace mozilla
167