1 // Copyright 2016 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/inspector/search-util.h" 6 7 #include "src/inspector/protocol/Protocol.h" 8 #include "src/inspector/v8-inspector-impl.h" 9 #include "src/inspector/v8-inspector-session-impl.h" 10 #include "src/inspector/v8-regex.h" 11 12 namespace v8_inspector { 13 14 namespace { 15 16 String16 findMagicComment(const String16& content, const String16& name, 17 bool multiline) { 18 DCHECK(name.find("=") == String16::kNotFound); 19 size_t length = content.length(); 20 size_t nameLength = name.length(); 21 22 size_t pos = length; 23 size_t equalSignPos = 0; 24 size_t closingCommentPos = 0; 25 while (true) { 26 pos = content.reverseFind(name, pos); 27 if (pos == String16::kNotFound) return String16(); 28 29 // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name. 30 if (pos < 4) return String16(); 31 pos -= 4; 32 if (content[pos] != '/') continue; 33 if ((content[pos + 1] != '/' || multiline) && 34 (content[pos + 1] != '*' || !multiline)) 35 continue; 36 if (content[pos + 2] != '#' && content[pos + 2] != '@') continue; 37 if (content[pos + 3] != ' ' && content[pos + 3] != '\t') continue; 38 equalSignPos = pos + 4 + nameLength; 39 if (equalSignPos < length && content[equalSignPos] != '=') continue; 40 if (multiline) { 41 closingCommentPos = content.find("*/", equalSignPos + 1); 42 if (closingCommentPos == String16::kNotFound) return String16(); 43 } 44 45 break; 46 } 47 48 DCHECK(equalSignPos); 49 DCHECK(!multiline || closingCommentPos); 50 size_t urlPos = equalSignPos + 1; 51 String16 match = multiline 52 ? content.substring(urlPos, closingCommentPos - urlPos) 53 : content.substring(urlPos); 54 55 size_t newLine = match.find("\n"); 56 if (newLine != String16::kNotFound) match = match.substring(0, newLine); 57 match = match.stripWhiteSpace(); 58 59 for (size_t i = 0; i < match.length(); ++i) { 60 UChar c = match[i]; 61 if (c == '"' || c == '\'' || c == ' ' || c == '\t') return ""; 62 } 63 64 return match; 65 } 66 67 String16 createSearchRegexSource(const String16& text) { 68 String16Builder result; 69 70 for (size_t i = 0; i < text.length(); i++) { 71 UChar c = text[i]; 72 if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || 73 c == '+' || c == '-' || c == '*' || c == '.' || c == ',' || c == '?' || 74 c == '\\' || c == '^' || c == '$' || c == '|') { 75 result.append('\\'); 76 } 77 result.append(c); 78 } 79 80 return result.toString(); 81 } 82 83 std::unique_ptr<std::vector<size_t>> lineEndings(const String16& text) { 84 std::unique_ptr<std::vector<size_t>> result(new std::vector<size_t>()); 85 86 const String16 lineEndString = "\n"; 87 size_t start = 0; 88 while (start < text.length()) { 89 size_t lineEnd = text.find(lineEndString, start); 90 if (lineEnd == String16::kNotFound) break; 91 92 result->push_back(lineEnd); 93 start = lineEnd + 1; 94 } 95 result->push_back(text.length()); 96 97 return result; 98 } 99 100 std::vector<std::pair<int, String16>> scriptRegexpMatchesByLines( 101 const V8Regex& regex, const String16& text) { 102 std::vector<std::pair<int, String16>> result; 103 if (text.isEmpty()) return result; 104 105 std::unique_ptr<std::vector<size_t>> endings(lineEndings(text)); 106 size_t size = endings->size(); 107 size_t start = 0; 108 for (size_t lineNumber = 0; lineNumber < size; ++lineNumber) { 109 size_t lineEnd = endings->at(lineNumber); 110 String16 line = text.substring(start, lineEnd - start); 111 if (line.length() && line[line.length() - 1] == '\r') 112 line = line.substring(0, line.length() - 1); 113 114 int matchLength; 115 if (regex.match(line, 0, &matchLength) != -1) 116 result.push_back(std::pair<int, String16>(lineNumber, line)); 117 118 start = lineEnd + 1; 119 } 120 return result; 121 } 122 123 std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch( 124 int lineNumber, const String16& lineContent) { 125 return protocol::Debugger::SearchMatch::create() 126 .setLineNumber(lineNumber) 127 .setLineContent(lineContent) 128 .build(); 129 } 130 131 std::unique_ptr<V8Regex> createSearchRegex(V8InspectorImpl* inspector, 132 const String16& query, 133 bool caseSensitive, bool isRegex) { 134 String16 regexSource = isRegex ? query : createSearchRegexSource(query); 135 return wrapUnique(new V8Regex(inspector, regexSource, caseSensitive)); 136 } 137 138 } // namespace 139 140 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> 141 searchInTextByLinesImpl(V8InspectorSession* session, const String16& text, 142 const String16& query, const bool caseSensitive, 143 const bool isRegex) { 144 std::unique_ptr<V8Regex> regex = createSearchRegex( 145 static_cast<V8InspectorSessionImpl*>(session)->inspector(), query, 146 caseSensitive, isRegex); 147 std::vector<std::pair<int, String16>> matches = 148 scriptRegexpMatchesByLines(*regex.get(), text); 149 150 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> result; 151 for (const auto& match : matches) 152 result.push_back(buildObjectForSearchMatch(match.first, match.second)); 153 return result; 154 } 155 156 String16 findSourceURL(const String16& content, bool multiline) { 157 return findMagicComment(content, "sourceURL", multiline); 158 } 159 160 String16 findSourceMapURL(const String16& content, bool multiline) { 161 return findMagicComment(content, "sourceMappingURL", multiline); 162 } 163 164 } // namespace v8_inspector 165