Fix some minor problems in the StringImpl header
[WebKit-https.git] / Source / JavaScriptCore / inspector / ContentSearchUtilities.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "ContentSearchUtilities.h"
31
32 #include "InspectorValues.h"
33 #include "RegularExpression.h"
34 #include "Yarr.h"
35 #include "YarrInterpreter.h"
36 #include <wtf/BumpPointerAllocator.h>
37 #include <wtf/StdLibExtras.h>
38 #include <wtf/text/StringBuilder.h>
39
40 using namespace JSC::Yarr;
41
42 namespace Inspector {
43 namespace ContentSearchUtilities {
44
45 static const char regexSpecialCharacters[] = "[](){}+-*.,?\\^$|";
46
47 static String createSearchRegexSource(const String& text)
48 {
49     StringBuilder result;
50
51     for (unsigned i = 0; i < text.length(); i++) {
52         UChar character = text[i];
53         if (isASCII(character) && strchr(regexSpecialCharacters, character))
54             result.append('\\');
55         result.append(character);
56     }
57
58     return result.toString();
59 }
60
61 static inline size_t sizetExtractor(const size_t* value)
62 {
63     return *value;
64 }
65
66 TextPosition textPositionFromOffset(size_t offset, const Vector<size_t>& lineEndings)
67 {
68     const size_t* foundNextStart = approximateBinarySearch<size_t, size_t>(lineEndings, lineEndings.size(), offset, sizetExtractor);
69     size_t lineIndex = foundNextStart - &lineEndings.at(0);
70     if (offset >= *foundNextStart)
71         ++lineIndex;
72     size_t lineStartOffset = lineIndex > 0 ? lineEndings.at(lineIndex - 1) : 0;
73     size_t column = offset - lineStartOffset;
74     return TextPosition(OrdinalNumber::fromZeroBasedInt(lineIndex), OrdinalNumber::fromZeroBasedInt(column));
75 }
76
77 static Vector<std::pair<size_t, String>> getRegularExpressionMatchesByLines(const RegularExpression& regex, const String& text)
78 {
79     Vector<std::pair<size_t, String>> result;
80     if (text.isEmpty())
81         return result;
82
83     std::unique_ptr<Vector<size_t>> endings(lineEndings(text));
84     size_t size = endings->size();
85     size_t start = 0;
86
87     for (size_t lineNumber = 0; lineNumber < size; ++lineNumber) {
88         size_t nextStart = endings->at(lineNumber);
89         String line = text.substring(start, nextStart - start);
90
91         int matchLength;
92         if (regex.match(line, 0, &matchLength) != -1)
93             result.append(std::pair<size_t, String>(lineNumber, line));
94
95         start = nextStart;
96     }
97
98     return result;
99 }
100
101 std::unique_ptr<Vector<size_t>> lineEndings(const String& text)
102 {
103     auto result = std::make_unique<Vector<size_t>>();
104
105     size_t start = 0;
106     while (start < text.length()) {
107         size_t nextStart = text.find('\n', start);
108         if (nextStart == notFound || nextStart == (text.length() - 1)) {
109             result->append(text.length());
110             break;
111         }
112
113         nextStart += 1;
114         result->append(nextStart);
115         start = nextStart;
116     }
117
118     result->append(text.length());
119
120     return result;
121 }
122
123 static Ref<Inspector::Protocol::GenericTypes::SearchMatch> buildObjectForSearchMatch(size_t lineNumber, const String& lineContent)
124 {
125     return Inspector::Protocol::GenericTypes::SearchMatch::create()
126         .setLineNumber(lineNumber)
127         .setLineContent(lineContent)
128         .release();
129 }
130
131 RegularExpression createSearchRegex(const String& query, bool caseSensitive, bool isRegex)
132 {
133     return RegularExpression { isRegex ? query : createSearchRegexSource(query), caseSensitive ? TextCaseSensitive : TextCaseInsensitive };
134 }
135
136 int countRegularExpressionMatches(const RegularExpression& regex, const String& content)
137 {
138     if (content.isEmpty())
139         return 0;
140
141     int result = 0;
142     int position;
143     unsigned start = 0;
144     int matchLength;
145     while ((position = regex.match(content, start, &matchLength)) != -1) {
146         if (start >= content.length())
147             break;
148         if (matchLength > 0)
149             ++result;
150         start = position + 1;
151     }
152     return result;
153 }
154
155 Ref<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>> searchInTextByLines(const String& text, const String& query, const bool caseSensitive, const bool isRegex)
156 {
157     Ref<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>> result = Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>::create();
158
159     RegularExpression regex = ContentSearchUtilities::createSearchRegex(query, caseSensitive, isRegex);
160     Vector<std::pair<size_t, String>> matches = getRegularExpressionMatchesByLines(regex, text);
161
162     for (const auto& match : matches) {
163         Ref<Inspector::Protocol::GenericTypes::SearchMatch> matchObject = buildObjectForSearchMatch(match.first, match.second);
164         result->addItem(WTFMove(matchObject));
165     }
166
167     return result;
168 }
169
170 static String stylesheetCommentPattern(const String& name)
171 {
172     // "/*# <name>=<value> */" and deprecated "/*@"
173     return "/\\*[#@][\040\t]" + name + "=[\040\t]*([^\\s\'\"]*)[\040\t]*\\*/";
174 }
175
176 static String findMagicComment(const String& content, const String& patternString)
177 {
178     ASSERT(!content.isNull());
179     const char* error = nullptr;
180     YarrPattern pattern(patternString, JSC::RegExpFlags::FlagMultiline, &error);
181     ASSERT(!error);
182     BumpPointerAllocator regexAllocator;
183     auto bytecodePattern = byteCompile(pattern, &regexAllocator);
184     ASSERT(bytecodePattern);
185
186     ASSERT(pattern.m_numSubpatterns == 1);
187     Vector<int, 4> matches;
188     matches.grow(4);
189     unsigned result = interpret(bytecodePattern.get(), content, 0, reinterpret_cast<unsigned*>(matches.data()));
190     if (result == offsetNoMatch)
191         return String();
192
193     ASSERT(matches[2] > 0 && matches[3] > 0);
194     return content.substring(matches[2], matches[3] - matches[2]);
195 }
196
197 String findStylesheetSourceMapURL(const String& content)
198 {
199     return findMagicComment(content, stylesheetCommentPattern(ASCIILiteral("sourceMappingURL")));
200 }
201
202 } // namespace ContentSearchUtilities
203 } // namespace Inspector