Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 "use strict";
9 const cssTokenizer = require("devtools/sourceeditor/css-tokenizer");
11 /**
12 * Returns the string enclosed in quotes
13 */
14 function quoteString(string) {
15 let hasDoubleQuotes = string.contains('"');
16 let hasSingleQuotes = string.contains("'");
18 if (hasDoubleQuotes && !hasSingleQuotes) {
19 // In this case, no escaping required, just enclose in single-quotes
20 return "'" + string + "'";
21 }
23 // In all other cases, enclose in double-quotes, and escape any double-quote
24 // that may be in the string
25 return '"' + string.replace(/"/g, '\"') + '"';
26 }
28 /**
29 * Returns an array of CSS declarations given an string.
30 * For example, parseDeclarations("width: 1px; height: 1px") would return
31 * [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
32 *
33 * The input string is assumed to only contain declarations so { and } characters
34 * will be treated as part of either the property or value, depending where it's
35 * found.
36 *
37 * @param {string} inputString
38 * An input string of CSS
39 * @return {Array} an array of objects with the following signature:
40 * [{"name": string, "value": string, "priority": string}, ...]
41 */
42 function parseDeclarations(inputString) {
43 let tokens = cssTokenizer(inputString);
45 let declarations = [{name: "", value: "", priority: ""}];
47 let current = "", hasBang = false, lastProp;
48 for (let token of tokens) {
49 lastProp = declarations[declarations.length - 1];
51 if (token.tokenType === ":") {
52 if (!lastProp.name) {
53 // Set the current declaration name if there's no name yet
54 lastProp.name = current.trim();
55 current = "";
56 hasBang = false;
57 } else {
58 // Otherwise, just append ':' to the current value (declaration value
59 // with colons)
60 current += ":";
61 }
62 } else if (token.tokenType === ";") {
63 lastProp.value = current.trim();
64 current = "";
65 hasBang = false;
66 declarations.push({name: "", value: "", priority: ""});
67 } else {
68 switch(token.tokenType) {
69 case "IDENT":
70 if (token.value === "important" && hasBang) {
71 lastProp.priority = "important";
72 hasBang = false;
73 } else {
74 if (hasBang) {
75 current += "!";
76 }
77 current += token.value;
78 }
79 break;
80 case "WHITESPACE":
81 current += " ";
82 break;
83 case "DIMENSION":
84 current += token.repr;
85 break;
86 case "HASH":
87 current += "#" + token.value;
88 break;
89 case "URL":
90 current += "url(" + quoteString(token.value) + ")";
91 break;
92 case "FUNCTION":
93 current += token.value + "(";
94 break;
95 case ")":
96 current += token.tokenType;
97 break;
98 case "EOF":
99 break;
100 case "DELIM":
101 if (token.value === "!") {
102 hasBang = true;
103 } else {
104 current += token.value;
105 }
106 break;
107 case "STRING":
108 current += quoteString(token.value);
109 break;
110 case "{":
111 case "}":
112 current += token.tokenType;
113 break;
114 default:
115 current += token.value;
116 break;
117 }
118 }
119 }
121 // Handle whatever trailing properties or values might still be there
122 if (current) {
123 if (!lastProp.name) {
124 // Trailing property found, e.g. p1:v1;p2:v2;p3
125 lastProp.name = current.trim();
126 } else {
127 // Trailing value found, i.e. value without an ending ;
128 lastProp.value += current.trim();
129 }
130 }
132 // Remove declarations that have neither a name nor a value
133 declarations = declarations.filter(prop => prop.name || prop.value);
135 return declarations;
136 };
137 exports.parseDeclarations = parseDeclarations;
139 /**
140 * Expects a single CSS value to be passed as the input and parses the value
141 * and priority.
142 *
143 * @param {string} value The value from the text editor.
144 * @return {object} an object with 'value' and 'priority' properties.
145 */
146 function parseSingleValue(value) {
147 let declaration = parseDeclarations("a: " + value + ";")[0];
148 return {
149 value: declaration ? declaration.value : "",
150 priority: declaration ? declaration.priority : ""
151 };
152 };
153 exports.parseSingleValue = parseSingleValue;