Home | History | Annotate | Download | only in inspector
      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 
     31 #include "core/inspector/ContentSearchUtils.h"
     32 
     33 #include "bindings/core/v8/ScriptRegexp.h"
     34 #include "wtf/Vector.h"
     35 #include "wtf/text/StringBuilder.h"
     36 
     37 namespace blink {
     38 namespace ContentSearchUtils {
     39 
     40 namespace {
     41 // This should be kept the same as the one in front-end/utilities.js
     42 static const char regexSpecialCharacters[] = "[](){}+-*.,?\\^$|";
     43 }
     44 
     45 static String createSearchRegexSource(const String& text)
     46 {
     47     StringBuilder result;
     48     String specials(regexSpecialCharacters);
     49 
     50     for (unsigned i = 0; i < text.length(); i++) {
     51         if (specials.find(text[i]) != kNotFound)
     52             result.append('\\');
     53         result.append(text[i]);
     54     }
     55 
     56     return result.toString();
     57 }
     58 
     59 static Vector<pair<int, String> > getScriptRegexpMatchesByLines(const ScriptRegexp* regex, const String& text)
     60 {
     61     Vector<pair<int, String> > result;
     62     if (text.isEmpty())
     63         return result;
     64 
     65     OwnPtr<Vector<unsigned> > endings(lineEndings(text));
     66     unsigned size = endings->size();
     67     unsigned start = 0;
     68     for (unsigned lineNumber = 0; lineNumber < size; ++lineNumber) {
     69         unsigned lineEnd = endings->at(lineNumber);
     70         String line = text.substring(start, lineEnd - start);
     71         if (line.endsWith('\r'))
     72             line = line.left(line.length() - 1);
     73 
     74         int matchLength;
     75         if (regex->match(line, 0, &matchLength) != -1)
     76             result.append(pair<int, String>(lineNumber, line));
     77 
     78         start = lineEnd + 1;
     79     }
     80     return result;
     81 }
     82 
     83 static PassRefPtr<TypeBuilder::Page::SearchMatch> buildObjectForSearchMatch(int lineNumber, const String& lineContent)
     84 {
     85     return TypeBuilder::Page::SearchMatch::create()
     86         .setLineNumber(lineNumber)
     87         .setLineContent(lineContent)
     88         .release();
     89 }
     90 
     91 PassOwnPtr<ScriptRegexp> createSearchRegex(const String& query, bool caseSensitive, bool isRegex)
     92 {
     93     String regexSource = isRegex ? query : createSearchRegexSource(query);
     94     return adoptPtr(new ScriptRegexp(regexSource, caseSensitive ? TextCaseSensitive : TextCaseInsensitive));
     95 }
     96 
     97 PassRefPtr<TypeBuilder::Array<TypeBuilder::Page::SearchMatch> > searchInTextByLines(const String& text, const String& query, const bool caseSensitive, const bool isRegex)
     98 {
     99     RefPtr<TypeBuilder::Array<TypeBuilder::Page::SearchMatch> > result = TypeBuilder::Array<TypeBuilder::Page::SearchMatch>::create();
    100 
    101     OwnPtr<ScriptRegexp> regex = ContentSearchUtils::createSearchRegex(query, caseSensitive, isRegex);
    102     Vector<pair<int, String> > matches = getScriptRegexpMatchesByLines(regex.get(), text);
    103 
    104     for (Vector<pair<int, String> >::const_iterator it = matches.begin(); it != matches.end(); ++it)
    105         result->addItem(buildObjectForSearchMatch(it->first, it->second));
    106 
    107     return result;
    108 }
    109 
    110 static String findMagicComment(const String& content, const String& name, MagicCommentType commentType, bool* deprecated = 0)
    111 {
    112     ASSERT(name.find("=") == kNotFound);
    113     if (deprecated)
    114         *deprecated = false;
    115 
    116     unsigned length = content.length();
    117     unsigned nameLength = name.length();
    118 
    119     size_t pos = length;
    120     size_t equalSignPos = 0;
    121     size_t closingCommentPos = 0;
    122     while (true) {
    123         pos = content.reverseFind(name, pos);
    124         if (pos == kNotFound)
    125             return String();
    126 
    127         // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name.
    128         if (pos < 4)
    129             return String();
    130         pos -= 4;
    131         if (content[pos] != '/')
    132             continue;
    133         if ((content[pos + 1] != '/' || commentType != JavaScriptMagicComment)
    134             && (content[pos + 1] != '*' || commentType != CSSMagicComment))
    135             continue;
    136         if (content[pos + 2] != '#' && content[pos + 2] != '@')
    137             continue;
    138         if (content[pos + 3] != ' ' && content[pos + 3] != '\t')
    139             continue;
    140         equalSignPos = pos + 4 + nameLength;
    141         if (equalSignPos < length && content[equalSignPos] != '=')
    142             continue;
    143         if (commentType == CSSMagicComment) {
    144             closingCommentPos = content.find("*/", equalSignPos + 1);
    145             if (closingCommentPos == kNotFound)
    146                 return String();
    147         }
    148 
    149         break;
    150     }
    151 
    152     if (deprecated && content[pos + 2] == '@')
    153         *deprecated = true;
    154 
    155     ASSERT(equalSignPos);
    156     ASSERT(commentType != CSSMagicComment || closingCommentPos);
    157     size_t urlPos = equalSignPos + 1;
    158     String match = commentType == CSSMagicComment
    159         ? content.substring(urlPos, closingCommentPos - urlPos)
    160         : content.substring(urlPos);
    161 
    162     size_t newLine = match.find("\n");
    163     if (newLine != kNotFound)
    164         match = match.substring(0, newLine);
    165     match = match.stripWhiteSpace();
    166 
    167     String disallowedChars("\"' \t");
    168     for (unsigned i = 0; i < match.length(); ++i) {
    169         if (disallowedChars.find(match[i]) != kNotFound)
    170             return "";
    171     }
    172 
    173     return match;
    174 }
    175 
    176 String findSourceURL(const String& content, MagicCommentType commentType, bool* deprecated)
    177 {
    178     return findMagicComment(content, "sourceURL", commentType, deprecated);
    179 }
    180 
    181 String findSourceMapURL(const String& content, MagicCommentType commentType, bool* deprecated)
    182 {
    183     return findMagicComment(content, "sourceMappingURL", commentType, deprecated);
    184 }
    185 
    186 } // namespace ContentSearchUtils
    187 } // namespace blink
    188 
    189