Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
3 (function () {
4 var tokenise = function (str) {
5 var tokens = []
6 , re = {
7 "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
8 , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/
9 , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/
10 , "string": /^"[^"]*"/
11 , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/
12 , "other": /^[^\t\n\r 0-9A-Z_a-z]/
13 }
14 , types = []
15 ;
16 for (var k in re) types.push(k);
17 while (str.length > 0) {
18 var matched = false;
19 for (var i = 0, n = types.length; i < n; i++) {
20 var type = types[i];
21 str = str.replace(re[type], function (tok) {
22 tokens.push({ type: type, value: tok });
23 matched = true;
24 return "";
25 });
26 if (matched) break;
27 }
28 if (matched) continue;
29 throw new Error("Token stream not progressing");
30 }
31 return tokens;
32 };
34 var parse = function (tokens) {
35 var line = 1;
36 tokens = tokens.slice();
38 var FLOAT = "float"
39 , INT = "integer"
40 , ID = "identifier"
41 , STR = "string"
42 , OTHER = "other"
43 ;
45 var WebIDLParseError = function (str, line, input, tokens) {
46 this.message = str;
47 this.line = line;
48 this.input = input;
49 this.tokens = tokens;
50 };
51 WebIDLParseError.prototype.toString = function () {
52 return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" +
53 JSON.stringify(this.tokens, null, 4);
54 };
56 var error = function (str) {
57 var tok = "", numTokens = 0, maxTokens = 5;
58 while (numTokens < maxTokens && tokens.length > numTokens) {
59 tok += tokens[numTokens].value;
60 numTokens++;
61 }
62 throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5));
63 };
65 var last_token = null;
67 var consume = function (type, value) {
68 if (!tokens.length || tokens[0].type !== type) return;
69 if (typeof value === "undefined" || tokens[0].value === value) {
70 last_token = tokens.shift();
71 if (type === ID) last_token.value = last_token.value.replace(/^_/, "");
72 return last_token;
73 }
74 };
76 var ws = function () {
77 if (!tokens.length) return;
78 if (tokens[0].type === "whitespace") {
79 var t = tokens.shift();
80 t.value.replace(/\n/g, function (m) { line++; return m; });
81 return t;
82 }
83 };
85 var all_ws = function () {
86 var t = { type: "whitespace", value: "" };
87 while (true) {
88 var w = ws();
89 if (!w) break;
90 t.value += w.value;
91 }
92 if (t.value.length > 0) return t;
93 };
95 var integer_type = function () {
96 var ret = "";
97 all_ws();
98 if (consume(ID, "unsigned")) ret = "unsigned ";
99 all_ws();
100 if (consume(ID, "short")) return ret + "short";
101 if (consume(ID, "long")) {
102 ret += "long";
103 all_ws();
104 if (consume(ID, "long")) return ret + " long";
105 return ret;
106 }
107 if (ret) error("Failed to parse integer type");
108 };
110 var float_type = function () {
111 var ret = "";
112 all_ws();
113 if (consume(ID, "unrestricted")) ret = "unrestricted ";
114 all_ws();
115 if (consume(ID, "float")) return ret + "float";
116 if (consume(ID, "double")) return ret + "double";
117 if (ret) error("Failed to parse float type");
118 };
120 var primitive_type = function () {
121 var num_type = integer_type() || float_type();
122 if (num_type) return num_type;
123 all_ws();
124 if (consume(ID, "boolean")) return "boolean";
125 if (consume(ID, "byte")) return "byte";
126 if (consume(ID, "octet")) return "octet";
127 };
129 var const_value = function () {
130 if (consume(ID, "true")) return { type: "boolean", value: true };
131 if (consume(ID, "false")) return { type: "boolean", value: false };
132 if (consume(ID, "null")) return { type: "null" };
133 if (consume(ID, "Infinity")) return { type: "Infinity", negative: false };
134 if (consume(ID, "NaN")) return { type: "NaN" };
135 var ret = consume(FLOAT) || consume(INT);
136 if (ret) return { type: "number", value: 1 * ret.value };
137 var tok = consume(OTHER, "-");
138 if (tok) {
139 if (consume(ID, "Infinity")) return { type: "Infinity", negative: true };
140 else tokens.unshift(tok);
141 }
142 };
144 var type_suffix = function (obj) {
145 while (true) {
146 all_ws();
147 if (consume(OTHER, "?")) {
148 if (obj.nullable) error("Can't nullable more than once");
149 obj.nullable = true;
150 }
151 else if (consume(OTHER, "[")) {
152 all_ws();
153 consume(OTHER, "]") || error("Unterminated array type");
154 if (!obj.array) obj.array = 1;
155 else obj.array++;
156 }
157 else return;
158 }
159 };
161 var single_type = function () {
162 var prim = primitive_type()
163 , ret = { sequence: false, nullable: false, array: false, union: false }
164 ;
165 if (prim) {
166 ret.idlType = prim;
167 }
168 else if (consume(ID, "sequence")) {
169 all_ws();
170 if (!consume(OTHER, "<")) {
171 ret.idlType = "sequence";
172 }
173 else {
174 ret.sequence = true;
175 ret.idlType = type() || error("Error parsing sequence type");
176 all_ws();
177 if (!consume(OTHER, ">")) error("Unterminated sequence");
178 all_ws();
179 if (consume(OTHER, "?")) ret.nullable = true;
180 return ret;
181 }
182 }
183 else {
184 var name = consume(ID);
185 if (!name) return;
186 ret.idlType = name.value;
187 }
188 type_suffix(ret);
189 if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
190 return ret;
191 };
193 var union_type = function () {
194 all_ws();
195 if (!consume(OTHER, "(")) return;
196 var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
197 var fst = type() || error("Union type with no content");
198 ret.idlType.push(fst);
199 while (true) {
200 all_ws();
201 if (!consume(ID, "or")) break;
202 var typ = type() || error("No type after 'or' in union type");
203 ret.idlType.push(typ);
204 }
205 if (!consume(OTHER, ")")) error("Unterminated union type");
206 type_suffix(ret);
207 return ret;
208 };
210 var type = function () {
211 return single_type() || union_type();
212 };
214 var argument = function () {
215 var ret = { optional: false, variadic: false };
216 ret.extAttrs = extended_attrs();
217 all_ws();
218 if (consume(ID, "optional")) {
219 ret.optional = true;
220 all_ws();
221 }
222 ret.idlType = type();
223 if (!ret.idlType) return;
224 if (!ret.optional) {
225 all_ws();
226 if (tokens.length >= 3 &&
227 tokens[0].type === "other" && tokens[0].value === "." &&
228 tokens[1].type === "other" && tokens[1].value === "." &&
229 tokens[2].type === "other" && tokens[2].value === "."
230 ) {
231 tokens.shift();
232 tokens.shift();
233 tokens.shift();
234 ret.variadic = true;
235 }
236 }
237 all_ws();
238 var name = consume(ID) || error("No name in argument");
239 ret.name = name.value;
240 if (ret.optional) {
241 all_ws();
242 ret["default"] = default_();
243 }
244 return ret;
245 };
247 var argument_list = function () {
248 var arg = argument(), ret = [];
249 if (!arg) return ret;
250 ret.push(arg);
251 while (true) {
252 all_ws();
253 if (!consume(OTHER, ",")) return ret;
254 all_ws();
255 var nxt = argument() || error("Trailing comma in arguments list");
256 ret.push(nxt);
257 }
258 };
260 var simple_extended_attr = function () {
261 all_ws();
262 var name = consume(ID);
263 if (!name) return;
264 var ret = {
265 name: name.value
266 , "arguments": null
267 };
268 all_ws();
269 var eq = consume(OTHER, "=");
270 if (eq) {
271 all_ws();
272 ret.rhs = consume(ID);
273 if (!ret.rhs) return error("No right hand side to extended attribute assignment");
274 }
275 all_ws();
276 if (consume(OTHER, "(")) {
277 ret["arguments"] = argument_list();
278 all_ws();
279 consume(OTHER, ")") || error("Unclosed argument in extended attribute");
280 }
281 return ret;
282 };
284 // Note: we parse something simpler than the official syntax. It's all that ever
285 // seems to be used
286 var extended_attrs = function () {
287 var eas = [];
288 all_ws();
289 if (!consume(OTHER, "[")) return eas;
290 eas[0] = simple_extended_attr() || error("Extended attribute with not content");
291 all_ws();
292 while (consume(OTHER, ",")) {
293 all_ws();
294 eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
295 all_ws();
296 }
297 consume(OTHER, "]") || error("No end of extended attribute");
298 return eas;
299 };
301 var default_ = function () {
302 all_ws();
303 if (consume(OTHER, "=")) {
304 all_ws();
305 var def = const_value();
306 if (def) {
307 return def;
308 }
309 else {
310 var str = consume(STR) || error("No value for default");
311 str.value = str.value.replace(/^"/, "").replace(/"$/, "");
312 return str;
313 }
314 }
315 };
317 var const_ = function () {
318 all_ws();
319 if (!consume(ID, "const")) return;
320 var ret = { type: "const", nullable: false };
321 all_ws();
322 var typ = primitive_type();
323 if (!typ) {
324 typ = consume(ID) || error("No type for const");
325 typ = typ.value;
326 }
327 ret.idlType = typ;
328 all_ws();
329 if (consume(OTHER, "?")) {
330 ret.nullable = true;
331 all_ws();
332 }
333 var name = consume(ID) || error("No name for const");
334 ret.name = name.value;
335 all_ws();
336 consume(OTHER, "=") || error("No value assignment for const");
337 all_ws();
338 var cnt = const_value();
339 if (cnt) ret.value = cnt;
340 else error("No value for const");
341 all_ws();
342 consume(OTHER, ";") || error("Unterminated const");
343 return ret;
344 };
346 var inheritance = function () {
347 all_ws();
348 if (consume(OTHER, ":")) {
349 all_ws();
350 var inh = consume(ID) || error ("No type in inheritance");
351 return inh.value;
352 }
353 };
355 var operation_rest = function (ret) {
356 all_ws();
357 if (!ret) ret = {};
358 var name = consume(ID);
359 ret.name = name ? name.value : null;
360 all_ws();
361 consume(OTHER, "(") || error("Invalid operation");
362 ret["arguments"] = argument_list();
363 all_ws();
364 consume(OTHER, ")") || error("Unterminated operation");
365 all_ws();
366 consume(OTHER, ";") || error("Unterminated operation");
367 return ret;
368 };
370 var callback = function () {
371 all_ws();
372 var ret;
373 if (!consume(ID, "callback")) return;
374 all_ws();
375 var tok = consume(ID, "interface");
376 if (tok) {
377 tokens.unshift(tok);
378 ret = interface_();
379 ret.type = "callback interface";
380 return ret;
381 }
382 var name = consume(ID) || error("No name for callback");
383 ret = { type: "callback", name: name.value };
384 all_ws();
385 consume(OTHER, "=") || error("No assignment in callback");
386 all_ws();
387 ret.idlType = return_type();
388 all_ws();
389 consume(OTHER, "(") || error("No arguments in callback");
390 ret["arguments"] = argument_list();
391 all_ws();
392 consume(OTHER, ")") || error("Unterminated callback");
393 all_ws();
394 consume(OTHER, ";") || error("Unterminated callback");
395 return ret;
396 };
398 var attribute = function () {
399 all_ws();
400 var grabbed = []
401 , ret = {
402 type: "attribute"
403 , "static": false
404 , stringifier: false
405 , inherit: false
406 , readonly: false
407 };
408 if (consume(ID, "static")) {
409 ret["static"] = true;
410 grabbed.push(last_token);
411 }
412 else if (consume(ID, "stringifier")) {
413 ret.stringifier = true;
414 grabbed.push(last_token);
415 }
416 var w = all_ws();
417 if (w) grabbed.push(w);
418 if (consume(ID, "inherit")) {
419 if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit");
420 ret.inherit = true;
421 grabbed.push(last_token);
422 var w = all_ws();
423 if (w) grabbed.push(w);
424 }
425 if (consume(ID, "readonly")) {
426 ret.readonly = true;
427 grabbed.push(last_token);
428 var w = all_ws();
429 if (w) grabbed.push(w);
430 }
431 if (!consume(ID, "attribute")) {
432 tokens = grabbed.concat(tokens);
433 return;
434 }
435 all_ws();
436 ret.idlType = type() || error("No type in attribute");
437 if (ret.idlType.sequence) error("Attributes cannot accept sequence types");
438 all_ws();
439 var name = consume(ID) || error("No name in attribute");
440 ret.name = name.value;
441 all_ws();
442 consume(OTHER, ";") || error("Unterminated attribute");
443 return ret;
444 };
446 var return_type = function () {
447 var typ = type();
448 if (!typ) {
449 if (consume(ID, "void")) {
450 return "void";
451 }
452 else error("No return type");
453 }
454 return typ;
455 };
457 var operation = function () {
458 all_ws();
459 var ret = {
460 type: "operation"
461 , getter: false
462 , setter: false
463 , creator: false
464 , deleter: false
465 , legacycaller: false
466 , "static": false
467 , stringifier: false
468 };
469 while (true) {
470 all_ws();
471 if (consume(ID, "getter")) ret.getter = true;
472 else if (consume(ID, "setter")) ret.setter = true;
473 else if (consume(ID, "creator")) ret.creator = true;
474 else if (consume(ID, "deleter")) ret.deleter = true;
475 else if (consume(ID, "legacycaller")) ret.legacycaller = true;
476 else break;
477 }
478 if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
479 all_ws();
480 ret.idlType = return_type();
481 operation_rest(ret);
482 return ret;
483 }
484 if (consume(ID, "static")) {
485 ret["static"] = true;
486 ret.idlType = return_type();
487 operation_rest(ret);
488 return ret;
489 }
490 else if (consume(ID, "stringifier")) {
491 ret.stringifier = true;
492 all_ws();
493 if (consume(OTHER, ";")) return ret;
494 ret.idlType = return_type();
495 operation_rest(ret);
496 return ret;
497 }
498 ret.idlType = return_type();
499 all_ws();
500 if (consume(ID, "iterator")) {
501 all_ws();
502 ret.type = "iterator";
503 if (consume(ID, "object")) {
504 ret.iteratorObject = "object";
505 }
506 else if (consume(OTHER, "=")) {
507 all_ws();
508 var name = consume(ID) || error("No right hand side in iterator");
509 ret.iteratorObject = name.value;
510 }
511 all_ws();
512 consume(OTHER, ";") || error("Unterminated iterator");
513 return ret;
514 }
515 else {
516 operation_rest(ret);
517 return ret;
518 }
519 };
521 var identifiers = function (arr) {
522 while (true) {
523 all_ws();
524 if (consume(OTHER, ",")) {
525 all_ws();
526 var name = consume(ID) || error("Trailing comma in identifiers list");
527 arr.push(name.value);
528 }
529 else break;
530 }
531 };
533 var serialiser = function () {
534 all_ws();
535 if (!consume(ID, "serializer")) return;
536 var ret = { type: "serializer" };
537 all_ws();
538 if (consume(OTHER, "=")) {
539 all_ws();
540 if (consume(OTHER, "{")) {
541 ret.patternMap = true;
542 all_ws();
543 var id = consume(ID);
544 if (id && id.value === "getter") {
545 ret.names = ["getter"];
546 }
547 else if (id && id.value === "inherit") {
548 ret.names = ["inherit"];
549 identifiers(ret.names);
550 }
551 else if (id) {
552 ret.names = [id.value];
553 identifiers(ret.names);
554 }
555 else {
556 ret.names = [];
557 }
558 all_ws();
559 consume(OTHER, "}") || error("Unterminated serializer pattern map");
560 }
561 else if (consume(OTHER, "[")) {
562 ret.patternList = true;
563 all_ws();
564 var id = consume(ID);
565 if (id && id.value === "getter") {
566 ret.names = ["getter"];
567 }
568 else if (id) {
569 ret.names = [id.value];
570 identifiers(ret.names);
571 }
572 else {
573 ret.names = [];
574 }
575 all_ws();
576 consume(OTHER, "]") || error("Unterminated serializer pattern list");
577 }
578 else {
579 var name = consume(ID) || error("Invalid serializer");
580 ret.name = name.value;
581 }
582 all_ws();
583 consume(OTHER, ";") || error("Unterminated serializer");
584 return ret;
585 }
586 else if (consume(OTHER, ";")) {
587 // noop, just parsing
588 }
589 else {
590 ret.idlType = return_type();
591 all_ws();
592 ret.operation = operation_rest();
593 }
594 return ret;
595 };
597 var interface_ = function (isPartial) {
598 all_ws();
599 if (!consume(ID, "interface")) return;
600 all_ws();
601 var name = consume(ID) || error("No name for interface");
602 var ret = {
603 type: "interface"
604 , name: name.value
605 , partial: false
606 , members: []
607 };
608 if (!isPartial) ret.inheritance = inheritance() || null;
609 all_ws();
610 consume(OTHER, "{") || error("Bodyless interface");
611 while (true) {
612 all_ws();
613 if (consume(OTHER, "}")) {
614 all_ws();
615 consume(OTHER, ";") || error("Missing semicolon after interface");
616 return ret;
617 }
618 var ea = extended_attrs();
619 all_ws();
620 var cnt = const_();
621 if (cnt) {
622 cnt.extAttrs = ea;
623 ret.members.push(cnt);
624 continue;
625 }
626 var mem = serialiser() || attribute() || operation() || error("Unknown member");
627 mem.extAttrs = ea;
628 ret.members.push(mem);
629 }
630 };
632 var partial = function () {
633 all_ws();
634 if (!consume(ID, "partial")) return;
635 var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything");
636 thing.partial = true;
637 return thing;
638 };
640 var dictionary = function (isPartial) {
641 all_ws();
642 if (!consume(ID, "dictionary")) return;
643 all_ws();
644 var name = consume(ID) || error("No name for dictionary");
645 var ret = {
646 type: "dictionary"
647 , name: name.value
648 , partial: false
649 , members: []
650 };
651 if (!isPartial) ret.inheritance = inheritance() || null;
652 all_ws();
653 consume(OTHER, "{") || error("Bodyless dictionary");
654 while (true) {
655 all_ws();
656 if (consume(OTHER, "}")) {
657 all_ws();
658 consume(OTHER, ";") || error("Missing semicolon after dictionary");
659 return ret;
660 }
661 var ea = extended_attrs();
662 all_ws();
663 var typ = type() || error("No type for dictionary member");
664 all_ws();
665 var name = consume(ID) || error("No name for dictionary member");
666 ret.members.push({
667 type: "field"
668 , name: name.value
669 , idlType: typ
670 , extAttrs: ea
671 , "default": default_()
672 });
673 all_ws();
674 consume(OTHER, ";") || error("Unterminated dictionary member");
675 }
676 };
678 var exception = function () {
679 all_ws();
680 if (!consume(ID, "exception")) return;
681 all_ws();
682 var name = consume(ID) || error("No name for exception");
683 var ret = {
684 type: "exception"
685 , name: name.value
686 , members: []
687 };
688 ret.inheritance = inheritance() || null;
689 all_ws();
690 consume(OTHER, "{") || error("Bodyless exception");
691 while (true) {
692 all_ws();
693 if (consume(OTHER, "}")) {
694 all_ws();
695 consume(OTHER, ";") || error("Missing semicolon after exception");
696 return ret;
697 }
698 var ea = extended_attrs();
699 all_ws();
700 var cnt = const_();
701 if (cnt) {
702 cnt.extAttrs = ea;
703 ret.members.push(cnt);
704 }
705 else {
706 var typ = type();
707 all_ws();
708 var name = consume(ID);
709 all_ws();
710 if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body");
711 ret.members.push({
712 type: "field"
713 , name: name.value
714 , idlType: typ
715 , extAttrs: ea
716 });
717 }
718 }
719 };
721 var enum_ = function () {
722 all_ws();
723 if (!consume(ID, "enum")) return;
724 all_ws();
725 var name = consume(ID) || error("No name for enum");
726 var ret = {
727 type: "enum"
728 , name: name.value
729 , values: []
730 };
731 all_ws();
732 consume(OTHER, "{") || error("No curly for enum");
733 var saw_comma = false;
734 while (true) {
735 all_ws();
736 if (consume(OTHER, "}")) {
737 all_ws();
738 if (saw_comma) error("Trailing comma in enum");
739 consume(OTHER, ";") || error("No semicolon after enum");
740 return ret;
741 }
742 var val = consume(STR) || error("Unexpected value in enum");
743 ret.values.push(val.value.replace(/"/g, ""));
744 all_ws();
745 if (consume(OTHER, ",")) {
746 all_ws();
747 saw_comma = true;
748 }
749 else {
750 saw_comma = false;
751 }
752 }
753 };
755 var typedef = function () {
756 all_ws();
757 if (!consume(ID, "typedef")) return;
758 var ret = {
759 type: "typedef"
760 };
761 all_ws();
762 ret.typeExtAttrs = extended_attrs();
763 all_ws();
764 ret.idlType = type() || error("No type in typedef");
765 all_ws();
766 var name = consume(ID) || error("No name in typedef");
767 ret.name = name.value;
768 all_ws();
769 consume(OTHER, ";") || error("Unterminated typedef");
770 return ret;
771 };
773 var implements_ = function () {
774 all_ws();
775 var target = consume(ID);
776 if (!target) return;
777 var w = all_ws();
778 if (consume(ID, "implements")) {
779 var ret = {
780 type: "implements"
781 , target: target.value
782 };
783 all_ws();
784 var imp = consume(ID) || error("Incomplete implements statement");
785 ret["implements"] = imp.value;
786 all_ws();
787 consume(OTHER, ";") || error("No terminating ; for implements statement");
788 return ret;
789 }
790 else {
791 // rollback
792 tokens.unshift(w);
793 tokens.unshift(target);
794 }
795 };
797 var definition = function () {
798 return callback() ||
799 interface_() ||
800 partial() ||
801 dictionary() ||
802 exception() ||
803 enum_() ||
804 typedef() ||
805 implements_()
806 ;
807 };
809 var definitions = function () {
810 if (!tokens.length) return [];
811 var defs = [];
812 while (true) {
813 var ea = extended_attrs()
814 , def = definition();
815 if (!def) {
816 if (ea.length) error("Stray extended attributes");
817 break;
818 }
819 def.extAttrs = ea;
820 defs.push(def);
821 }
822 return defs;
823 };
824 var res = definitions();
825 if (tokens.length) error("Unrecognised tokens");
826 return res;
827 };
829 var obj = {
830 parse: function (str) {
831 var tokens = tokenise(str);
832 return parse(tokens);
833 }
834 };
835 if (typeof module !== "undefined" && module.exports) {
836 module.exports = obj;
837 }
838 else {
839 window.WebIDL2 = obj;
840 }
841 }());