1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if ENABLE(INDEXED_DATABASE)
30#include "IDBKeyPath.h"
31
32#include <wtf/ASCIICType.h>
33#include <wtf/dtoa.h>
34#include <wtf/text/StringBuilder.h>
35
36namespace WebCore {
37
38class IDBKeyPathLexer {
39public:
40 enum TokenType {
41 TokenIdentifier,
42 TokenDot,
43 TokenEnd,
44 TokenError
45 };
46
47 explicit IDBKeyPathLexer(const String& s)
48 : m_string(s)
49 , m_remainingText(s)
50 , m_currentTokenType(TokenError)
51 {
52 }
53
54 TokenType currentTokenType() const { return m_currentTokenType; }
55
56 TokenType nextTokenType()
57 {
58 m_currentTokenType = lex(m_currentElement);
59 return m_currentTokenType;
60 }
61
62 const String& currentElement() { return m_currentElement; }
63
64private:
65 TokenType lex(String&);
66 TokenType lexIdentifier(String&);
67
68 String m_currentElement;
69 const String m_string;
70 StringView m_remainingText;
71 TokenType m_currentTokenType;
72};
73
74IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element)
75{
76 if (m_remainingText.isEmpty())
77 return TokenEnd;
78
79 if (m_remainingText[0] == '.') {
80 m_remainingText = m_remainingText.substring(1);
81 return TokenDot;
82 }
83
84 return lexIdentifier(element);
85}
86
87namespace {
88
89// The following correspond to grammar in ECMA-262.
90const uint32_t unicodeLetter = U_GC_L_MASK | U_GC_NL_MASK;
91const uint32_t unicodeCombiningMark = U_GC_MN_MASK | U_GC_MC_MASK;
92const uint32_t unicodeDigit = U_GC_ND_MASK;
93const uint32_t unicodeConnectorPunctuation = U_GC_PC_MASK;
94const UChar ZWNJ = 0x200C;
95const UChar ZWJ = 0x200D;
96
97static inline bool isIdentifierStartCharacter(UChar c)
98{
99 return (U_GET_GC_MASK(c) & unicodeLetter) || (c == '$') || (c == '_');
100}
101
102static inline bool isIdentifierCharacter(UChar c)
103{
104 return (U_GET_GC_MASK(c) & (unicodeLetter | unicodeCombiningMark | unicodeDigit | unicodeConnectorPunctuation)) || (c == '$') || (c == '_') || (c == ZWNJ) || (c == ZWJ);
105}
106
107} // namespace
108
109IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element)
110{
111 StringView start = m_remainingText;
112 if (!m_remainingText.isEmpty() && isIdentifierStartCharacter(m_remainingText[0]))
113 m_remainingText = m_remainingText.substring(1);
114 else
115 return TokenError;
116
117 while (!m_remainingText.isEmpty() && isIdentifierCharacter(m_remainingText[0]))
118 m_remainingText = m_remainingText.substring(1);
119
120 element = start.substring(0, start.length() - m_remainingText.length()).toString();
121 return TokenIdentifier;
122}
123
124static bool IDBIsValidKeyPath(const String& keyPath)
125{
126 IDBKeyPathParseError error;
127 Vector<String> keyPathElements;
128 IDBParseKeyPath(keyPath, keyPathElements, error);
129 return error == IDBKeyPathParseError::None;
130}
131
132void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPathParseError& error)
133{
134 // IDBKeyPath ::= EMPTY_STRING | identifier ('.' identifier)*
135 // The basic state machine is:
136 // Start => {Identifier, End}
137 // Identifier => {Dot, End}
138 // Dot => {Identifier}
139 // It bails out as soon as it finds an error, but doesn't discard the bits it managed to parse.
140 enum ParserState { Identifier, Dot, End };
141
142 IDBKeyPathLexer lexer(keyPath);
143 IDBKeyPathLexer::TokenType tokenType = lexer.nextTokenType();
144 ParserState state;
145 if (tokenType == IDBKeyPathLexer::TokenIdentifier)
146 state = Identifier;
147 else if (tokenType == IDBKeyPathLexer::TokenEnd)
148 state = End;
149 else {
150 error = IDBKeyPathParseError::Start;
151 return;
152 }
153
154 while (1) {
155 switch (state) {
156 case Identifier : {
157 IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
158 ASSERT(tokenType == IDBKeyPathLexer::TokenIdentifier);
159
160 String element = lexer.currentElement();
161 elements.append(element);
162
163 tokenType = lexer.nextTokenType();
164 if (tokenType == IDBKeyPathLexer::TokenDot)
165 state = Dot;
166 else if (tokenType == IDBKeyPathLexer::TokenEnd)
167 state = End;
168 else {
169 error = IDBKeyPathParseError::Identifier;
170 return;
171 }
172 break;
173 }
174 case Dot: {
175 IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
176 ASSERT(tokenType == IDBKeyPathLexer::TokenDot);
177
178 tokenType = lexer.nextTokenType();
179 if (tokenType == IDBKeyPathLexer::TokenIdentifier)
180 state = Identifier;
181 else {
182 error = IDBKeyPathParseError::Dot;
183 return;
184 }
185 break;
186 }
187 case End: {
188 error = IDBKeyPathParseError::None;
189 return;
190 }
191 }
192 }
193}
194
195bool isIDBKeyPathValid(const IDBKeyPath& keyPath)
196{
197 auto visitor = WTF::makeVisitor([](const String& string) {
198 return IDBIsValidKeyPath(string);
199 }, [](const Vector<String>& vector) {
200 if (vector.isEmpty())
201 return false;
202 for (auto& key : vector) {
203 if (!IDBIsValidKeyPath(key))
204 return false;
205 }
206 return true;
207 });
208 return WTF::visit(visitor, keyPath);
209}
210
211IDBKeyPath isolatedCopy(const IDBKeyPath& keyPath)
212{
213 auto visitor = WTF::makeVisitor([](const String& string) -> IDBKeyPath {
214 return string.isolatedCopy();
215 }, [](const Vector<String>& vector) -> IDBKeyPath {
216 Vector<String> vectorCopy;
217 vectorCopy.reserveInitialCapacity(vector.size());
218 for (auto& string : vector)
219 vectorCopy.uncheckedAppend(string.isolatedCopy());
220 return vectorCopy;
221 });
222
223 return WTF::visit(visitor, keyPath);
224}
225
226#if !LOG_DISABLED
227String loggingString(const IDBKeyPath& path)
228{
229 auto visitor = WTF::makeVisitor([](const String& string) {
230 return makeString("< ", string, " >");
231 }, [](const Vector<String>& strings) {
232 if (strings.isEmpty())
233 return "< >"_str;
234
235 StringBuilder builder;
236 builder.append("< ");
237 for (size_t i = 0; i < strings.size() - 1; ++i) {
238 builder.append(strings[i]);
239 builder.append(", ");
240 }
241 builder.append(strings.last());
242 builder.append(" >");
243
244 return builder.toString();
245 });
246
247 return WTF::visit(visitor, path);
248}
249#endif
250
251} // namespace WebCore
252
253#endif // ENABLE(INDEXED_DATABASE)
254