WebHTTPBody: Keep track of whether the data includes passwords.
[WebKit-https.git] / Source / WebKit / chromium / scripts / jsmin.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2010 Google Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 #         * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #         * 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 #         * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #
31 # This is a Python port of Douglas Crockford's jsmin.cc. See original
32 # copyright notice below.
33 #
34 # Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
35 #
36 # Permission is hereby granted, free of charge, to any person obtaining a copy of
37 # this software and associated documentation files (the "Software"), to deal in
38 # the Software without restriction, including without limitation the rights to
39 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
40 # of the Software, and to permit persons to whom the Software is furnished to do
41 # so, subject to the following conditions:
42 #
43 # The above copyright notice and this permission notice shall be included in all
44 # copies or substantial portions of the Software.
45 #
46 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
52 # SOFTWARE.
53 #
54
55 from cStringIO import StringIO
56 import sys
57
58
59 class UnterminatedComment(Exception):
60     pass
61
62
63 class UnterminatedStringLiteral(Exception):
64     pass
65
66
67 class UnterminatedRegularExpression(Exception):
68     pass
69
70
71 EOF = ''
72
73
74 def jsmin(text):
75     minifier = JavaScriptMinifier()
76     minifier.input = StringIO(text)
77     minifier.output = StringIO()
78     minifier.jsmin()
79     return minifier.output.getvalue()
80
81
82 class JavaScriptMinifier(object):
83
84     def isAlphanum(self, c):
85         """ return true if the character is a letter, digit, underscore,
86             dollar sign, or non-ASCII character.
87         """
88         return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
89             (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or
90             c > 126)
91
92     def get(self):
93         """ return the next character from stdin. Watch out for lookahead. If
94             the character is a control character, translate it to a space or
95             linefeed.
96         """
97         c = self.theLookahead
98         self.theLookahead = EOF
99         if c == EOF:
100             c = self.input.read(1)
101         if c >= ' ' or c == '\n' or c == EOF:
102             return c
103         if c == '\r':
104             return '\n'
105         return ' '
106
107     def peek(self):
108         """ get the next character without getting it. """
109         self.theLookahead = self.get()
110         return self.theLookahead
111
112     def next(self):
113         """ get the next character, excluding comments. peek() is used to see
114             if a '/' is followed by a '/' or '*'.
115         """
116         c = self.get()
117         if c == '/':
118             peek = self.peek()
119             if peek == '/':
120                 while True:
121                     c = self.get()
122                     if c <= '\n':
123                         return c
124             elif peek == '*':
125                 self.get()
126                 while True:
127                     get = self.get()
128                     if get == '*':
129                         if self.peek() == '/':
130                             self.get()
131                             return ' '
132                     elif get == EOF:
133                         raise UnterminatedComment()
134             else:
135                 return c
136         return c
137
138     def putc(self, c):
139         self.output.write(c)
140
141     def action(self, d):
142         """ do something! What you do is determined by the argument:
143                1   Output A. Copy B to A. Get the next B.
144                2   Copy B to A. Get the next B. (Delete A).
145                3   Get the next B. (Delete B).
146             action treats a string as a single character. Wow!
147             action recognizes a regular expression if it is preceded by ( or , or =.
148         """
149         if d <= 1:
150             self.putc(self.theA)
151         if d <= 2:
152             self.theA = self.theB
153             if self.theA == '\'' or self.theA == '"':
154                 while True:
155                     self.putc(self.theA)
156                     self.theA = self.get()
157                     if self.theA == self.theB:
158                         break
159                     if self.theA == '\\':
160                         self.putc(self.theA)
161                         self.theA = self.get()
162                     if self.theA == EOF:
163                         raise UnterminatedString()
164         if d <= 3:
165             self.theB = self.next()
166             if self.theB == '/' and self.theA in ['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '\n']:
167                 self.putc(self.theA)
168                 self.putc(self.theB)
169                 while True:
170                     self.theA = self.get()
171                     if self.theA == '/':
172                         break
173                     if self.theA == '\\':
174                         self.putc(self.theA)
175                         self.theA = self.get()
176                     if self.theA == EOF:
177                         raise UnterminatedRegularExpression()
178                     self.putc(self.theA)
179                 self.theB = self.next()
180
181     def jsmin(self):
182         """ Copy the input to the output, deleting the characters which are
183             insignificant to JavaScript. Comments will be removed. Tabs will be
184             replaced with spaces. Carriage returns will be replaced with linefeeds.
185             Most spaces and linefeeds will be removed.
186         """
187         self.theA = '\n'
188         self.theLookahead = EOF
189         self.action(3)
190         while self.theA != EOF:
191             if self.theA == ' ':
192                 if self.isAlphanum(self.theB):
193                     self.action(1)
194                 else:
195                     self.action(2)
196             elif self.theA == '\n':
197                 if self.theB in ['{', '[', '(', '+', '-']:
198                     self.action(1)
199                 elif self.theB == ' ':
200                     self.action(3)
201                 else:
202                     if self.isAlphanum(self.theB):
203                         self.action(1)
204                     else:
205                         self.action(2)
206             else:
207                 if self.theB == ' ':
208                     if self.isAlphanum(self.theA):
209                         self.action(1)
210                     else:
211                         self.action(3)
212                 elif self.theB == '\n':
213                     if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
214                         self.action(1)
215                     else:
216                         if self.isAlphanum(self.theA):
217                             self.action(1)
218                         else:
219                             self.action(3)
220                 else:
221                     self.action(1)
222
223
224 if __name__ == '__main__':
225     minifier = JavaScriptMinifier()
226     minifier.input = sys.stdin
227     minifier.output = sys.stdout
228     minifier.jsmin()
229     sys.stdin.close()