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