bf3270f1d061dc8f63e1f4397a9288ee64f47532
[platform/upstream/v8.git] / src / uri.js
1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file contains support for URI manipulations written in
6 // JavaScript.
7
8 (function(global, utils) {
9
10 "use strict";
11
12 %CheckIsBootstrapping();
13
14 //- ------------------------------------------------------------------
15 // Imports
16
17 var GlobalObject = global.Object;
18 var GlobalArray = global.Array;
19 var InternalArray = utils.InternalArray;
20 var ToString;
21
22 utils.Import(function(from) {
23   ToString = from.ToString;
24 });
25
26 // -------------------------------------------------------------------
27 // Define internal helper functions.
28
29 function HexValueOf(code) {
30   // 0-9
31   if (code >= 48 && code <= 57) return code - 48;
32   // A-F
33   if (code >= 65 && code <= 70) return code - 55;
34   // a-f
35   if (code >= 97 && code <= 102) return code - 87;
36
37   return -1;
38 }
39
40 // Does the char code correspond to an alpha-numeric char.
41 function isAlphaNumeric(cc) {
42   // a - z
43   if (97 <= cc && cc <= 122) return true;
44   // A - Z
45   if (65 <= cc && cc <= 90) return true;
46   // 0 - 9
47   if (48 <= cc && cc <= 57) return true;
48
49   return false;
50 }
51
52 // Lazily initialized.
53 var hexCharCodeArray = 0;
54
55 function URIAddEncodedOctetToBuffer(octet, result, index) {
56   result[index++] = 37; // Char code of '%'.
57   result[index++] = hexCharCodeArray[octet >> 4];
58   result[index++] = hexCharCodeArray[octet & 0x0F];
59   return index;
60 }
61
62 function URIEncodeOctets(octets, result, index) {
63   if (hexCharCodeArray === 0) {
64     hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
65                         65, 66, 67, 68, 69, 70];
66   }
67   index = URIAddEncodedOctetToBuffer(octets[0], result, index);
68   if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
69   if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
70   if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
71   return index;
72 }
73
74 function URIEncodeSingle(cc, result, index) {
75   var x = (cc >> 12) & 0xF;
76   var y = (cc >> 6) & 63;
77   var z = cc & 63;
78   var octets = new GlobalArray(3);
79   if (cc <= 0x007F) {
80     octets[0] = cc;
81   } else if (cc <= 0x07FF) {
82     octets[0] = y + 192;
83     octets[1] = z + 128;
84   } else {
85     octets[0] = x + 224;
86     octets[1] = y + 128;
87     octets[2] = z + 128;
88   }
89   return URIEncodeOctets(octets, result, index);
90 }
91
92 function URIEncodePair(cc1 , cc2, result, index) {
93   var u = ((cc1 >> 6) & 0xF) + 1;
94   var w = (cc1 >> 2) & 0xF;
95   var x = cc1 & 3;
96   var y = (cc2 >> 6) & 0xF;
97   var z = cc2 & 63;
98   var octets = new GlobalArray(4);
99   octets[0] = (u >> 2) + 240;
100   octets[1] = (((u & 3) << 4) | w) + 128;
101   octets[2] = ((x << 4) | y) + 128;
102   octets[3] = z + 128;
103   return URIEncodeOctets(octets, result, index);
104 }
105
106 function URIHexCharsToCharCode(highChar, lowChar) {
107   var highCode = HexValueOf(highChar);
108   var lowCode = HexValueOf(lowChar);
109   if (highCode == -1 || lowCode == -1) throw MakeURIError();
110   return (highCode << 4) | lowCode;
111 }
112
113 // Callers must ensure that |result| is a sufficiently long sequential
114 // two-byte string!
115 function URIDecodeOctets(octets, result, index) {
116   var value;
117   var o0 = octets[0];
118   if (o0 < 0x80) {
119     value = o0;
120   } else if (o0 < 0xc2) {
121     throw MakeURIError();
122   } else {
123     var o1 = octets[1];
124     if (o0 < 0xe0) {
125       var a = o0 & 0x1f;
126       if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
127       var b = o1 & 0x3f;
128       value = (a << 6) + b;
129       if (value < 0x80 || value > 0x7ff) throw MakeURIError();
130     } else {
131       var o2 = octets[2];
132       if (o0 < 0xf0) {
133         var a = o0 & 0x0f;
134         if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
135         var b = o1 & 0x3f;
136         if ((o2 < 0x80) || (o2 > 0xbf)) throw MakeURIError();
137         var c = o2 & 0x3f;
138         value = (a << 12) + (b << 6) + c;
139         if ((value < 0x800) || (value > 0xffff)) throw MakeURIError();
140       } else {
141         var o3 = octets[3];
142         if (o0 < 0xf8) {
143           var a = (o0 & 0x07);
144           if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
145           var b = (o1 & 0x3f);
146           if ((o2 < 0x80) || (o2 > 0xbf)) {
147             throw MakeURIError();
148           }
149           var c = (o2 & 0x3f);
150           if ((o3 < 0x80) || (o3 > 0xbf)) throw MakeURIError();
151           var d = (o3 & 0x3f);
152           value = (a << 18) + (b << 12) + (c << 6) + d;
153           if ((value < 0x10000) || (value > 0x10ffff)) throw MakeURIError();
154         } else {
155           throw MakeURIError();
156         }
157       }
158     }
159   }
160   if (0xD800 <= value && value <= 0xDFFF) throw MakeURIError();
161   if (value < 0x10000) {
162     %_TwoByteSeqStringSetChar(index++, value, result);
163   } else {
164     %_TwoByteSeqStringSetChar(index++, (value >> 10) + 0xd7c0, result);
165     %_TwoByteSeqStringSetChar(index++, (value & 0x3ff) + 0xdc00, result);
166   }
167   return index;
168 }
169
170 // ECMA-262, section 15.1.3
171 function Encode(uri, unescape) {
172   uri = TO_STRING_INLINE(uri);
173   var uriLength = uri.length;
174   var array = new InternalArray(uriLength);
175   var index = 0;
176   for (var k = 0; k < uriLength; k++) {
177     var cc1 = %_StringCharCodeAt(uri, k);
178     if (unescape(cc1)) {
179       array[index++] = cc1;
180     } else {
181       if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw MakeURIError();
182       if (cc1 < 0xD800 || cc1 > 0xDBFF) {
183         index = URIEncodeSingle(cc1, array, index);
184       } else {
185         k++;
186         if (k == uriLength) throw MakeURIError();
187         var cc2 = %_StringCharCodeAt(uri, k);
188         if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw MakeURIError();
189         index = URIEncodePair(cc1, cc2, array, index);
190       }
191     }
192   }
193
194   var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
195   for (var i = 0; i < array.length; i++) {
196     %_OneByteSeqStringSetChar(i, array[i], result);
197   }
198   return result;
199 }
200
201 // ECMA-262, section 15.1.3
202 function Decode(uri, reserved) {
203   uri = TO_STRING_INLINE(uri);
204   var uriLength = uri.length;
205   var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
206   var index = 0;
207   var k = 0;
208
209   // Optimistically assume one-byte string.
210   for ( ; k < uriLength; k++) {
211     var code = %_StringCharCodeAt(uri, k);
212     if (code == 37) {  // '%'
213       if (k + 2 >= uriLength) throw MakeURIError();
214       var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, k+1),
215                                      %_StringCharCodeAt(uri, k+2));
216       if (cc >> 7) break;  // Assumption wrong, two-byte string.
217       if (reserved(cc)) {
218         %_OneByteSeqStringSetChar(index++, 37, one_byte);  // '%'.
219         %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+1),
220                                   one_byte);
221         %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+2),
222                                   one_byte);
223       } else {
224         %_OneByteSeqStringSetChar(index++, cc, one_byte);
225       }
226       k += 2;
227     } else {
228       if (code > 0x7f) break;  // Assumption wrong, two-byte string.
229       %_OneByteSeqStringSetChar(index++, code, one_byte);
230     }
231   }
232
233   one_byte = %TruncateString(one_byte, index);
234   if (k == uriLength) return one_byte;
235
236   // Write into two byte string.
237   var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
238   index = 0;
239
240   for ( ; k < uriLength; k++) {
241     var code = %_StringCharCodeAt(uri, k);
242     if (code == 37) {  // '%'
243       if (k + 2 >= uriLength) throw MakeURIError();
244       var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
245                                      %_StringCharCodeAt(uri, ++k));
246       if (cc >> 7) {
247         var n = 0;
248         while (((cc << ++n) & 0x80) != 0) { }
249         if (n == 1 || n > 4) throw MakeURIError();
250         var octets = new GlobalArray(n);
251         octets[0] = cc;
252         if (k + 3 * (n - 1) >= uriLength) throw MakeURIError();
253         for (var i = 1; i < n; i++) {
254           if (uri[++k] != '%') throw MakeURIError();
255           octets[i] = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
256                                             %_StringCharCodeAt(uri, ++k));
257         }
258         index = URIDecodeOctets(octets, two_byte, index);
259       } else  if (reserved(cc)) {
260         %_TwoByteSeqStringSetChar(index++, 37, two_byte);  // '%'.
261         %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k - 1),
262                                   two_byte);
263         %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k),
264                                   two_byte);
265       } else {
266         %_TwoByteSeqStringSetChar(index++, cc, two_byte);
267       }
268     } else {
269       %_TwoByteSeqStringSetChar(index++, code, two_byte);
270     }
271   }
272
273   two_byte = %TruncateString(two_byte, index);
274   return one_byte + two_byte;
275 }
276
277 // -------------------------------------------------------------------
278 // Define exported functions.
279
280 // ECMA-262 - B.2.1.
281 function URIEscapeJS(str) {
282   var s = ToString(str);
283   return %URIEscape(s);
284 }
285
286 // ECMA-262 - B.2.2.
287 function URIUnescapeJS(str) {
288   var s = ToString(str);
289   return %URIUnescape(s);
290 }
291
292 // ECMA-262 - 15.1.3.1.
293 function URIDecode(uri) {
294   var reservedPredicate = function(cc) {
295     // #$
296     if (35 <= cc && cc <= 36) return true;
297     // &
298     if (cc == 38) return true;
299     // +,
300     if (43 <= cc && cc <= 44) return true;
301     // /
302     if (cc == 47) return true;
303     // :;
304     if (58 <= cc && cc <= 59) return true;
305     // =
306     if (cc == 61) return true;
307     // ?@
308     if (63 <= cc && cc <= 64) return true;
309
310     return false;
311   };
312   var string = ToString(uri);
313   return Decode(string, reservedPredicate);
314 }
315
316 // ECMA-262 - 15.1.3.2.
317 function URIDecodeComponent(component) {
318   var reservedPredicate = function(cc) { return false; };
319   var string = ToString(component);
320   return Decode(string, reservedPredicate);
321 }
322
323 // ECMA-262 - 15.1.3.3.
324 function URIEncode(uri) {
325   var unescapePredicate = function(cc) {
326     if (isAlphaNumeric(cc)) return true;
327     // !
328     if (cc == 33) return true;
329     // #$
330     if (35 <= cc && cc <= 36) return true;
331     // &'()*+,-./
332     if (38 <= cc && cc <= 47) return true;
333     // :;
334     if (58 <= cc && cc <= 59) return true;
335     // =
336     if (cc == 61) return true;
337     // ?@
338     if (63 <= cc && cc <= 64) return true;
339     // _
340     if (cc == 95) return true;
341     // ~
342     if (cc == 126) return true;
343
344     return false;
345   };
346   var string = ToString(uri);
347   return Encode(string, unescapePredicate);
348 }
349
350 // ECMA-262 - 15.1.3.4
351 function URIEncodeComponent(component) {
352   var unescapePredicate = function(cc) {
353     if (isAlphaNumeric(cc)) return true;
354     // !
355     if (cc == 33) return true;
356     // '()*
357     if (39 <= cc && cc <= 42) return true;
358     // -.
359     if (45 <= cc && cc <= 46) return true;
360     // _
361     if (cc == 95) return true;
362     // ~
363     if (cc == 126) return true;
364
365     return false;
366   };
367   var string = ToString(component);
368   return Encode(string, unescapePredicate);
369 }
370
371 // -------------------------------------------------------------------
372 // Install exported functions.
373
374 // Set up non-enumerable URI functions on the global object and set
375 // their names.
376 utils.InstallFunctions(global, DONT_ENUM, [
377   "escape", URIEscapeJS,
378   "unescape", URIUnescapeJS,
379   "decodeURI", URIDecode,
380   "decodeURIComponent", URIDecodeComponent,
381   "encodeURI", URIEncode,
382   "encodeURIComponent", URIEncodeComponent
383 ]);
384
385 })