Home | History | Annotate | Download | only in wml
      1 /**
      2  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  *
     19  */
     20 
     21 #include "config.h"
     22 
     23 #if ENABLE(WML)
     24 #include "WMLVariables.h"
     25 
     26 #include "WMLDocument.h"
     27 #include <wtf/ASCIICType.h>
     28 
     29 namespace WebCore {
     30 
     31 // WML variables specification, excluding the
     32 // pre-WML 1.0 deprecated variable syntax
     33 //
     34 // varname = ("_" | alpha) ("_" | alpha | digit)*
     35 // conv = ":" ("e" ("scape")? | "n" ("oesc")? | "u" ("nesc")?)
     36 // var = ("$" varname) | ("$(" varname (conv)? ")")
     37 
     38 static bool isValidFirstVariableNameCharacter(const UChar& character)
     39 {
     40     return WTF::isASCIIAlpha(character)
     41            || character == '_';
     42 }
     43 
     44 static bool isValidVariableNameCharacter(const UChar& character)
     45 {
     46     return WTF::isASCIIAlpha(character)
     47            || WTF::isASCIIDigit(character)
     48            || character == '_';
     49 }
     50 
     51 static bool isValidVariableEscapingModeString(const String& mode, WMLVariableEscapingMode& escapeMode)
     52 {
     53     if (mode == "e" || mode == "escape")
     54         escapeMode = WMLVariableEscapingEscape;
     55     else if (mode == "u" || mode == "unesc")
     56         escapeMode = WMLVariableEscapingUnescape;
     57     else if (mode == "n" || mode == "noesc")
     58         escapeMode = WMLVariableEscapingNone;
     59     else
     60         return false;
     61 
     62     return true;
     63 }
     64 
     65 bool isValidVariableName(const String& name)
     66 {
     67     if (name.isEmpty())
     68         return false;
     69 
     70     const UChar* characters = name.characters();
     71     if (!isValidFirstVariableNameCharacter(characters[0]))
     72         return false;
     73 
     74     unsigned length = name.length();
     75     for (unsigned i = 1; i < length; ++i) {
     76         if (!isValidVariableNameCharacter(characters[i]))
     77             return false;
     78     }
     79 
     80     return true;
     81 }
     82 
     83 bool containsVariableReference(const String& text, bool& isValid)
     84 {
     85     isValid = true;
     86     bool foundReference = false;
     87     bool finished = false;
     88     int currentPosition = 0;
     89     const UChar* characters = text.characters();
     90 
     91     while (!finished) {
     92         // Find beginning of variable reference
     93         int referenceStartPosition = text.find('$', currentPosition);
     94         if (referenceStartPosition == -1) {
     95             finished = true;
     96             break;
     97         }
     98 
     99         foundReference = true;
    100 
    101         int nameStartPosition = referenceStartPosition + 1;
    102         int nameEndPosition = -1;
    103 
    104         if (characters[nameStartPosition] == '(') {
    105             // If the input string contains an open brace, a close brace must exist as well
    106             nameEndPosition = text.find(')', nameStartPosition + 1);
    107             if (nameEndPosition == -1) {
    108                 finished = true;
    109                 isValid = false;
    110                 break;
    111             }
    112 
    113             ++nameStartPosition;
    114         } else {
    115             int length = text.length();
    116             for (nameEndPosition = nameStartPosition; nameEndPosition < length; ++nameEndPosition) {
    117                 if (!isValidVariableNameCharacter(text[nameEndPosition]))
    118                     break;
    119             }
    120         }
    121 
    122         if (nameEndPosition < nameStartPosition) {
    123             finished = true;
    124             isValid = false;
    125             break;
    126         }
    127 
    128         // Eventually split of conversion string, and check its syntax afterwards
    129         String conversionString;
    130         String variableName = text.substring(nameStartPosition, nameEndPosition - nameStartPosition);
    131 
    132         int conversionStringStart = variableName.find(':');
    133         if (conversionStringStart != -1) {
    134             conversionString = variableName.substring(conversionStringStart + 1, variableName.length() - (conversionStringStart + 1));
    135             variableName = variableName.left(conversionStringStart);
    136         }
    137 
    138         isValid = isValidVariableName(variableName);
    139         if (!isValid) {
    140             finished = true;
    141             break;
    142         }
    143 
    144         if (!conversionString.isEmpty()) {
    145             isValid = isValidVariableName(conversionString);
    146             if (!isValid) {
    147                 finished = true;
    148                 break;
    149             }
    150 
    151             WMLVariableEscapingMode escapeMode = WMLVariableEscapingNone;
    152             isValid = isValidVariableEscapingModeString(conversionString, escapeMode);
    153             if (!isValid) {
    154                 finished = true;
    155                 break;
    156             }
    157         }
    158 
    159         currentPosition = nameEndPosition;
    160     }
    161 
    162     return foundReference;
    163 }
    164 
    165 String substituteVariableReferences(const String& reference, Document* document, WMLVariableEscapingMode escapeMode)
    166 {
    167     ASSERT(document);
    168 
    169     if (reference.isEmpty())
    170         return reference;
    171 
    172     WMLPageState* pageState = wmlPageStateForDocument(document);
    173     if (!pageState)
    174         return reference;
    175 
    176     bool isValid = true;
    177     String remainingInput = reference;
    178     String result;
    179 
    180     while (!remainingInput.isEmpty()) {
    181         ASSERT(isValid);
    182 
    183         int start = remainingInput.find("$");
    184         if (start == -1) {
    185             // Consume all remaining characters, as there's nothing more to substitute
    186             result += remainingInput;
    187             break;
    188         }
    189 
    190         // Consume all characters until the variable reference beginning
    191         result += remainingInput.left(start);
    192         remainingInput.remove(0, start);
    193 
    194         // Transform adjacent dollar signs into a single dollar sign as string literal
    195         if (remainingInput[1] == '$') {
    196             result += "$";
    197             remainingInput.remove(0, 2);
    198             continue;
    199         }
    200 
    201         String variableName;
    202         String conversionMode;
    203 
    204         if (remainingInput[1] == '(') {
    205             int referenceEndPosition = remainingInput.find(")");
    206             if (referenceEndPosition == -1) {
    207                 isValid = false;
    208                 break;
    209             }
    210 
    211             variableName = remainingInput.substring(2, referenceEndPosition - 2);
    212             remainingInput.remove(0, referenceEndPosition + 1);
    213 
    214             // Determine variable conversion mode string
    215             int pos = variableName.find(':');
    216             if (pos != -1) {
    217                 conversionMode = variableName.substring(pos + 1, variableName.length() - (pos + 1));
    218                 variableName = variableName.left(pos);
    219             }
    220         } else {
    221             int length = remainingInput.length();
    222             int referenceEndPosition = 1;
    223 
    224             for (; referenceEndPosition < length; ++referenceEndPosition) {
    225                 if (!isValidVariableNameCharacter(remainingInput[referenceEndPosition]))
    226                     break;
    227             }
    228 
    229             variableName = remainingInput.substring(1, referenceEndPosition - 1);
    230             remainingInput.remove(0, referenceEndPosition);
    231         }
    232 
    233         isValid = isValidVariableName(variableName);
    234         if (!isValid)
    235             break;
    236 
    237         ASSERT(!variableName.isEmpty());
    238 
    239         String variableValue = pageState->getVariable(variableName);
    240         if (variableValue.isEmpty())
    241             continue;
    242 
    243         if (containsVariableReference(variableValue, isValid)) {
    244             if (!isValid)
    245                 break;
    246 
    247             variableValue = substituteVariableReferences(variableValue, document, escapeMode);
    248             continue;
    249         }
    250 
    251         if (!conversionMode.isEmpty()) {
    252             // Override default escape mode, if desired
    253             WMLVariableEscapingMode specifiedEscapeMode = WMLVariableEscapingNone;
    254             if (isValid = isValidVariableEscapingModeString(conversionMode, specifiedEscapeMode))
    255                 escapeMode = specifiedEscapeMode;
    256 
    257             if (!isValid)
    258                 break;
    259         }
    260 
    261         switch (escapeMode) {
    262         case WMLVariableEscapingNone:
    263             break;
    264         case WMLVariableEscapingEscape:
    265             variableValue = encodeWithURLEscapeSequences(variableValue);
    266             break;
    267         case WMLVariableEscapingUnescape:
    268             variableValue = decodeURLEscapeSequences(variableValue);
    269             break;
    270         }
    271 
    272         result += variableValue;
    273         ASSERT(isValid);
    274     }
    275 
    276     if (!isValid) {
    277         reportWMLError(document, WMLErrorInvalidVariableReference);
    278         return reference;
    279     }
    280 
    281     return result;
    282 }
    283 
    284 }
    285 
    286 #endif
    287