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