tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XPathGenerator.cpp (5762B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "XPathGenerator.h"
      8 
      9 #include "Element.h"
     10 #include "nsGkAtoms.h"
     11 #include "nsTArray.h"
     12 
     13 /**
     14 * Check whether a character is a non-word character. A non-word character is a
     15 * character that isn't in ('a'..'z') or in ('A'..'Z') or a number or an
     16 * underscore.
     17 * */
     18 bool IsNonWordCharacter(const char16_t& aChar) {
     19  if (((char16_t('A') <= aChar) && (aChar <= char16_t('Z'))) ||
     20      ((char16_t('a') <= aChar) && (aChar <= char16_t('z'))) ||
     21      ((char16_t('0') <= aChar) && (aChar <= char16_t('9'))) ||
     22      (aChar == char16_t('_'))) {
     23    return false;
     24  } else {
     25    return true;
     26  }
     27 }
     28 
     29 /**
     30 * Check whether a string contains a non-word character.
     31 * */
     32 bool ContainNonWordCharacter(const nsAString& aStr) {
     33  const char16_t* cur = aStr.BeginReading();
     34  const char16_t* end = aStr.EndReading();
     35  for (; cur < end; ++cur) {
     36    if (IsNonWordCharacter(*cur)) {
     37      return true;
     38    }
     39  }
     40  return false;
     41 }
     42 
     43 /**
     44 * Get the prefix according to the given namespace and assign the result to
     45 * aResult.
     46 * */
     47 void GetPrefix(const nsINode* aNode, nsAString& aResult) {
     48  if (aNode->IsXULElement()) {
     49    aResult.AssignLiteral(u"xul");
     50  } else if (aNode->IsHTMLElement()) {
     51    aResult.AssignLiteral(u"xhtml");
     52  }
     53 }
     54 
     55 void GetNameAttribute(const nsINode* aNode, nsAString& aResult) {
     56  if (aNode->HasName()) {
     57    const mozilla::dom::Element* elem = aNode->AsElement();
     58    elem->GetAttr(nsGkAtoms::name, aResult);
     59  }
     60 }
     61 
     62 /**
     63 * Put all sequences of ' in a string in between '," and ",' . And then put
     64 * the result string in between concat(' and ').
     65 *
     66 * For example, a string 'a'' will return result concat('',"'",'a',"''",'')
     67 * */
     68 void GenerateConcatExpression(const nsAString& aStr, nsAString& aResult) {
     69  const char16_t* cur = aStr.BeginReading();
     70  const char16_t* end = aStr.EndReading();
     71 
     72  // Put all sequences of ' in between '," and ",'
     73  nsAutoString result;
     74  const char16_t* nonQuoteBeginPtr = nullptr;
     75  const char16_t* quoteBeginPtr = nullptr;
     76  for (; cur < end; ++cur) {
     77    if (char16_t('\'') == *cur) {
     78      if (nonQuoteBeginPtr) {
     79        result.Append(nonQuoteBeginPtr, cur - nonQuoteBeginPtr);
     80        nonQuoteBeginPtr = nullptr;
     81      }
     82      if (!quoteBeginPtr) {
     83        result.AppendLiteral(u"\',\"");
     84        quoteBeginPtr = cur;
     85      }
     86    } else {
     87      if (!nonQuoteBeginPtr) {
     88        nonQuoteBeginPtr = cur;
     89      }
     90      if (quoteBeginPtr) {
     91        result.Append(quoteBeginPtr, cur - quoteBeginPtr);
     92        result.AppendLiteral(u"\",\'");
     93        quoteBeginPtr = nullptr;
     94      }
     95    }
     96  }
     97 
     98  if (quoteBeginPtr) {
     99    result.Append(quoteBeginPtr, cur - quoteBeginPtr);
    100    result.AppendLiteral(u"\",\'");
    101  } else if (nonQuoteBeginPtr) {
    102    result.Append(nonQuoteBeginPtr, cur - nonQuoteBeginPtr);
    103  }
    104 
    105  // Prepend concat(' and append ').
    106  aResult.Assign(u"concat(\'"_ns + result + u"\')"_ns);
    107 }
    108 
    109 void XPathGenerator::QuoteArgument(const nsAString& aArg, nsAString& aResult) {
    110  if (!aArg.Contains('\'')) {
    111    aResult.Assign(u"\'"_ns + aArg + u"\'"_ns);
    112  } else if (!aArg.Contains('\"')) {
    113    aResult.Assign(u"\""_ns + aArg + u"\""_ns);
    114  } else {
    115    GenerateConcatExpression(aArg, aResult);
    116  }
    117 }
    118 
    119 void XPathGenerator::EscapeName(const nsAString& aName, nsAString& aResult) {
    120  if (ContainNonWordCharacter(aName)) {
    121    nsAutoString quotedArg;
    122    QuoteArgument(aName, quotedArg);
    123    aResult.Assign(u"*[local-name()="_ns + quotedArg + u"]"_ns);
    124  } else {
    125    aResult.Assign(aName);
    126  }
    127 }
    128 
    129 void XPathGenerator::Generate(const nsINode* aNode, nsAString& aResult) {
    130  if (!aNode->GetParentNode()) {
    131    aResult.Truncate();
    132    return;
    133  }
    134 
    135  nsAutoString nodeNamespaceURI;
    136  aNode->GetNamespaceURI(nodeNamespaceURI);
    137  const nsString& nodeLocalName = aNode->LocalName();
    138 
    139  nsAutoString prefix;
    140  nsAutoString tag;
    141  nsAutoString nodeEscapeName;
    142  GetPrefix(aNode, prefix);
    143  EscapeName(nodeLocalName, nodeEscapeName);
    144  if (prefix.IsEmpty()) {
    145    tag.Assign(nodeEscapeName);
    146  } else {
    147    tag.Assign(prefix + u":"_ns + nodeEscapeName);
    148  }
    149 
    150  if (aNode->HasID()) {
    151    // this must be an element
    152    const mozilla::dom::Element* elem = aNode->AsElement();
    153    nsAutoString elemId;
    154    nsAutoString quotedArgument;
    155    elem->GetId(elemId);
    156    QuoteArgument(elemId, quotedArgument);
    157    aResult.Assign(u"//"_ns + tag + u"[@id="_ns + quotedArgument + u"]"_ns);
    158    return;
    159  }
    160 
    161  int32_t count = 1;
    162  nsAutoString nodeNameAttribute;
    163  GetNameAttribute(aNode, nodeNameAttribute);
    164  for (const mozilla::dom::Element* e = aNode->GetPreviousElementSibling(); e;
    165       e = e->GetPreviousElementSibling()) {
    166    nsAutoString elementNamespaceURI;
    167    e->GetNamespaceURI(elementNamespaceURI);
    168    nsAutoString elementNameAttribute;
    169    GetNameAttribute(e, elementNameAttribute);
    170    if (e->LocalName().Equals(nodeLocalName) &&
    171        elementNamespaceURI.Equals(nodeNamespaceURI) &&
    172        (nodeNameAttribute.IsEmpty() ||
    173         elementNameAttribute.Equals(nodeNameAttribute))) {
    174      ++count;
    175    }
    176  }
    177 
    178  nsAutoString namePart;
    179  nsAutoString countPart;
    180  if (!nodeNameAttribute.IsEmpty()) {
    181    nsAutoString quotedArgument;
    182    QuoteArgument(nodeNameAttribute, quotedArgument);
    183    namePart.Assign(u"[@name="_ns + quotedArgument + u"]"_ns);
    184  }
    185  if (count != 1) {
    186    countPart.AssignLiteral(u"[");
    187    countPart.AppendInt(count);
    188    countPart.AppendLiteral(u"]");
    189  }
    190  Generate(aNode->GetParentNode(), aResult);
    191  aResult.Append(u"/"_ns + tag + namePart + countPart);
    192 }