Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright (C) 2008-2011 Google Inc. | 1 // Copyright (C) 2008-2012 Google Inc. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 /** | 15 /** |
16 * @fileoverview | 16 * @fileoverview |
17 * A partially tamed browser object model based on | 17 * A partially tamed browser object model based on |
18 * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/Overview.html" | 18 * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/Overview.html" |
19 * >DOM-Level-2-HTML</a> and specifically, the | 19 * >DOM-Level-2-HTML</a> and specifically, the |
20 * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/ecma-script-binding.html" | 20 * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/ecma-script-binding.html" |
21 * >ECMAScript Language Bindings</a>. | 21 * >ECMAScript Language Bindings</a>. |
22 * | 22 * |
23 * Caveats:<ul> | 23 * Caveats:<ul> |
24 * <li>This is not a full implementation. | |
25 * <li>Security Review is pending. | 24 * <li>Security Review is pending. |
26 * <li><code>===</code> and <code>!==</code> on node lists will not | 25 * <li><code>===</code> and <code>!==</code> on node lists will not |
27 * behave the same as with untamed node lists. Specifically, it is | 26 * behave the same as with untamed node lists. Specifically, it is |
28 * not always true that {@code nodeA.childNodes === nodeA.childNodes}. | 27 * not always true that {@code nodeA.childNodes === nodeA.childNodes}. |
29 * <li>Node lists are not "live" -- do not reflect changes in the DOM. | |
30 * </ul> | 28 * </ul> |
31 * | 29 * |
32 * <p> | 30 * <p> |
33 * TODO(ihab.awad): Our implementation of getAttribute (and friends) | 31 * TODO(ihab.awad): Our implementation of getAttribute (and friends) |
34 * is such that standard DOM attributes which we disallow for security | 32 * is such that standard DOM attributes which we disallow for security |
35 * reasons (like 'form:enctype') are placed in the "virtual" attributes | 33 * reasons (like 'form:enctype') are placed in the "virtual" attributes |
36 * map (the data-caja-* namespace). They appear to be settable and gettable, | 34 * map (the data-caja-* namespace). They appear to be settable and gettable, |
37 * but their values are ignored and do not have the expected semantics | 35 * but their values are ignored and do not have the expected semantics |
38 * per the DOM API. This is because we do not have a column in | 36 * per the DOM API. This is because we do not have a column in |
39 * html4-defs.js stating that an attribute is valid but explicitly | 37 * html4-defs.js stating that an attribute is valid but explicitly |
40 * blacklisted. Alternatives would be to always throw upon access to | 38 * blacklisted. Alternatives would be to always throw upon access to |
41 * these attributes; to make them always appear to be null; etc. Revisit | 39 * these attributes; to make them always appear to be null; etc. Revisit |
42 * this decision if needed. | 40 * this decision if needed. |
43 * | 41 * |
44 * @author mikesamuel@gmail.com (original Domita) | 42 * @author mikesamuel@gmail.com (original Domita) |
45 * @author kpreid@switchb.org (port to ES5) | 43 * @author kpreid@switchb.org (port to ES5) |
46 * @requires console | 44 * @requires console |
47 * @requires bridalMaker, cajaVM, cssSchema, lexCss, URI | 45 * @requires bridalMaker, cajaVM, cssSchema, lexCss, URI |
48 * @requires parseCssDeclarations, sanitizeCssProperty, unicode | 46 * @requires parseCssDeclarations, sanitizeCssProperty, unicode |
49 * @requires html, html4, htmlSchema | 47 * @requires html, html4, htmlSchema |
50 * @requires WeakMap, Proxy | 48 * @requires WeakMap, Proxy |
51 * @requires CSS_PROP_BIT_HISTORY_INSENSITIVE | 49 * @requires CSS_PROP_BIT_HISTORY_INSENSITIVE |
52 * @provides Domado | 50 * @provides Domado |
53 * @overrides domitaModules, window | 51 * @overrides window |
54 */ | 52 */ |
55 | 53 |
56 // The Turkish i seems to be a non-issue, but abort in case it is. | 54 // The Turkish i seems to be a non-issue, but abort in case it is. |
57 if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; } | 55 if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; } |
58 | 56 |
59 // TODO(kpreid): Review whether multiple uses of np() should be coalesced for | 57 // TODO(kpreid): Review whether multiple uses of np() should be coalesced for |
60 // efficiency. | 58 // efficiency. |
61 | 59 |
62 // TODO(kpreid): Move this from the global scope into the function(){}(); | |
63 // eliminate the domitaModules object (or possibly move more stuff into it). | |
64 var domitaModules; | |
65 var Domado = (function() { | 60 var Domado = (function() { |
66 'use strict'; | 61 'use strict'; |
67 | 62 |
68 var isVirtualizedElementName = htmlSchema.isVirtualizedElementName; | 63 var isVirtualizedElementName = htmlSchema.isVirtualizedElementName; |
69 var realToVirtualElementName = htmlSchema.realToVirtualElementName; | 64 var realToVirtualElementName = htmlSchema.realToVirtualElementName; |
70 var virtualToRealElementName = htmlSchema.virtualToRealElementName; | 65 var virtualToRealElementName = htmlSchema.virtualToRealElementName; |
71 | 66 |
72 var cajaPrefix = 'data-caja-'; | 67 var cajaPrefix = 'data-caja-'; |
73 var cajaPrefRe = new RegExp('^' + cajaPrefix); | 68 var cajaPrefRe = new RegExp('^' + cajaPrefix); |
74 | 69 |
(...skipping 30 matching lines...) Expand all Loading... | |
105 | 100 |
106 function uriRewrite(naiveUriPolicy, uri, effects, ltype, hints) { | 101 function uriRewrite(naiveUriPolicy, uri, effects, ltype, hints) { |
107 if (!naiveUriPolicy) { return null; } | 102 if (!naiveUriPolicy) { return null; } |
108 return allowedUriScheme(uri) ? | 103 return allowedUriScheme(uri) ? |
109 'function' === typeof naiveUriPolicy.rewrite ? | 104 'function' === typeof naiveUriPolicy.rewrite ? |
110 naiveUriPolicy.rewrite(uri, effects, ltype, hints) | 105 naiveUriPolicy.rewrite(uri, effects, ltype, hints) |
111 : null | 106 : null |
112 : null; | 107 : null; |
113 } | 108 } |
114 | 109 |
115 if (!domitaModules) { domitaModules = {}; } | 110 // TODO(kpreid): If not used for the upcoming modularity-of-element-tamings |
111 // refactoring, eliminate the domitaModules object. | |
112 var domitaModules = {}; | |
116 | 113 |
117 domitaModules.proxiesAvailable = typeof Proxy !== 'undefined'; | 114 domitaModules.proxiesAvailable = typeof Proxy !== 'undefined'; |
118 | 115 |
119 // The proxy facilities provided by Firefox and ES5/3 differ in whether the | 116 // The proxy facilities provided by Firefox and ES5/3 differ in whether the |
120 // proxy itself (or rather 'receiver') is the first argument to the 'get' | 117 // proxy itself (or rather 'receiver') is the first argument to the 'get' |
121 // traps. This autoswitches as needed, removing the first argument. | 118 // traps. This autoswitches as needed, removing the first argument. |
122 domitaModules.permuteProxyGetSet = (function () { | 119 domitaModules.permuteProxyGetSet = (function () { |
123 var needToSwap = false; | 120 var needToSwap = false; |
124 | 121 |
125 if (domitaModules.proxiesAvailable) { | 122 if (domitaModules.proxiesAvailable) { |
(...skipping 378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
504 }; | 501 }; |
505 ExpandoProxyHandler.prototype.defineProperty = function (name, descriptor) { | 502 ExpandoProxyHandler.prototype.defineProperty = function (name, descriptor) { |
506 if (name === "ident___") { | 503 if (name === "ident___") { |
507 if (descriptor.set === null) descriptor.set = undefined; | 504 if (descriptor.set === null) descriptor.set = undefined; |
508 Object.defineProperty(this, "ident", descriptor); | 505 Object.defineProperty(this, "ident", descriptor); |
509 } else if (name in this.target) { | 506 } else if (name in this.target) { |
510 // Forwards everything already defined (not expando). | 507 // Forwards everything already defined (not expando). |
511 return ProxyHandler.prototype.defineProperty.call(this, name, | 508 return ProxyHandler.prototype.defineProperty.call(this, name, |
512 descriptor); | 509 descriptor); |
513 } else { | 510 } else { |
514 if (!this.editable) { throw new Error("Not editable"); } | 511 if (!this.editable) { throw new Error(NOT_EDITABLE); } |
515 Object.defineProperty(this.storage, name, descriptor); | 512 Object.defineProperty(this.storage, name, descriptor); |
516 return true; | 513 return true; |
517 } | 514 } |
518 return false; | 515 return false; |
519 }; | 516 }; |
520 ExpandoProxyHandler.prototype['delete'] = function (name) { | 517 ExpandoProxyHandler.prototype['delete'] = function (name) { |
521 if (name === "ident___") { | 518 if (name === "ident___") { |
522 return false; | 519 return false; |
523 } else if (name in this.target) { | 520 } else if (name in this.target) { |
524 // Forwards everything already defined (not expando). | 521 // Forwards everything already defined (not expando). |
525 return ProxyHandler.prototype['delete'].call(this, name); | 522 return ProxyHandler.prototype['delete'].call(this, name); |
526 } else { | 523 } else { |
527 if (!this.editable) { throw new Error("Not editable"); } | 524 if (!this.editable) { throw new Error(NOT_EDITABLE); } |
528 return delete this.storage[name]; | 525 return delete this.storage[name]; |
529 } | 526 } |
530 return false; | 527 return false; |
531 }; | 528 }; |
532 ExpandoProxyHandler.prototype.fix = function () { | 529 ExpandoProxyHandler.prototype.fix = function () { |
533 // TODO(kpreid): Implement fixing, because it is possible to freeze a | 530 // TODO(kpreid): Implement fixing, because it is possible to freeze a |
534 // host DOM node and so ours should support it too. | 531 // host DOM node and so ours should support it too. |
535 return undefined; | 532 return undefined; |
536 }; | 533 }; |
537 | 534 |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
905 if (cssPropertyNames.hasOwnProperty(p)) { | 902 if (cssPropertyNames.hasOwnProperty(p)) { |
906 f(p); | 903 f(p); |
907 } | 904 } |
908 } | 905 } |
909 } | 906 } |
910 }; | 907 }; |
911 }; | 908 }; |
912 | 909 |
913 cajaVM.def(domitaModules); | 910 cajaVM.def(domitaModules); |
914 | 911 |
912 var NOT_EDITABLE = "Node not editable."; | |
913 var UNSAFE_TAGNAME = "Unsafe tag name."; | |
914 var UNKNOWN_TAGNAME = "Unknown tag name."; | |
915 var INDEX_SIZE_ERROR = "Index size error."; | |
916 | |
915 /** | 917 /** |
916 * Authorize the Domado library. | 918 * Authorize the Domado library. |
917 * | 919 * |
918 * The result of this constructor is almost stateless. The exceptions are | 920 * The result of this constructor is almost stateless. The exceptions are |
919 * that each instance has unique trademarks for certain types of tamed | 921 * that each instance has unique trademarks for certain types of tamed |
920 * objects, and a shared map allowing separate virtual documents to dispatch | 922 * objects, and a shared map allowing separate virtual documents to dispatch |
921 * events across them. (TODO(kpreid): Confirm this explanation is good.) | 923 * events across them. (TODO(kpreid): Confirm this explanation is good.) |
922 * | 924 * |
923 * @param {Object} taming. An interface to a taming membrane. | 925 * @param {Object} taming. An interface to a taming membrane. |
924 * @param {Object} opt_rulebreaker. If necessary, authorities to break the | 926 * @param {Object} opt_rulebreaker. If necessary, authorities to break the |
(...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1365 // a dimension negative. | 1367 // a dimension negative. |
1366 if (dh && element.clientHeight !== goalHeight) { | 1368 if (dh && element.clientHeight !== goalHeight) { |
1367 var hError = element.clientHeight - goalHeight; | 1369 var hError = element.clientHeight - goalHeight; |
1368 element.style.height = Math.max(0, h - hError) + 'px'; | 1370 element.style.height = Math.max(0, h - hError) + 'px'; |
1369 } | 1371 } |
1370 if (dw && element.clientWidth !== goalWidth) { | 1372 if (dw && element.clientWidth !== goalWidth) { |
1371 var wError = element.clientWidth - goalWidth; | 1373 var wError = element.clientWidth - goalWidth; |
1372 element.style.width = Math.max(0, w - wError) + 'px'; | 1374 element.style.width = Math.max(0, w - wError) + 'px'; |
1373 } | 1375 } |
1374 } | 1376 } |
1377 | |
1378 /** | |
1379 * Access policies | |
1380 * | |
1381 * Each of these objects is a policy for what type of access (read/write, | |
1382 * read-only, or none) is permitted to a Node or NodeList. Each policy | |
1383 * object determines the access for the associated node and its children. | |
1384 * The childPolicy may be overridden if the node is an opaque or foreign | |
1385 * node. | |
1386 * | |
1387 * Definitions: | |
1388 * childrenVisible: | |
1389 * This node appears to have the children it actually does; otherwise, | |
1390 * appears to have no children. | |
1391 * attributesVisible: | |
1392 * This node appears to have the attributes it actually does; | |
1393 * otherwise, appears to have no attributes. | |
1394 * editable: | |
1395 * This node's attributes and properties (other than children) may be | |
1396 * modified. | |
1397 * childrenEditable: | |
1398 * This node's childNodes list may be modified, and its children are | |
1399 * both editable and childrenEditable. | |
1400 * | |
1401 * These flags can express several meaningless cases; in particular, the | |
1402 * 'editable but not visible' cases do not occur. | |
1403 */ | |
1404 var protoNodePolicy = { | |
1405 requireEditable: function () { | |
1406 if (!this.editable) { | |
1407 throw new Error(NOT_EDITABLE); | |
1408 } | |
1409 }, | |
1410 requireChildrenEditable: function () { | |
1411 if (!this.childrenEditable) { | |
1412 throw new Error(NOT_EDITABLE); | |
1413 } | |
1414 }, | |
1415 requireUnrestricted: function () { | |
1416 if (!this.unrestricted) { | |
1417 throw new Error("Node is restricted"); | |
1418 } | |
1419 }, | |
1420 assertRestrictedBy: function (policy) { | |
1421 if (!this.childrenVisible && policy.childrenVisible || | |
1422 !this.attributesVisible && policy.attributesVisible || | |
1423 !this.editable && policy.editable || | |
1424 !this.childrenEditable && policy.childrenEditable || | |
1425 !this.upwardNavigation && policy.upwardNavigation || | |
1426 !this.unrestricted && policy.unrestricted) { | |
1427 throw new Error("Domado internal error: non-monotonic node policy"); | |
1428 } | |
1429 } | |
1430 }; | |
1431 // We eagerly await ES6 offering some kind of literal-with-prototype... | |
1432 var nodePolicyEditable = Object.create(protoNodePolicy); | |
1433 nodePolicyEditable.toString = function () { return "nodePolicyEditable"; }; | |
1434 nodePolicyEditable.childrenVisible = true; | |
1435 nodePolicyEditable.attributesVisible = true; | |
1436 nodePolicyEditable.editable = true; | |
1437 nodePolicyEditable.childrenEditable = true; | |
1438 nodePolicyEditable.upwardNavigation = true; | |
1439 nodePolicyEditable.unrestricted = true; | |
1440 nodePolicyEditable.childPolicy = nodePolicyEditable; | |
1441 | |
1442 var nodePolicyReadOnly = Object.create(protoNodePolicy); | |
1443 nodePolicyReadOnly.toString = function () { return "nodePolicyReadOnly"; }; | |
1444 nodePolicyReadOnly.childrenVisible = true; | |
1445 nodePolicyReadOnly.attributesVisible = true; | |
1446 nodePolicyReadOnly.editable = false; | |
1447 nodePolicyReadOnly.childrenEditable = false; | |
1448 nodePolicyReadOnly.upwardNavigation = true; | |
1449 nodePolicyReadOnly.unrestricted = true; | |
1450 nodePolicyReadOnly.childPolicy = nodePolicyReadOnly; | |
1451 | |
1452 var nodePolicyReadOnlyChildren = Object.create(protoNodePolicy); | |
1453 nodePolicyReadOnlyChildren.toString = | |
1454 function () { return "nodePolicyReadOnlyChildren"; }; | |
1455 nodePolicyReadOnlyChildren.childrenVisible = true; | |
1456 nodePolicyReadOnlyChildren.attributesVisible = true; | |
1457 nodePolicyReadOnlyChildren.editable = true; | |
1458 nodePolicyReadOnlyChildren.childrenEditable = false; | |
1459 nodePolicyReadOnlyChildren.upwardNavigation = true; | |
1460 nodePolicyReadOnlyChildren.unrestricted = true; | |
1461 nodePolicyReadOnlyChildren.childPolicy = nodePolicyReadOnly; | |
1462 | |
1463 var nodePolicyOpaque = Object.create(protoNodePolicy); | |
1464 nodePolicyOpaque.toString = function () { return "nodePolicyOpaque"; }; | |
1465 nodePolicyOpaque.childrenVisible = true; | |
1466 nodePolicyOpaque.attributesVisible = false; | |
1467 nodePolicyOpaque.editable = false; | |
1468 nodePolicyOpaque.childrenEditable = false; | |
1469 nodePolicyOpaque.upwardNavigation = true; | |
1470 nodePolicyOpaque.unrestricted = false; | |
1471 nodePolicyOpaque.childPolicy = nodePolicyReadOnly; | |
1472 | |
1473 var nodePolicyForeign = Object.create(protoNodePolicy); | |
1474 nodePolicyForeign.toString = function () { return "nodePolicyForeign"; }; | |
1475 nodePolicyForeign.childrenVisible = false; | |
1476 nodePolicyForeign.attributesVisible = false; | |
1477 nodePolicyForeign.editable = false; | |
1478 nodePolicyForeign.childrenEditable = false; | |
1479 nodePolicyForeign.upwardNavigation = false; | |
1480 nodePolicyForeign.unrestricted = false; | |
1481 Object.defineProperty(nodePolicyForeign, "childPolicy", { | |
1482 get: function () { | |
1483 throw new Error("Foreign node childPolicy should never be consulted"); | |
1484 } | |
1485 }); | |
1486 cajaVM.def([ | |
1487 nodePolicyEditable, | |
1488 nodePolicyReadOnly, | |
1489 nodePolicyReadOnlyChildren, | |
1490 nodePolicyOpaque, | |
1491 nodePolicyForeign | |
1492 ]); | |
1375 | 1493 |
1376 /** | 1494 /** |
1377 * Add a tamed document implementation to a Gadget's global scope. | 1495 * Add a tamed document implementation to a Gadget's global scope. |
1378 * | 1496 * |
1379 * Has the side effect of adding the classes "vdoc-container___" and | 1497 * Has the side effect of adding the classes "vdoc-container___" and |
1380 * idSuffix.substring(1) to the containerNode. | 1498 * idSuffix.substring(1) to the containerNode. |
1381 * | 1499 * |
1382 * @param {string} idSuffix a string suffix appended to all node IDs. | 1500 * @param {string} idSuffix a string suffix appended to all node IDs. |
1383 * It should begin with "-" and end with "___". | 1501 * It should begin with "-" and end with "___". |
1384 * @param {Object} uriPolicy an object like <pre>{ | 1502 * @param {Object} uriPolicy an object like <pre>{ |
(...skipping 29 matching lines...) Expand all Loading... | |
1414 'attachDocument arity mismatch: ' + arguments.length); | 1532 'attachDocument arity mismatch: ' + arguments.length); |
1415 } | 1533 } |
1416 if (!optTargetAttributePresets) { | 1534 if (!optTargetAttributePresets) { |
1417 optTargetAttributePresets = { | 1535 optTargetAttributePresets = { |
1418 'default': '_blank', | 1536 'default': '_blank', |
1419 whitelist: [ '_blank', '_self' ] | 1537 whitelist: [ '_blank', '_self' ] |
1420 }; | 1538 }; |
1421 } | 1539 } |
1422 | 1540 |
1423 var domicile = { | 1541 var domicile = { |
1424 isProcessingEvent: false | 1542 // True when we're executing a handler for events like click |
1543 handlingUserAction: false | |
1425 }; | 1544 }; |
1426 var pluginId; | 1545 var pluginId; |
1546 | |
1547 var vdocContainsForeignNodes = false; | |
1427 | 1548 |
1428 containerNode = makeDOMAccessible(containerNode); | 1549 containerNode = makeDOMAccessible(containerNode); |
1429 var document = containerNode.ownerDocument; | 1550 var document = containerNode.ownerDocument; |
1430 document = makeDOMAccessible(document); | 1551 document = makeDOMAccessible(document); |
1431 var docEl = makeDOMAccessible(document.documentElement); | 1552 var docEl = makeDOMAccessible(document.documentElement); |
1432 var bridal = bridalMaker(makeDOMAccessible, document); | 1553 var bridal = bridalMaker(makeDOMAccessible, document); |
1433 | 1554 |
1434 var window = bridalMaker.getWindow(containerNode, makeDOMAccessible); | 1555 var window = bridalMaker.getWindow(containerNode, makeDOMAccessible); |
1435 window = makeDOMAccessible(window); | 1556 window = makeDOMAccessible(window); |
1436 | 1557 |
(...skipping 28 matching lines...) Expand all Loading... | |
1465 } | 1586 } |
1466 | 1587 |
1467 // TODO(kpreid): should elementPolicies be exported in domicile? | 1588 // TODO(kpreid): should elementPolicies be exported in domicile? |
1468 | 1589 |
1469 // On IE, turn <canvas> tags into canvas elements that explorercanvas | 1590 // On IE, turn <canvas> tags into canvas elements that explorercanvas |
1470 // will recognize | 1591 // will recognize |
1471 bridal.initCanvasElements(containerNode); | 1592 bridal.initCanvasElements(containerNode); |
1472 | 1593 |
1473 // The private properties used in TameNodeConf are: | 1594 // The private properties used in TameNodeConf are: |
1474 // feral (feral node) | 1595 // feral (feral node) |
1475 // editable (this node editable) | 1596 // policy (access policy) |
1476 // childrenEditable (this node editable) | |
1477 // Several specifically for TameHTMLDocument. | 1597 // Several specifically for TameHTMLDocument. |
1478 // Furthermore, by virtual of being scoped inside attachDocument, | 1598 // Furthermore, by virtual of being scoped inside attachDocument, |
1479 // TameNodeT also indicates that the object is a node from the *same* | 1599 // TameNodeT also indicates that the object is a node from the *same* |
1480 // virtual document. | 1600 // virtual document. |
1481 // TODO(kpreid): Review how necessary it is to scope this inside | 1601 // TODO(kpreid): Review how necessary it is to scope this inside |
1482 // attachDocument. The issues are: | 1602 // attachDocument. The issues are: |
1483 // * Using authority or types from a different virtual document (check | 1603 // * Using authority or types from a different virtual document (check |
1484 // the things that used to be TameHTMLDocument.doc___ in particular) | 1604 // the things that used to be TameHTMLDocument.doc___ in particular) |
1485 // * Using nodes from a different real document (Domita would seem to | 1605 // * Using nodes from a different real document (Domita would seem to |
1486 // be vulnerable to this?) | 1606 // be vulnerable to this?) |
(...skipping 29 matching lines...) Expand all Loading... | |
1516 delete np(node).proxyHandler; // no longer needed | 1636 delete np(node).proxyHandler; // no longer needed |
1517 | 1637 |
1518 ExpandoProxyHandler.register(proxiedNode, node); | 1638 ExpandoProxyHandler.register(proxiedNode, node); |
1519 TameNodeConf.confide(proxiedNode, node); | 1639 TameNodeConf.confide(proxiedNode, node); |
1520 tamingProxies.set(node, proxiedNode); | 1640 tamingProxies.set(node, proxiedNode); |
1521 | 1641 |
1522 node = proxiedNode; | 1642 node = proxiedNode; |
1523 } | 1643 } |
1524 | 1644 |
1525 if (feral) { | 1645 if (feral) { |
1526 if (node.nodeType === 1) { | 1646 if (feral.nodeType === 1) { |
1527 // Elements must only be tamed once; to do otherwise would be | 1647 // Elements must only be tamed once; to do otherwise would be |
1528 // a bug in Domado. | 1648 // a bug in Domado. |
1529 taming.tamesTo(feral, node); | 1649 taming.tamesTo(feral, node); |
1530 } else { | 1650 } else { |
1531 // Other node types are tamed every time they are encountered; | 1651 // Other node types are tamed every time they are encountered; |
1532 // we simply remember the latest taming here. | 1652 // we simply remember the latest taming here. |
1533 taming.reTamesTo(feral, node); | 1653 taming.reTamesTo(feral, node); |
1534 } | 1654 } |
1535 } else { | 1655 } else { |
1536 // If guest code passes a node of its own with no feral counterpart to | 1656 // If guest code passes a node of its own with no feral counterpart to |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1707 case html4.atype.URI_FRAGMENT: | 1827 case html4.atype.URI_FRAGMENT: |
1708 if (realValue && '#' === realValue.charAt(0)) { | 1828 if (realValue && '#' === realValue.charAt(0)) { |
1709 return unsuffix(realValue, idSuffix, null); | 1829 return unsuffix(realValue, idSuffix, null); |
1710 } else { | 1830 } else { |
1711 return null; | 1831 return null; |
1712 } | 1832 } |
1713 default: | 1833 default: |
1714 return realValue; | 1834 return realValue; |
1715 } | 1835 } |
1716 } | 1836 } |
1717 | |
1718 /** | |
1719 * Undoes some of the changes made by sanitizeHtml, e.g. stripping ID | |
1720 * prefixes. | |
1721 */ | |
1722 function tameInnerHtml(htmlText) { | |
1723 var out = []; | |
1724 innerHtmlTamer(htmlText, out); | |
1725 return out.join(''); | |
1726 } | |
1727 var innerHtmlTamer = html.makeSaxParser({ | |
1728 startTag: function (tagName, attribs, out) { | |
1729 tagName = realToVirtualElementName(tagName); | |
1730 out.push('<', tagName); | |
1731 for (var i = 0; i < attribs.length; i += 2) { | |
1732 var aname = '' + attribs[+i]; | |
1733 var atype = htmlSchema.attribute(tagName, aname).type; | |
1734 var value = attribs[i + 1]; | |
1735 if (aname !== 'target' && atype !== void 0) { | |
1736 value = virtualizeAttributeValue(atype, value); | |
1737 if (typeof value === 'string') { | |
1738 out.push(' ', aname, '="', html.escapeAttrib(value), '"'); | |
1739 } | |
1740 } else if (cajaPrefRe.test(aname)) { | |
1741 out.push(' ', aname.substring(cajaPrefix.length), '="', | |
1742 html.escapeAttrib(value), '"'); | |
1743 } | |
1744 } | |
1745 out.push('>'); | |
1746 }, | |
1747 endTag: function (tagName, out) { | |
1748 var rempty = htmlSchema.element(tagName).empty; | |
1749 tagName = realToVirtualElementName(tagName); | |
1750 var vempty = htmlSchema.element(tagName).empty; | |
1751 if (vempty && !rempty) { | |
1752 // omit end tag because the browser doesn't see the virtualized | |
1753 // element as empty | |
1754 return; | |
1755 } | |
1756 out.push('</', tagName, '>'); | |
1757 }, | |
1758 pcdata: function (text, out) { out.push(text); }, | |
1759 rcdata: function (text, out) { out.push(text); }, | |
1760 cdata: function (text, out) { out.push(text); } | |
1761 }); | |
1762 | 1837 |
1763 function getSafeTargetAttribute(tagName, attribName, value) { | 1838 function getSafeTargetAttribute(tagName, attribName, value) { |
1764 if (value !== null) { | 1839 if (value !== null) { |
1765 value = String(value); | 1840 value = String(value); |
1766 for (var i = 0; i < optTargetAttributePresets.whitelist.length; ++i) { | 1841 for (var i = 0; i < optTargetAttributePresets.whitelist.length; ++i) { |
1767 if (optTargetAttributePresets.whitelist[i] === value) { | 1842 if (optTargetAttributePresets.whitelist[i] === value) { |
1768 return value; | 1843 return value; |
1769 } | 1844 } |
1770 } | 1845 } |
1771 } | 1846 } |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1904 css.push(propName + ' : ' + propValue); | 1979 css.push(propName + ' : ' + propValue); |
1905 } | 1980 } |
1906 return css.join(' ; '); | 1981 return css.join(' ; '); |
1907 // Frames are ambient, so disallow reference. | 1982 // Frames are ambient, so disallow reference. |
1908 case html4.atype.FRAME_TARGET: | 1983 case html4.atype.FRAME_TARGET: |
1909 return getSafeTargetAttribute(tagName, attribName, value); | 1984 return getSafeTargetAttribute(tagName, attribName, value); |
1910 default: | 1985 default: |
1911 return null; | 1986 return null; |
1912 } | 1987 } |
1913 } | 1988 } |
1989 ······ | |
1990 // Implementation of HTML5 "HTML fragment serialization algorithm" | |
1991 // per http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end .html#html-fragment-serialization-algorithm | |
1992 // as of 2012-09-11. | |
1993 // | |
1994 // Per HTML5: "Warning! It is possible that the output of this algorithm, | |
1995 // if parsed with an HTML parser, will not return the original tree | |
1996 // structure." Therefore, an innerHTML round-trip on a safe (from Caja's | |
1997 // perspective) but malicious DOM may be able to attack guest code. | |
1998 // TODO(kpreid): Evaluate desirability of prohibiting the worst cases of | |
1999 // this in our DOM mutators. | |
2000 function htmlFragmentSerialization(tameRoot) { | |
2001 tameRoot = TameNodeT.coerce(tameRoot); | |
2002 var sl = []; | |
2003 | |
2004 // Note: This algorithm is implemented in terms of tame nodes, not | |
2005 // feral nodes; therefore, it requires no access checks as it yields | |
2006 // only information which clients can obtain by object access. | |
2007 function recur(tameParent) { | |
2008 var nodes = tameParent.childNodes; | |
2009 var nNodes = nodes.length; | |
2010 for (var i = 0; i < nNodes; i++) { | |
2011 var tameCurrent = nodes.item(i); | |
2012 switch (tameCurrent.nodeType) { | |
2013 case 1: // Element | |
2014 // TODO(kpreid): namespace issues | |
2015 var tagName = tameCurrent.tagName; | |
2016 if (tagName === undefined) { | |
2017 // foreign node case | |
2018 continue; | |
2019 } | |
2020 tagName = tagName.toLowerCase(); | |
2021 // TODO(kpreid): not conformant | |
2022 sl.push('<', tagName); | |
2023 var attrs = tameCurrent.attributes; | |
2024 var nAttrs = attrs.length; | |
2025 for (var j = 0; j < nAttrs; j++) { | |
2026 var attr = attrs.item(j); | |
2027 var aName = attr.name; | |
2028 if (aName === 'target') { | |
2029 // hide Caja-added link target attributes | |
2030 // TODO(kpreid): Shouldn't these be hidden in the attributes | |
2031 // list? This special case (and the one below) is emulating | |
2032 // tested-for behavior in a previous .innerHTML | |
2033 // implementation, not written from first principles. | |
2034 continue; | |
2035 } | |
2036 var aValue = attr.value; | |
2037 if (aValue === null) { | |
2038 // rejected by virtualizeAttributeValue | |
2039 // TODO(kpreid): Shouldn't these be hidden in the attributes | |
2040 // list? | |
2041 continue; | |
2042 } | |
2043 // TODO(kpreid): check escapeAttrib conformance | |
2044 sl.push(' ', attr.name, '="', html.escapeAttrib(aValue), '"'); | |
2045 } | |
2046 sl.push('>'); | |
2047 switch (tagName) { | |
2048 case 'area': | |
2049 case 'base': | |
2050 case 'basefont': | |
2051 case 'bgsound': | |
2052 case 'br': | |
2053 case 'col': | |
2054 case 'command': | |
2055 case 'embed': | |
2056 case 'frame': | |
2057 case 'hr': | |
2058 case 'img': | |
2059 case 'input': | |
2060 case 'keygen': | |
2061 case 'link': | |
2062 case 'meta': | |
2063 case 'param': | |
2064 case 'source': | |
2065 case 'track': | |
2066 case 'wbr': | |
2067 // do nothing | |
2068 break; | |
2069 case 'pre': | |
2070 case 'textarea': | |
2071 case 'listing': | |
2072 if (tameCurrent.firstChild && | |
2073 tameCurrent.firstChild.nodeType === 3 && | |
2074 tameCurrent.firstChild.data[0] === '\n') { | |
2075 sl.push('\n'); | |
2076 } | |
2077 // fallthrough | |
2078 default: | |
2079 recur(tameCurrent); | |
2080 sl.push('</', tagName, '>'); | |
2081 } | |
2082 break; | |
2083 case 3: // Text | |
2084 switch (tameCurrent.parentNode.tagName.toLowerCase()) { | |
2085 // TODO(kpreid): namespace | |
2086 case 'style': | |
2087 case 'script': | |
2088 case 'xmp': | |
2089 case 'iframe': | |
2090 case 'noembed': | |
2091 case 'noframes': | |
2092 case 'plaintext': | |
2093 case 'noscript': | |
2094 sl.push(tameCurrent.data); | |
2095 break; | |
2096 default: | |
2097 sl.push(html.escapeAttrib(tameCurrent.data)); | |
2098 break; | |
2099 } | |
2100 break; | |
2101 case 8: // Comment | |
2102 sl.push('<', '!--', tameCurrent.data, '-->'); | |
2103 break; | |
2104 case 7: // ProcessingInstruction | |
2105 sl.push('<?', tameCurrent.target, ' ', tameCurrent.data, '>'); | |
2106 break; | |
2107 case 10: // DocumentType | |
2108 sl.push('<', '!DOCTYPE ', tameCurrent.name, '>'); | |
2109 break; | |
2110 default: | |
2111 if (typeof console !== 'undefined') { | |
2112 console.error('Domado internal: HTML fragment serialization ' | |
2113 + 'algorithm met unexpected node type ' | |
2114 + tameCurrent.nodeType); | |
2115 } | |
2116 break; | |
2117 } | |
2118 } | |
2119 } | |
2120 recur(tameRoot); | |
2121 | |
2122 return sl.join(''); | |
2123 } | |
1914 | 2124 |
1915 // Property descriptors which are independent of any feral object. | 2125 // Property descriptors which are independent of any feral object. |
1916 /** | 2126 /** |
1917 * Property descriptor which throws on access. | 2127 * Property descriptor which throws on access. |
1918 */ | 2128 */ |
1919 var P_UNIMPLEMENTED = { | 2129 var P_UNIMPLEMENTED = { |
1920 enumerable: true, | 2130 enumerable: true, |
1921 get: cajaVM.def(function () { | 2131 get: cajaVM.def(function () { |
1922 throw new Error('Not implemented'); | 2132 throw new Error('Not implemented'); |
1923 }) | 2133 }) |
1924 }; | 2134 }; |
1925 /** | 2135 /** |
1926 * Property descriptor for an unsettable constant attribute (like DOM | 2136 * Property descriptor for an unsettable constant attribute (like DOM |
1927 * attributes such as nodeType). | 2137 * attributes such as nodeType). |
1928 */ | 2138 */ |
1929 function P_constant(value) { | 2139 function P_constant(value) { |
1930 return { enumerable: true, value: value }; | 2140 return { enumerable: true, value: value }; |
1931 } | 2141 } |
1932 | 2142 |
1933 /** | 2143 /** |
1934 * Construct property descriptors suitable for taming objects which use | 2144 * Construct property descriptors suitable for taming objects which use |
1935 * the specified confidence, such that confidence.p(obj).feral is the | 2145 * the specified confidence, such that confidence.p(obj).feral is the |
1936 * feral object to forward to and confidence.p(obj).editable is an | 2146 * feral object to forward to and confidence.p(obj).policy is a node |
1937 * editable/readonly flag. | 2147 * policy object for writability decisions. |
1938 * | 2148 * |
1939 * Lowercase properties are property descriptors; uppercase ones are | 2149 * Lowercase properties are property descriptors; uppercase ones are |
1940 * constructors for parameterized property descriptors. | 2150 * constructors for parameterized property descriptors. |
1941 */ | 2151 */ |
1942 function PropertyTaming(confidence) { | 2152 function PropertyTaming(confidence) { |
1943 var p = confidence.p; | 2153 var p = confidence.p; |
1944 var method = confidence.protectMethod; | 2154 var method = confidence.protectMethod; |
1945 | 2155 |
1946 return cajaVM.def({ | 2156 return cajaVM.def({ |
1947 /** | 2157 /** |
(...skipping 12 matching lines...) Expand all Loading... | |
1960 * Property descriptor for properties which have the value the feral | 2170 * Property descriptor for properties which have the value the feral |
1961 * object does, and are assignable if the wrapper is editable. | 2171 * object does, and are assignable if the wrapper is editable. |
1962 */ | 2172 */ |
1963 rw: { | 2173 rw: { |
1964 enumerable: true, | 2174 enumerable: true, |
1965 extendedAccessors: true, | 2175 extendedAccessors: true, |
1966 get: method(function (prop) { | 2176 get: method(function (prop) { |
1967 return p(this).feral[prop]; | 2177 return p(this).feral[prop]; |
1968 }), | 2178 }), |
1969 set: method(function (value, prop) { | 2179 set: method(function (value, prop) { |
1970 if (!p(this).editable) { throw new Error(NOT_EDITABLE); } | 2180 var privates = p(this); |
1971 p(this).feral[prop] = value; | 2181 privates.policy.requireEditable(); |
2182 privates.feral[prop] = value; | |
1972 }) | 2183 }) |
1973 }, | 2184 }, |
1974 | 2185 |
1975 /** | 2186 /** |
1976 * Property descriptor for properties which have the value the feral | 2187 * Property descriptor for properties which have the value the feral |
1977 * object does, and are assignable (with a predicate restricting the | 2188 * object does, and are assignable (with a predicate restricting the |
1978 * values which may be assigned) if the wrapper is editable. | 2189 * values which may be assigned) if the wrapper is editable. |
1979 * TODO(kpreid): Use guards instead of predicates. | 2190 * TODO(kpreid): Use guards instead of predicates. |
1980 */ | 2191 */ |
1981 RWCond: function (predicate) { | 2192 RWCond: function (predicate) { |
1982 return { | 2193 return { |
1983 enumerable: true, | 2194 enumerable: true, |
1984 extendedAccessors: true, | 2195 extendedAccessors: true, |
1985 get: method(function (prop) { | 2196 get: method(function (prop) { |
1986 return p(this).feral[prop]; | 2197 return p(this).feral[prop]; |
1987 }), | 2198 }), |
1988 set: method(function (value, prop) { | 2199 set: method(function (value, prop) { |
1989 var privates = p(this); | 2200 var privates = p(this); |
1990 if (!privates.editable) { throw new Error(NOT_EDITABLE); } | 2201 privates.policy.requireEditable(); |
1991 if (predicate(value)) { | 2202 if (predicate(value)) { |
1992 privates.feral[prop] = value; | 2203 privates.feral[prop] = value; |
1993 } | 2204 } |
1994 }) | 2205 }) |
1995 }; | 2206 }; |
1996 }, | 2207 }, |
1997 | 2208 |
1998 /** | 2209 /** |
1999 * Property descriptor for properties which have a different name | 2210 * Property descriptor for properties which have a different name |
2000 * than what they map to (e.g. labelElement.htmlFor vs. | 2211 * than what they map to (e.g. labelElement.htmlFor vs. |
(...skipping 15 matching lines...) Expand all Loading... | |
2016 }, | 2227 }, |
2017 | 2228 |
2018 /** | 2229 /** |
2019 * Property descriptor for forwarded properties which have node values | 2230 * Property descriptor for forwarded properties which have node values |
2020 * which may be nodes that might be outside of the virtual document. | 2231 * which may be nodes that might be outside of the virtual document. |
2021 */ | 2232 */ |
2022 related: { | 2233 related: { |
2023 enumerable: true, | 2234 enumerable: true, |
2024 extendedAccessors: true, | 2235 extendedAccessors: true, |
2025 get: method(function (prop) { | 2236 get: method(function (prop) { |
2026 if (!('editable' in p(this))) { | 2237 var privates = p(this); |
2027 throw new Error( | 2238 if (privates.policy.upwardNavigation) { |
2028 "Internal error: related property tamer can only" | 2239 // TODO(kpreid): Can we move this check *into* tameRelatedNode? |
2029 + " be applied to objects with an editable flag"); | 2240 return tameRelatedNode(privates.feral[prop], defaultTameNode); |
2241 } else { | |
2242 return null; | |
2030 } | 2243 } |
2031 return tameRelatedNode(p(this).feral[prop], | |
2032 p(this).editable, | |
2033 defaultTameNode); | |
2034 }) | 2244 }) |
2035 }, | 2245 }, |
2036 | 2246 |
2037 /** | 2247 /** |
2038 * Property descriptor which maps to an attribute or property, is | 2248 * Property descriptor which maps to an attribute or property, is |
2039 * assignable, and has the value transformed in some way. | 2249 * assignable, and has the value transformed in some way. |
2040 * @param {boolean} useAttrGetter true if the getter should delegate | 2250 * @param {boolean} useAttrGetter true if the getter should delegate |
2041 * to {@code this.getAttribute}. That method is assumed to | 2251 * to {@code this.getAttribute}. That method is assumed to |
2042 * already be trusted though {@code toValue} is still called on | 2252 * already be trusted though {@code toValue} is still called on |
2043 * the result. | 2253 * the result. |
(...skipping 25 matching lines...) Expand all Loading... | |
2069 : method(function (name) { | 2279 : method(function (name) { |
2070 return toValue.call(this, p(this).feral[name]); | 2280 return toValue.call(this, p(this).feral[name]); |
2071 }) | 2281 }) |
2072 }; | 2282 }; |
2073 if (fromValue) { | 2283 if (fromValue) { |
2074 desc.set = useAttrSetter | 2284 desc.set = useAttrSetter |
2075 ? method(function (value, name) { | 2285 ? method(function (value, name) { |
2076 this.setAttribute(name, fromValue.call(this, value)); | 2286 this.setAttribute(name, fromValue.call(this, value)); |
2077 }) | 2287 }) |
2078 : method(function (value, name) { | 2288 : method(function (value, name) { |
2079 if (!p(this).editable) { throw new Error(NOT_EDITABLE); } | 2289 var privates = p(this); |
2080 p(this).feral[name] = fromValue.call(this, value); | 2290 privates.policy.requireEditable(); |
2291 privates.feral[name] = fromValue.call(this, value); | |
2081 }); | 2292 }); |
2082 } | 2293 } |
2083 return desc; | 2294 return desc; |
2084 }, | 2295 }, |
2085 filterAttr: function(toValue, fromValue) { | 2296 filterAttr: function(toValue, fromValue) { |
2086 return NP.filter(true, toValue, true, fromValue); | 2297 return NP.filter(true, toValue, true, fromValue); |
2087 }, | 2298 }, |
2088 filterProp: function(toValue, fromValue) { | 2299 filterProp: function(toValue, fromValue) { |
2089 return NP.filter(false, toValue, false, fromValue); | 2300 return NP.filter(false, toValue, false, fromValue); |
2090 } | 2301 } |
2091 }); | 2302 }); |
2092 } | 2303 } |
2093 cajaVM.def(PropertyTaming); // and its prototype | 2304 cajaVM.def(PropertyTaming); // and its prototype |
2094 | 2305 |
2095 // TODO(kpreid): We have completely unrelated things called 'np' and 'NP'. | 2306 // TODO(kpreid): We have completely unrelated things called 'np' and 'NP'. |
2096 var NP = new PropertyTaming(TameNodeConf); | 2307 var NP = new PropertyTaming(TameNodeConf); |
2097 | 2308 |
2098 // Node-specific property accessors: | 2309 // Node-specific property accessors: |
2099 /** | 2310 /** |
2100 * Property descriptor for forwarded properties which have node values | 2311 * Property descriptor for forwarded properties which have node values |
2101 * and are descendants of this node. | 2312 * and are descendants of this node. |
2102 */ | 2313 */ |
2103 var NP_tameDescendant = { | 2314 var NP_tameDescendant = { |
2104 enumerable: true, | 2315 enumerable: true, |
2105 extendedAccessors: true, | 2316 extendedAccessors: true, |
2106 get: nodeMethod(function (prop) { | 2317 get: nodeMethod(function (prop) { |
2107 return defaultTameNode(np(this).feral[prop], | 2318 var privates = np(this); |
2108 np(this).childrenEditable); | 2319 if (privates.policy.childrenVisible) { |
2320 return defaultTameNode(np(this).feral[prop]); | |
2321 } else { | |
2322 return null; | |
2323 } | |
2109 }) | 2324 }) |
2110 }; | 2325 }; |
2111 | 2326 |
2112 var nodeExpandos = new WeakMap(true); | 2327 var nodeExpandos = new WeakMap(true); |
2113 /** | 2328 /** |
2114 * Return the object which stores expando properties for a given | 2329 * Return the object which stores expando properties for a given |
2115 * host DOM node. | 2330 * host DOM node. |
2116 */ | 2331 */ |
2117 function getNodeExpandoStorage(node) { | 2332 function getNodeExpandoStorage(node) { |
2118 var s = nodeExpandos.get(node); | 2333 var s = nodeExpandos.get(node); |
2119 if (s === undefined) { | 2334 if (s === undefined) { |
2120 nodeExpandos.set(node, s = {}); | 2335 nodeExpandos.set(node, s = {}); |
2121 } | 2336 } |
2122 return s; | 2337 return s; |
2123 } | 2338 } |
2124 ······ | 2339 ······ |
2125 var nodeClassNoImplWarnings = {}; | 2340 var nodeClassNoImplWarnings = {}; |
2126 | 2341 |
2127 function makeTameNodeByType(node, editable) { | 2342 function makeTameNodeByType(node) { |
2128 switch (node.nodeType) { | 2343 switch (node.nodeType) { |
2129 case 1: // Element | 2344 case 1: // Element |
2130 var tagName = node.tagName.toLowerCase(); | 2345 var tagName = node.tagName.toLowerCase(); |
2131 var schemaElem = htmlSchema.element(tagName); | 2346 var schemaElem = htmlSchema.element(tagName); |
2132 if (schemaElem.allowed || tagName === 'script') { | 2347 if (schemaElem.allowed || tagName === 'script') { |
2133 // <script> is specifically allowed because we make provisions | 2348 // <script> is specifically allowed because we make provisions |
2134 // for controlling its content and src. | 2349 // for controlling its content and src. |
2135 var domInterfaceName = schemaElem.domInterface; | 2350 var domInterfaceName = schemaElem.domInterface; |
2136 if (nodeTamers.hasOwnProperty(domInterfaceName)) { | 2351 if (nodeTamers.hasOwnProperty(domInterfaceName)) { |
2137 return new nodeTamers[domInterfaceName]( | 2352 return new nodeTamers[domInterfaceName](node); |
2138 node, editable, editable); | |
2139 } else { | 2353 } else { |
2140 if (!nodeClassNoImplWarnings[domInterfaceName]) { | 2354 if (!nodeClassNoImplWarnings[domInterfaceName]) { |
2141 nodeClassNoImplWarnings[domInterfaceName] = true; | 2355 nodeClassNoImplWarnings[domInterfaceName] = true; |
2142 if (typeof console !== 'undefined') { | 2356 if (typeof console !== 'undefined') { |
2143 console.warn("Domado: " + domInterfaceName + " is not " + | 2357 console.warn("Domado: " + domInterfaceName + " is not " + |
2144 "tamed; its specific properties/methods will not be " + | 2358 "tamed; its specific properties/methods will not be " + |
2145 "available on <" + | 2359 "available on <" + |
2146 htmlSchema.realToVirtualElementName(tagName) + ">."); | 2360 htmlSchema.realToVirtualElementName(tagName) + ">."); |
2147 } | 2361 } |
2148 } | 2362 } |
2149 return new TameElement(node, editable, editable); | 2363 return new TameElement(node); |
2150 } | 2364 } |
2151 } else { | 2365 } else { |
2152 // If an unrecognized or unsafe node, return a | 2366 // If an unrecognized or unsafe node, return a |
2153 // placeholder that doesn't prevent tree navigation, | 2367 // placeholder that doesn't prevent tree navigation, |
2154 // but that doesn't allow mutation or leak attribute | 2368 // but that doesn't allow mutation or leak attribute |
2155 // information. | 2369 // information. |
2156 return new TameOpaqueNode(node, editable); | 2370 return new TameOpaqueNode(node); |
2157 } | 2371 } |
2158 case 2: // Attr | 2372 case 2: // Attr |
2159 // Cannot generically wrap since we must have access to the | 2373 // Cannot generically wrap since we must have access to the |
2160 // owner element | 2374 // owner element |
2161 throw 'Internal: Attr nodes cannot be generically wrapped'; | 2375 throw 'Internal: Attr nodes cannot be generically wrapped'; |
2162 case 3: // Text | 2376 case 3: // Text |
2163 case 4: // CDATA Section Node | 2377 case 4: // CDATA Section Node |
2164 return new TameTextNode(node, editable); | 2378 return new TameTextNode(node); |
2165 case 8: // Comment | 2379 case 8: // Comment |
2166 return new TameCommentNode(node, editable); | 2380 return new TameCommentNode(node); |
2167 case 11: // Document Fragment | 2381 case 11: // Document Fragment |
2168 return new TameBackedNode(node, editable, editable); | 2382 return new TameBackedNode(node); |
2169 default: | 2383 default: |
2170 return new TameOpaqueNode(node, editable); | 2384 return new TameOpaqueNode(node); |
2171 } | 2385 } |
2172 } | 2386 } |
2173 | 2387 |
2174 /** | 2388 /** |
2175 * returns a tame DOM node. | 2389 * returns a tame DOM node. |
2176 * @param {Node} node | 2390 * @param {Node} node |
2177 * @param {boolean} editable | 2391 * @param {boolean} foreign |
2178 * @see <a href="http://www.w3.org/TR/DOM-Level-2-HTML/html.html" | 2392 * @see <a href="http://www.w3.org/TR/DOM-Level-2-HTML/html.html" |
2179 * >DOM Level 2</a> | 2393 * >DOM Level 2</a> |
2180 */ | 2394 */ |
2181 function defaultTameNode(node, editable, foreign) { | 2395 function defaultTameNode(node, foreign) { |
2182 if (node === null || node === void 0) { return null; } | 2396 if (node === null || node === void 0) { return null; } |
2183 node = makeDOMAccessible(node); | 2397 node = makeDOMAccessible(node); |
2184 // TODO(mikesamuel): make sure it really is a DOM node | 2398 // TODO(mikesamuel): make sure it really is a DOM node |
2185 | 2399 |
2186 if (taming.hasTameTwin(node)) { | 2400 if (taming.hasTameTwin(node)) { |
2187 return taming.tame(node); | 2401 return taming.tame(node); |
2188 } | 2402 } |
2189 | 2403 |
2404 if (foreign) { | |
2405 vdocContainsForeignNodes = true; | |
2406 } | |
2407 | |
2190 var tamed = foreign | 2408 var tamed = foreign |
2191 ? new TameForeignNode(node, editable) | 2409 ? new TameForeignNode(node) |
2192 : makeTameNodeByType(node, editable); | 2410 : makeTameNodeByType(node); |
2193 tamed = finishNode(tamed); | 2411 tamed = finishNode(tamed); |
2194 | 2412 |
2195 return tamed; | 2413 return tamed; |
2196 } | 2414 } |
2197 | 2415 |
2198 function tameRelatedNode(node, editable, tameNodeCtor) { | 2416 function tameRelatedNode(node, tameNodeCtor) { |
2199 if (node === null || node === void 0) { return null; } | 2417 if (node === null || node === void 0) { return null; } |
2200 if (node === np(tameDocument).feralContainerNode) { | 2418 if (node === np(tameDocument).feralContainerNode) { |
2201 if (np(tameDocument).editable && !editable) { | |
2202 // FIXME: return a non-editable version instead | |
2203 throw new Error(NOT_EDITABLE); | |
2204 } | |
2205 return tameDocument; | 2419 return tameDocument; |
2206 } | 2420 } |
2207 | 2421 |
2208 node = makeDOMAccessible(node); | 2422 node = makeDOMAccessible(node); |
2209 | 2423 |
2210 // Catch errors because node might be from a different domain. | 2424 // Catch errors because node might be from a different domain. |
2211 try { | 2425 try { |
2212 var docElem = node.ownerDocument.documentElement; | 2426 var docElem = node.ownerDocument.documentElement; |
2213 for (var ancestor = node; | 2427 for (var ancestor = node; |
2214 ancestor; | 2428 ancestor; |
2215 ancestor = makeDOMAccessible(ancestor.parentNode)) { | 2429 ancestor = makeDOMAccessible(ancestor.parentNode)) { |
2216 if (idClassPattern.test(ancestor.className)) { | 2430 if (idClassPattern.test(ancestor.className)) { |
2217 return tameNodeCtor(node, editable); | 2431 return tameNodeCtor(node); |
2218 } else if (ancestor === docElem) { | 2432 } else if (ancestor === docElem) { |
2219 return null; | 2433 return null; |
2220 } | 2434 } |
2221 } | 2435 } |
2222 return tameNodeCtor(node, editable); | 2436 return tameNodeCtor(node); |
2223 } catch (e) {} | 2437 } catch (e) {} |
2224 return null; | 2438 return null; |
2225 } | 2439 } |
2226 | 2440 |
2227 domicile.tameNodeAsForeign = function(node) { | 2441 domicile.tameNodeAsForeign = function(node) { |
2228 return defaultTameNode(node, true, true); | 2442 return defaultTameNode(node, true); |
2229 }; | 2443 }; |
2230 | 2444 |
2231 /** | 2445 /** |
2232 * Returns the length of a raw DOM Nodelist object, working around | 2446 * Returns the length of a raw DOM Nodelist object, working around |
2233 * NamedNodeMap bugs in IE, Opera, and Safari as discussed at | 2447 * NamedNodeMap bugs in IE, Opera, and Safari as discussed at |
2234 * http://code.google.com/p/google-caja/issues/detail?id=935 | 2448 * http://code.google.com/p/google-caja/issues/detail?id=935 |
2235 * | 2449 * |
2236 * @param nodeList a DOM NodeList. | 2450 * @param nodeList a DOM NodeList. |
2237 * | 2451 * |
2238 * @return the number of nodes in the NodeList. | 2452 * @return the number of nodes in the NodeList. |
2239 */ | 2453 */ |
2240 function getNodeListLength(nodeList) { | 2454 function getNodeListLength(nodeList) { |
2241 var limit = nodeList.length; | 2455 var limit = nodeList.length; |
2242 if (limit !== +limit) { limit = 1/0; } | 2456 if (limit !== +limit) { limit = 1/0; } |
2243 return limit; | 2457 return limit; |
2244 } | 2458 } |
2245 | 2459 |
2460 function nodeListEqualsArray(nodeList, array) { | |
2461 var nll = getNodeListLength(nodeList); | |
2462 if (nll !== array.length) { | |
2463 return false; | |
2464 } else { | |
2465 for (var i = 0; i < nll; i++) { | |
2466 if (nodeList[i] !== array[i]) { | |
2467 return false; | |
2468 } | |
2469 } | |
2470 return true; | |
2471 } | |
2472 } | |
2473 | |
2474 // Commentary on foreign node children in NodeLists: | |
2475 // | |
2476 // The children of a foreign node are an implementation detail which | |
2477 // guest code should not be permitted to see. Therefore, we must hide them | |
2478 // from appearing in NodeLists. This would be a straightforward matter of | |
2479 // filtering, except that NodeLists are "live", reflecting DOM changes | |
2480 // immediately; and DOM changes change the membership and numeric indexes | |
2481 // of the NodeList. | |
2482 // | |
2483 // One could imagine caching the outcomes: given an index, scan the host | |
2484 // list until the required number of visible-to-guest nodes have been | |
2485 // found, cache the indexes and node, and then validate the cache entry | |
2486 // later by comparing indexes, but that is not sufficient; consider if a | |
2487 // foreign child is deleted, and at the same time a guest-visible node | |
2488 // is added in a similar document position; then the index of a guest node | |
2489 // which is after that position *should* increase, but this cache cannot | |
2490 // tell. | |
2491 // | |
2492 // Therefore, we do cache the list, but we must re-validate the cache | |
2493 // from 0 up to the desired index on every access. | |
2494 /** | |
2495 * This is NOT a node list taming. This is a component for performing | |
2496 * foreign node filtering only. | |
2497 */ | |
2498 function NodeListFilter(feralNodeList) { | |
2499 feralNodeList = makeDOMAccessible(feralNodeList); | |
2500 var expectation = []; | |
2501 var filteredCache = []; | |
2502 | |
2503 function calcUpTo(index) { | |
2504 var feralLength = getNodeListLength(feralNodeList); | |
2505 var feralIndex = 0; | |
2506 | |
2507 // Validate cache | |
2508 if (feralLength < expectation.length) { | |
2509 expectation = []; | |
2510 filteredCache = []; | |
2511 feralIndex = 0; | |
2512 } else { | |
2513 for ( | |
2514 ; | |
2515 feralIndex < expectation.length && feralIndex < feralLength; | |
2516 feralIndex++) { | |
2517 if (feralNodeList[feralIndex] !== expectation[feralIndex]) { | |
2518 expectation = []; | |
2519 filteredCache = []; | |
2520 feralIndex = 0; | |
2521 break; | |
2522 } | |
2523 } | |
2524 } | |
2525 | |
2526 // Extend cache | |
2527 nodeListScan: for ( | |
2528 ; | |
2529 feralIndex < feralLength && filteredCache.length <= index; | |
2530 feralIndex++) { | |
2531 var node = feralNodeList[feralIndex]; | |
2532 expectation.push(node); | |
2533 makeDOMAccessible(node); | |
2534 // Filter out foreign nodes' descendants | |
2535 walkUp: for ( | |
2536 var ancestor = makeDOMAccessible(node.parentNode); | |
2537 ancestor !== null; | |
2538 ancestor = makeDOMAccessible(ancestor.parentNode)) { | |
2539 if (taming.hasTameTwin(ancestor)) { | |
2540 if (taming.tame(ancestor) instanceof TameForeignNode) { | |
2541 // Every foreign node is already tamed as foreign, by | |
2542 // definition. | |
2543 continue nodeListScan; | |
2544 } else { | |
2545 // Reached a node known to be non-foreign. | |
2546 break walkUp; | |
2547 } | |
2548 } | |
2549 } | |
2550 // Not a foreign node descendant, so include it in the list. | |
2551 filteredCache.push(node); | |
2552 } | |
2553 } | |
2554 return cajaVM.def({ | |
2555 getLength: function() { | |
2556 if (vdocContainsForeignNodes) { | |
2557 calcUpTo(Infinity); | |
2558 return filteredCache.length; | |
2559 } else { | |
2560 return getNodeListLength(feralNodeList); | |
2561 } | |
2562 }, | |
2563 item: function(i) { | |
2564 if (vdocContainsForeignNodes) { | |
2565 calcUpTo(i); | |
2566 return filteredCache[i]; | |
2567 } else { | |
2568 return feralNodeList[i]; | |
2569 } | |
2570 } | |
2571 }); | |
2572 } | |
2573 | |
2246 /** | 2574 /** |
2247 * Constructs a NodeList-like object. | 2575 * Constructs a NodeList-like object. |
2248 * | 2576 * |
2249 * @param tamed a JavaScript array that will be populated and decorated | 2577 * @param tamed a JavaScript array that will be populated and decorated |
2250 * with the DOM NodeList API. If it has existing elements they will | 2578 * with the DOM NodeList API. If it has existing elements they will |
2251 * precede the actual NodeList elements. | 2579 * precede the actual NodeList elements. |
2252 * @param nodeList an array-like object supporting a "length" property | 2580 * @param nodeList an array-like object supporting a "length" property |
2253 * and "[]" numeric indexing, or a raw DOM NodeList; | 2581 * and "[]" numeric indexing, or a raw DOM NodeList; |
2254 * @param editable whether the tame nodes wrapped by this object | |
2255 * should permit editing. | |
2256 * @param opt_tameNodeCtor a function for constructing tame nodes | 2582 * @param opt_tameNodeCtor a function for constructing tame nodes |
2257 * out of raw DOM nodes. | 2583 * out of raw DOM nodes. |
2258 */ | 2584 */ |
2259 function mixinNodeList(tamed, nodeList, editable, opt_tameNodeCtor) { | 2585 function mixinNodeList(tamed, nodeList, opt_tameNodeCtor) { |
2260 // TODO(kpreid): Under a true ES5 environment, node lists should be | 2586 // TODO(kpreid): Under a true ES5 environment, node lists should be |
2261 // proxies so that they preserve liveness of the original lists. | 2587 // proxies so that they preserve liveness of the original lists. |
2262 // This should be controlled by an option. | 2588 // This should be controlled by an option. |
2263 | 2589 // UPDATE: We have live NodeLists as TameNodeList and TameOptionsList. |
2264 var limit = getNodeListLength(nodeList); | 2590 // This is not live, but is used in less-mainstream cases. |
2591 | |
2592 var visibleList = new NodeListFilter(nodeList); | |
2593 | |
2594 var limit = visibleList.getLength(); | |
2265 if (limit > 0 && !opt_tameNodeCtor) { | 2595 if (limit > 0 && !opt_tameNodeCtor) { |
2266 throw 'Internal: Nonempty mixinNodeList() without a tameNodeCtor'; | 2596 throw 'Internal: Nonempty mixinNodeList() without a tameNodeCtor'; |
2267 } | 2597 } |
2268 | 2598 |
2269 for (var i = tamed.length, j = 0; j < limit && nodeList[+j]; ++i, ++j) { | 2599 for (var i = tamed.length, j = 0; |
2270 tamed[+i] = opt_tameNodeCtor(nodeList[+j], editable); | 2600 j < limit && visibleList.item(j); |
2601 ++i, ++j) { | |
2602 tamed[+i] = opt_tameNodeCtor(visibleList.item(+j)); | |
2271 } | 2603 } |
2272 | 2604 |
2273 // Guard against accidental leakage of untamed nodes | 2605 // Guard against accidental leakage of untamed nodes |
2274 nodeList = null; | 2606 nodeList = visibleList = null; |
2275 | 2607 |
2276 tamed.item = cajaVM.def(function (k) { | 2608 tamed.item = cajaVM.def(function (k) { |
2277 k &= 0x7fffffff; | 2609 k &= 0x7fffffff; |
2278 if (k !== k) { throw new Error(); } | 2610 if (k !== k) { throw new Error(); } |
2279 return tamed[+k] || null; | 2611 return tamed[+k] || null; |
2280 }); | 2612 }); |
2281 | 2613 |
2282 return tamed; | 2614 return tamed; |
2283 } | 2615 } |
2284 | 2616 |
2285 function rebuildTameListConstructors(ArrayLike) { | 2617 function rebuildTameListConstructors(ArrayLike) { |
2286 TameNodeList = makeTameNodeList(); | 2618 TameNodeList = makeTameNodeList(); |
2287 TameNodeList.prototype = Object.create(ArrayLike.prototype); | 2619 TameNodeList.prototype = Object.create(ArrayLike.prototype); |
2288 Object.defineProperty(TameNodeList.prototype, 'constructor', | 2620 Object.defineProperty(TameNodeList.prototype, 'constructor', |
2289 { value: TameNodeList }); | 2621 { value: TameNodeList }); |
2290 Object.freeze(TameNodeList.prototype); | 2622 Object.freeze(TameNodeList.prototype); |
2291 Object.freeze(TameNodeList); | 2623 Object.freeze(TameNodeList); |
2292 TameOptionsList = makeTameOptionsList(); | 2624 TameOptionsList = makeTameOptionsList(); |
2293 TameOptionsList.prototype = Object.create(ArrayLike.prototype); | 2625 TameOptionsList.prototype = Object.create(ArrayLike.prototype); |
2294 Object.defineProperty(TameOptionsList.prototype, 'constructor', | 2626 Object.defineProperty(TameOptionsList.prototype, 'constructor', |
2295 { value: TameOptionsList }); | 2627 { value: TameOptionsList }); |
2296 Object.freeze(TameOptionsList.prototype); | 2628 Object.freeze(TameOptionsList.prototype); |
2297 Object.freeze(TameOptionsList); | 2629 Object.freeze(TameOptionsList); |
2298 } | 2630 } |
2299 | 2631 |
2300 function makeTameNodeList() { | 2632 function makeTameNodeList() { |
2301 return function TNL(nodeList, editable, tameNodeCtor) { | 2633 return function TNL(nodeList, tameNodeCtor) { |
2302 nodeList = makeDOMAccessible(nodeList); | 2634 var visibleList = new NodeListFilter(nodeList); |
2303 function getItem(i) { | 2635 function getItem(i) { |
2304 i = +i; | 2636 i = +i; |
2305 if (i >= nodeList.length) { return void 0; } | 2637 if (i >= visibleList.getLength()) { return void 0; } |
2306 return tameNodeCtor(nodeList[i], editable); | 2638 return tameNodeCtor(visibleList.item(i)); |
2307 } | 2639 } |
2308 function getLength() { | 2640 var getLength = visibleList.getLength.bind(visibleList); |
2309 return nodeList.length; | |
2310 } | |
2311 var len = +getLength(); | 2641 var len = +getLength(); |
2312 var ArrayLike = cajaVM.makeArrayLike(len); | 2642 var ArrayLike = cajaVM.makeArrayLike(len); |
2313 if (!(TameNodeList.prototype instanceof ArrayLike)) { | 2643 if (!(TameNodeList.prototype instanceof ArrayLike)) { |
2314 rebuildTameListConstructors(ArrayLike); | 2644 rebuildTameListConstructors(ArrayLike); |
2315 } | 2645 } |
2316 var result = ArrayLike(TameNodeList.prototype, getItem, getLength); | 2646 var result = ArrayLike(TameNodeList.prototype, getItem, getLength); |
2317 Object.defineProperty(result, 'item', | 2647 Object.defineProperty(result, 'item', |
2318 { value: Object.freeze(getItem) }); | 2648 { value: Object.freeze(getItem) }); |
2319 return result; | 2649 return result; |
2320 }; | 2650 }; |
2321 } | 2651 } |
2322 | 2652 |
2323 var TameNodeList = Object.freeze(makeTameNodeList()); | 2653 var TameNodeList = Object.freeze(makeTameNodeList()); |
2324 | 2654 |
2325 function makeTameOptionsList() { | 2655 function makeTameOptionsList() { |
2326 return function TOL(nodeList, editable, opt_tameNodeCtor) { | 2656 return function TOL(nodeList, opt_tameNodeCtor) { |
2327 nodeList = makeDOMAccessible(nodeList); | 2657 var visibleList = new NodeListFilter(nodeList); |
2328 function getItem(i) { | 2658 function getItem(i) { |
2329 i = +i; | 2659 i = +i; |
2330 return opt_tameNodeCtor(nodeList[i], editable); | 2660 return opt_tameNodeCtor(visibleList.item(i)); |
2331 } | 2661 } |
2332 function getLength() { return nodeList.length; } | 2662 var getLength = visibleList.getLength.bind(visibleList); |
2333 var len = +getLength(); | 2663 var len = +getLength(); |
2334 var ArrayLike = cajaVM.makeArrayLike(len); | 2664 var ArrayLike = cajaVM.makeArrayLike(len); |
2335 if (!(TameOptionsList.prototype instanceof ArrayLike)) { | 2665 if (!(TameOptionsList.prototype instanceof ArrayLike)) { |
2336 rebuildTameListConstructors(ArrayLike); | 2666 rebuildTameListConstructors(ArrayLike); |
2337 } | 2667 } |
2338 var result = ArrayLike( | 2668 var result = ArrayLike( |
2339 TameOptionsList.prototype, getItem, getLength); | 2669 TameOptionsList.prototype, getItem, getLength); |
2340 Object.defineProperty(result, 'selectedIndex', { | 2670 Object.defineProperty(result, 'selectedIndex', { |
2341 get: function () { return +nodeList.selectedIndex; } | 2671 get: function () { return +nodeList.selectedIndex; } |
2342 }); | 2672 }); |
(...skipping 14 matching lines...) Expand all Loading... | |
2357 } | 2687 } |
2358 | 2688 |
2359 /** | 2689 /** |
2360 * Constructs an HTMLCollection-like object which indexes its elements | 2690 * Constructs an HTMLCollection-like object which indexes its elements |
2361 * based on their NAME attribute. | 2691 * based on their NAME attribute. |
2362 * | 2692 * |
2363 * @param tamed a JavaScript array that will be populated and decorated | 2693 * @param tamed a JavaScript array that will be populated and decorated |
2364 * with the DOM HTMLCollection API. | 2694 * with the DOM HTMLCollection API. |
2365 * @param nodeList an array-like object supporting a "length" property | 2695 * @param nodeList an array-like object supporting a "length" property |
2366 * and "[]" numeric indexing. | 2696 * and "[]" numeric indexing. |
2367 * @param editable whether the tame nodes wrapped by this object | |
2368 * should permit editing. | |
2369 * @param opt_tameNodeCtor a function for constructing tame nodes | 2697 * @param opt_tameNodeCtor a function for constructing tame nodes |
2370 * out of raw DOM nodes. | 2698 * out of raw DOM nodes. |
2371 * | 2699 * |
2372 * TODO(kpreid): Per | 2700 * TODO(kpreid): Per |
2373 * <http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-75708506> | 2701 * <http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-75708506> |
2374 * this should be looking up ids as well as names. (And not returning | 2702 * this should be looking up ids as well as names. (And not returning |
2375 * nodelists, but is that for compatibility?) | 2703 * nodelists, but is that for compatibility?) |
2376 */ | 2704 */ |
2377 function mixinHTMLCollection(tamed, nodeList, editable, | 2705 function mixinHTMLCollection(tamed, nodeList, opt_tameNodeCtor) { |
2378 opt_tameNodeCtor) { | 2706 mixinNodeList(tamed, nodeList, opt_tameNodeCtor); |
2379 mixinNodeList(tamed, nodeList, editable, opt_tameNodeCtor); | |
2380 | 2707 |
2381 var tameNodesByName = {}; | 2708 var tameNodesByName = {}; |
2382 var tameNode; | 2709 var tameNode; |
2383 | 2710 |
2384 for (var i = 0; i < tamed.length && (tameNode = tamed[+i]); ++i) { | 2711 for (var i = 0; i < tamed.length && (tameNode = tamed[+i]); ++i) { |
2385 var name = void 0; | 2712 var name = void 0; |
2386 if (tameNode.getAttribute) { name = tameNode.getAttribute('name'); } | 2713 if (tameNode.getAttribute) { name = tameNode.getAttribute('name'); } |
2387 if (name && !(name.charAt(name.length - 1) === '_' || (name in tamed) | 2714 if (name && !(name.charAt(name.length - 1) === '_' || (name in tamed) |
2388 || name === String(name & 0x7fffffff))) { | 2715 || name === String(name & 0x7fffffff))) { |
2389 if (!tameNodesByName[name]) { tameNodesByName[name] = []; } | 2716 if (!tameNodesByName[name]) { tameNodesByName[name] = []; } |
(...skipping 18 matching lines...) Expand all Loading... | |
2408 if (Object.prototype.hasOwnProperty.call(tamed, name)) { | 2735 if (Object.prototype.hasOwnProperty.call(tamed, name)) { |
2409 return cajaVM.passesGuard(TameNodeT, tamed[name]) | 2736 return cajaVM.passesGuard(TameNodeT, tamed[name]) |
2410 ? tamed[name] : tamed[name][0]; | 2737 ? tamed[name] : tamed[name][0]; |
2411 } | 2738 } |
2412 return null; | 2739 return null; |
2413 }); | 2740 }); |
2414 | 2741 |
2415 return tamed; | 2742 return tamed; |
2416 } | 2743 } |
2417 | 2744 |
2418 function tameHTMLCollection(nodeList, editable, opt_tameNodeCtor) { | 2745 function tameHTMLCollection(nodeList, opt_tameNodeCtor) { |
2419 return Object.freeze( | 2746 return Object.freeze( |
2420 mixinHTMLCollection([], nodeList, editable, opt_tameNodeCtor)); | 2747 mixinHTMLCollection([], nodeList, opt_tameNodeCtor)); |
2421 } | 2748 } |
2422 | 2749 |
2423 function tameGetElementsByTagName(rootNode, tagName, editable) { | 2750 function tameGetElementsByTagName(rootNode, tagName) { |
2424 tagName = String(tagName); | 2751 tagName = String(tagName); |
2425 var eflags = 0; | 2752 var eflags = 0; |
2426 if (tagName !== '*') { | 2753 if (tagName !== '*') { |
2427 tagName = tagName.toLowerCase(); | 2754 tagName = tagName.toLowerCase(); |
2428 tagName = virtualToRealElementName(tagName); | 2755 tagName = virtualToRealElementName(tagName); |
2429 } | 2756 } |
2430 return new TameNodeList(rootNode.getElementsByTagName(tagName), | 2757 return new TameNodeList(rootNode.getElementsByTagName(tagName), |
2431 editable, defaultTameNode); | 2758 defaultTameNode); |
2432 } | 2759 } |
2433 | 2760 |
2434 /** | 2761 /** |
2435 * Implements http://www.whatwg.org/specs/web-apps/current-work/#dom-docum ent-getelementsbyclassname | 2762 * Implements http://www.whatwg.org/specs/web-apps/current-work/#dom-docum ent-getelementsbyclassname |
2436 * using an existing implementation on browsers that have one. | 2763 * using an existing implementation on browsers that have one. |
2437 */ | 2764 */ |
2438 function tameGetElementsByClassName(rootNode, className, editable) { | 2765 function tameGetElementsByClassName(rootNode, className) { |
2439 className = String(className); | 2766 className = String(className); |
2440 | 2767 |
2441 // The quotes below are taken from the HTML5 draft referenced above. | 2768 // The quotes below are taken from the HTML5 draft referenced above. |
2442 | 2769 |
2443 // "having obtained the classes by splitting a string on spaces" | 2770 // "having obtained the classes by splitting a string on spaces" |
2444 // Instead of using split, we use match with the global modifier so that | 2771 // Instead of using split, we use match with the global modifier so that |
2445 // we don't have to remove leading and trailing spaces. | 2772 // we don't have to remove leading and trailing spaces. |
2446 var classes = className.match(/[^\t\n\f\r ]+/g); | 2773 var classes = className.match(/[^\t\n\f\r ]+/g); |
2447 | 2774 |
2448 // Filter out classnames in the restricted namespace. | 2775 // Filter out classnames in the restricted namespace. |
(...skipping 12 matching lines...) Expand all Loading... | |
2461 // htmlEl.ownerDocument.getElementsByClassName(htmlEl.className) | 2788 // htmlEl.ownerDocument.getElementsByClassName(htmlEl.className) |
2462 // will return an HtmlCollection containing htmlElement iff | 2789 // will return an HtmlCollection containing htmlElement iff |
2463 // htmlEl.className contains a non-space character. | 2790 // htmlEl.className contains a non-space character. |
2464 return fakeNodeList([]); | 2791 return fakeNodeList([]); |
2465 } | 2792 } |
2466 | 2793 |
2467 // "unordered set of unique space-separated tokens representing classes" | 2794 // "unordered set of unique space-separated tokens representing classes" |
2468 if (typeof rootNode.getElementsByClassName === 'function') { | 2795 if (typeof rootNode.getElementsByClassName === 'function') { |
2469 return new TameNodeList( | 2796 return new TameNodeList( |
2470 rootNode.getElementsByClassName( | 2797 rootNode.getElementsByClassName( |
2471 classes.join(' ')), editable, defaultTameNode); | 2798 classes.join(' ')), defaultTameNode); |
2472 } else { | 2799 } else { |
2473 // Add spaces around each class so that we can use indexOf later to | 2800 // Add spaces around each class so that we can use indexOf later to |
2474 // find a match. | 2801 // find a match. |
2475 // This use of indexOf is strictly incorrect since | 2802 // This use of indexOf is strictly incorrect since |
2476 // http://www.whatwg.org/specs/web-apps/current-work/#reflecting-conte nt-attributes-in-dom-attributes | 2803 // http://www.whatwg.org/specs/web-apps/current-work/#reflecting-conte nt-attributes-in-dom-attributes |
2477 // does not normalize spaces in unordered sets of unique | 2804 // does not normalize spaces in unordered sets of unique |
2478 // space-separated tokens. This is not a problem since HTML5 | 2805 // space-separated tokens. This is not a problem since HTML5 |
2479 // compliant implementations already have a getElementsByClassName | 2806 // compliant implementations already have a getElementsByClassName |
2480 // implementation, and legacy | 2807 // implementation, and legacy |
2481 // implementations do normalize according to comments on issue 935. | 2808 // implementations do normalize according to comments on issue 935. |
(...skipping 17 matching lines...) Expand all Loading... | |
2499 candidate_loop: | 2826 candidate_loop: |
2500 for (var j = 0, candidate, k = -1; | 2827 for (var j = 0, candidate, k = -1; |
2501 j < limit && (candidate = candidates[+j]); | 2828 j < limit && (candidate = candidates[+j]); |
2502 ++j) { | 2829 ++j) { |
2503 var candidateClass = ' ' + candidate.className + ' '; | 2830 var candidateClass = ' ' + candidate.className + ' '; |
2504 for (var i = nClasses; --i >= 0;) { | 2831 for (var i = nClasses; --i >= 0;) { |
2505 if (-1 === candidateClass.indexOf(classes[+i])) { | 2832 if (-1 === candidateClass.indexOf(classes[+i])) { |
2506 continue candidate_loop; | 2833 continue candidate_loop; |
2507 } | 2834 } |
2508 } | 2835 } |
2509 var tamed = defaultTameNode(candidate, editable); | 2836 var tamed = defaultTameNode(candidate); |
2510 if (tamed) { | 2837 if (tamed) { |
2511 matches[++k] = tamed; | 2838 matches[++k] = tamed; |
2512 } | 2839 } |
2513 } | 2840 } |
2514 // "the method must return a live NodeList object" | 2841 // "the method must return a live NodeList object" |
2515 return fakeNodeList(matches); | 2842 return fakeNodeList(matches); |
2516 } | 2843 } |
2517 } | 2844 } |
2518 | 2845 |
2519 function makeEventHandlerWrapper(thisNode, listener) { | 2846 function makeEventHandlerWrapper(thisNode, listener) { |
2520 domitaModules.ensureValidCallback(listener); | 2847 domitaModules.ensureValidCallback(listener); |
2521 function wrapper(event) { | 2848 function wrapper(event) { |
2522 return plugin_dispatchEvent( | 2849 return plugin_dispatchEvent( |
2523 thisNode, event, rulebreaker.getId(tameWindow), listener); | 2850 thisNode, event, rulebreaker.getId(tameWindow), listener); |
2524 } | 2851 } |
2525 return wrapper; | 2852 return wrapper; |
2526 } | 2853 } |
2527 | 2854 |
2528 var NOT_EDITABLE = "Node not editable."; | |
2529 var INDEX_SIZE_ERROR = "Index size error."; | |
2530 | |
2531 // Implementation of EventTarget::addEventListener | 2855 // Implementation of EventTarget::addEventListener |
2532 function tameAddEventListener(name, listener, useCapture) { | 2856 function tameAddEventListener(name, listener, useCapture) { |
2533 var feral = np(this).feral; | 2857 var feral = np(this).feral; |
2534 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 2858 np(this).policy.requireEditable(); |
2535 if (!np(this).wrappedListeners) { np(this).wrappedListeners = []; } | 2859 if (!np(this).wrappedListeners) { np(this).wrappedListeners = []; } |
2536 useCapture = Boolean(useCapture); | 2860 useCapture = Boolean(useCapture); |
2537 var wrappedListener = makeEventHandlerWrapper(np(this).feral, listener); | 2861 var wrappedListener = makeEventHandlerWrapper(np(this).feral, listener); |
2538 wrappedListener = bridal.addEventListener( | 2862 wrappedListener = bridal.addEventListener( |
2539 np(this).feral, name, wrappedListener, useCapture); | 2863 np(this).feral, name, wrappedListener, useCapture); |
2540 wrappedListener._d_originalListener = listener; | 2864 wrappedListener._d_originalListener = listener; |
2541 np(this).wrappedListeners.push(wrappedListener); | 2865 np(this).wrappedListeners.push(wrappedListener); |
2542 } | 2866 } |
2543 | 2867 |
2544 // Implementation of EventTarget::removeEventListener | 2868 // Implementation of EventTarget::removeEventListener |
2545 function tameRemoveEventListener(name, listener, useCapture) { | 2869 function tameRemoveEventListener(name, listener, useCapture) { |
2546 var self = TameNodeT.coerce(this); | 2870 var self = TameNodeT.coerce(this); |
2547 var feral = np(self).feral; | 2871 var feral = np(self).feral; |
2548 if (!np(self).editable) { throw new Error(NOT_EDITABLE); } | 2872 np(this).policy.requireEditable(); |
2549 if (!np(this).wrappedListeners) { return; } | 2873 if (!np(this).wrappedListeners) { return; } |
2550 var wrappedListener = null; | 2874 var wrappedListener = null; |
2551 for (var i = np(this).wrappedListeners.length; --i >= 0;) { | 2875 for (var i = np(this).wrappedListeners.length; --i >= 0;) { |
2552 if (np(this).wrappedListeners[+i]._d_originalListener === listener) { | 2876 if (np(this).wrappedListeners[+i]._d_originalListener === listener) { |
2553 wrappedListener = np(this).wrappedListeners[+i]; | 2877 wrappedListener = np(this).wrappedListeners[+i]; |
2554 arrayRemove(np(this).wrappedListeners, i, i); | 2878 arrayRemove(np(this).wrappedListeners, i, i); |
2555 break; | 2879 break; |
2556 } | 2880 } |
2557 } | 2881 } |
2558 if (!wrappedListener) { return; } | 2882 if (!wrappedListener) { return; } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2598 traceStartup("DT: about to make TameNode"); | 2922 traceStartup("DT: about to make TameNode"); |
2599 | 2923 |
2600 /** | 2924 /** |
2601 * Base class for a Node wrapper. Do not create directly -- use the | 2925 * Base class for a Node wrapper. Do not create directly -- use the |
2602 * tameNode factory instead. | 2926 * tameNode factory instead. |
2603 * | 2927 * |
2604 * NOTE that all TameNodes should have the TameNodeT trademark, but it is | 2928 * NOTE that all TameNodes should have the TameNodeT trademark, but it is |
2605 * not applied here since that freezes the object, and also because of the | 2929 * not applied here since that freezes the object, and also because of the |
2606 * forwarding proxies used for catching expando properties. | 2930 * forwarding proxies used for catching expando properties. |
2607 * | 2931 * |
2608 * @param {boolean} editable true if the node's value, attributes, | 2932 * @param {policy} Mutability policy to apply. |
2609 * children, | |
2610 * or custom properties are mutable. | |
2611 * @constructor | 2933 * @constructor |
2612 */ | 2934 */ |
2613 function TameNode(editable) { | 2935 function TameNode(policy) { |
2614 TameNodeConf.confide(this); | 2936 TameNodeConf.confide(this); |
2615 np(this).editable = editable; | 2937 if (!policy || !policy.requireEditable) { |
2938 throw new Error("Domado internal error: Policy missing or invalid"); | |
2939 } | |
2940 np(this).policy = policy; | |
2616 return this; | 2941 return this; |
2617 } | 2942 } |
2618 inertCtor(TameNode, Object, 'Node'); | 2943 inertCtor(TameNode, Object, 'Node'); |
2619 traceStartup("DT: about to DPA TameNode"); | 2944 traceStartup("DT: about to DPA TameNode"); |
2620 definePropertiesAwesomely(TameNode.prototype, { | 2945 definePropertiesAwesomely(TameNode.prototype, { |
2621 // tameDocument is not yet defined at this point so can't be a constant | 2946 // tameDocument is not yet defined at this point so can't be a constant |
2622 ownerDocument: { | 2947 ownerDocument: { |
2623 enumerable: canHaveEnumerableAccessors, | 2948 enumerable: canHaveEnumerableAccessors, |
2624 get: cajaVM.def(function () { | 2949 get: cajaVM.def(function () { |
2625 return tameDocument; | 2950 return tameDocument; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2661 // abstract TameNode.prototype.replaceChild | 2986 // abstract TameNode.prototype.replaceChild |
2662 // abstract TameNode.prototype.firstChild | 2987 // abstract TameNode.prototype.firstChild |
2663 // abstract TameNode.prototype.lastChild | 2988 // abstract TameNode.prototype.lastChild |
2664 // abstract TameNode.prototype.nextSibling | 2989 // abstract TameNode.prototype.nextSibling |
2665 // abstract TameNode.prototype.previousSibling | 2990 // abstract TameNode.prototype.previousSibling |
2666 // abstract TameNode.prototype.parentNode | 2991 // abstract TameNode.prototype.parentNode |
2667 // abstract TameNode.prototype.getElementsByTagName | 2992 // abstract TameNode.prototype.getElementsByTagName |
2668 // abstract TameNode.prototype.getElementsByClassName | 2993 // abstract TameNode.prototype.getElementsByClassName |
2669 // abstract TameNode.prototype.childNodes | 2994 // abstract TameNode.prototype.childNodes |
2670 // abstract TameNode.prototype.attributes | 2995 // abstract TameNode.prototype.attributes |
2671 var tameNodePublicMembers = [ | |
2672 'cloneNode', | |
2673 'appendChild', 'insertBefore', 'removeChild', 'replaceChild', | |
2674 'dispatchEvent', 'hasChildNodes' | |
2675 ]; | |
2676 traceStartup("DT: about to defend TameNode"); | 2996 traceStartup("DT: about to defend TameNode"); |
2677 cajaVM.def(TameNode); // and its prototype | 2997 cajaVM.def(TameNode); // and its prototype |
2678 | 2998 |
2679 traceStartup("DT: about to make TameBackedNode"); | 2999 traceStartup("DT: about to make TameBackedNode"); |
2680 | 3000 |
2681 /** | 3001 /** |
2682 * A tame node that is backed by a real node. | 3002 * A tame node that is backed by a real node. |
2683 * | 3003 * |
2684 * Note that the constructor returns a proxy which delegates to 'this'; | 3004 * Note that the constructor returns a proxy which delegates to 'this'; |
2685 * subclasses should apply properties to 'this' but return the proxy. | 3005 * subclasses should apply properties to 'this' but return the proxy. |
2686 * | 3006 * |
2687 * @param {boolean} childrenEditable true iff the child list is mutable. | |
2688 * @param {Function} opt_proxyType The constructor of the proxy handler | 3007 * @param {Function} opt_proxyType The constructor of the proxy handler |
2689 * to use, defaulting to ExpandoProxyHandler. | 3008 * to use, defaulting to ExpandoProxyHandler. |
2690 * @constructor | 3009 * @constructor |
2691 */ | 3010 */ |
2692 function TameBackedNode(node, editable, childrenEditable, opt_proxyType) { | 3011 function TameBackedNode(node, opt_policy, opt_proxyType) { |
2693 node = makeDOMAccessible(node); | 3012 node = makeDOMAccessible(node); |
2694 | 3013 |
2695 if (!node) { | 3014 if (!node) { |
2696 throw new Error('Creating tame node with undefined native delegate'); | 3015 throw new Error('Creating tame node with undefined native delegate'); |
2697 } | 3016 } |
2698 | 3017 |
2699 TameNode.call(this, editable); | 3018 // Determine access policy |
3019 var parent = makeDOMAccessible(node.parentNode); | |
3020 var parentPolicy; | |
3021 if (!parent || | |
3022 idClassPattern.test(parent.className) || | |
3023 idClassPattern.test(node.className)) { | |
3024 parentPolicy = null; | |
3025 } else { | |
3026 // Parent is inside the vdoc. | |
3027 parentPolicy = np(defaultTameNode(parent)).policy; | |
3028 } | |
3029 var policy; | |
3030 if (opt_policy) { | |
3031 if (parentPolicy) { | |
3032 parentPolicy.childPolicy.assertRestrictedBy(opt_policy); | |
3033 } | |
3034 policy = opt_policy; | |
3035 //console.log("", parent, "->", node, "policy explicit", | |
3036 // policy.toString()); | |
3037 } else if (idClassPattern.test(node.className)) { | |
3038 // Virtual document root -- stop implicit recursion and define the | |
3039 // root policy. If we wanted to be able to define a "entire DOM | |
3040 // read-only" policy, this is where to hook it in. | |
3041 policy = nodePolicyEditable; | |
3042 //console.log("", parent, "->", node, "root policy", | |
3043 // policy.toString()); | |
3044 } else if (parentPolicy) { | |
3045 policy = parentPolicy.childPolicy; | |
3046 //console.log("", parent, "->", node, "policy via parent", | |
3047 // parentPolicy.toString(), policy.toString()); | |
3048 } else { | |
3049 policy = nodePolicyEditable; | |
3050 //console.log("", parent, "->", node, "policy isolated", | |
3051 // policy.toString()); | |
3052 } | |
3053 | |
3054 TameNode.call(this, policy); | |
2700 | 3055 |
2701 np(this).feral = node; | 3056 np(this).feral = node; |
2702 np(this).childrenEditable = editable && childrenEditable; | |
2703 | 3057 |
2704 if (domitaModules.proxiesAvailable) { | 3058 if (domitaModules.proxiesAvailable) { |
2705 np(this).proxyHandler = new (opt_proxyType || ExpandoProxyHandler)( | 3059 np(this).proxyHandler = new (opt_proxyType || ExpandoProxyHandler)( |
2706 this, editable, getNodeExpandoStorage(node)); | 3060 this, policy.editable, getNodeExpandoStorage(node)); |
2707 } | 3061 } |
2708 } | 3062 } |
2709 inertCtor(TameBackedNode, TameNode); | 3063 inertCtor(TameBackedNode, TameNode); |
2710 definePropertiesAwesomely(TameBackedNode.prototype, { | 3064 definePropertiesAwesomely(TameBackedNode.prototype, { |
2711 nodeType: NP.ro, | 3065 nodeType: NP.ro, |
2712 nodeName: NP.ro, | 3066 nodeName: NP.ro, |
2713 nodeValue: NP.ro, | 3067 nodeValue: NP.ro, |
2714 firstChild: NP_tameDescendant, | 3068 firstChild: NP_tameDescendant, // TODO(kpreid): Must be disableable |
2715 lastChild: NP_tameDescendant, | 3069 lastChild: NP_tameDescendant, |
2716 nextSibling: NP.related, | 3070 nextSibling: NP.related, |
2717 previousSibling: NP.related, | 3071 previousSibling: NP.related, |
2718 parentNode: NP.related, | 3072 parentNode: NP.related, |
2719 childNodes: { | 3073 childNodes: { |
2720 enumerable: true, | 3074 enumerable: true, |
2721 get: cajaVM.def(function () { | 3075 get: cajaVM.def(function () { |
2722 return new TameNodeList(np(this).feral.childNodes, | 3076 var privates = np(this); |
2723 np(this).childrenEditable, defaultTameNode); | 3077 if (privates.policy.childrenVisible) { |
3078 return new TameNodeList(np(this).feral.childNodes, | |
3079 defaultTameNode); | |
3080 } else { | |
3081 return fakeNodeList([]); | |
3082 } | |
2724 }) | 3083 }) |
2725 }, | 3084 }, |
2726 attributes: { | 3085 attributes: { |
2727 enumerable: true, | 3086 enumerable: true, |
2728 get: cajaVM.def(function () { | 3087 get: cajaVM.def(function () { |
2729 var thisNode = np(this).feral; | 3088 var privates = np(this); |
2730 var tameNodeCtor = function(node, editable) { | 3089 if (privates.policy.attributesVisible) { |
2731 return new TameBackedAttributeNode(node, editable, thisNode); | 3090 var thisNode = privates.feral; |
2732 }; | 3091 var tameNodeCtor = function(node) { |
2733 return new TameNodeList( | 3092 return new TameBackedAttributeNode(node, thisNode); |
2734 thisNode.attributes, thisNode, tameNodeCtor); | 3093 }; |
3094 // TODO(kpreid): There is no test which caught a previous | |
3095 // editability policy failure here | |
3096 return new TameNodeList(thisNode.attributes, tameNodeCtor); | |
3097 } else { | |
3098 return fakeNodeList([]); | |
3099 } | |
2735 }) | 3100 }) |
2736 } | 3101 } |
2737 }); | 3102 }); |
2738 TameBackedNode.prototype.cloneNode = nodeMethod(function (deep) { | 3103 TameBackedNode.prototype.cloneNode = nodeMethod(function (deep) { |
3104 np(this).policy.requireUnrestricted(); | |
2739 var clone = bridal.cloneNode(np(this).feral, Boolean(deep)); | 3105 var clone = bridal.cloneNode(np(this).feral, Boolean(deep)); |
2740 // From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4 | 3106 // From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4 |
2741 // "Note that cloning an immutable subtree results in a mutable copy" | 3107 // "Note that cloning an immutable subtree results in a mutable copy" |
2742 return defaultTameNode(clone, true); | 3108 return defaultTameNode(clone); |
2743 }); | 3109 }); |
3110 /** Is it OK to make 'child' a child of 'parent'? */ | |
3111 function checkAdoption(parent, child) { | |
3112 // Child must be editable since appendChild can remove it from its | |
3113 // parent. | |
3114 np(parent).policy.requireChildrenEditable(); | |
3115 np(child).policy.requireEditable(); | |
3116 // Sanity check: this cannot currently happen but if it does then we | |
3117 // need to rethink the calculation of policies. | |
3118 np(parent).policy.childPolicy.assertRestrictedBy(np(child).policy); | |
3119 } | |
2744 TameBackedNode.prototype.appendChild = nodeMethod(function (child) { | 3120 TameBackedNode.prototype.appendChild = nodeMethod(function (child) { |
2745 child = child || {}; | 3121 child = child || {}; |
2746 // Child must be editable since appendChild can remove it from its | |
2747 // parent. | |
2748 child = TameNodeT.coerce(child); | 3122 child = TameNodeT.coerce(child); |
2749 if (!np(this).childrenEditable || !np(child).editable) { | 3123 |
2750 throw new Error(NOT_EDITABLE); | 3124 checkAdoption(this, child); |
2751 } | 3125 |
2752 np(this).feral.appendChild(np(child).feral); | 3126 np(this).feral.appendChild(np(child).feral); |
2753 return child; | 3127 return child; |
2754 }); | 3128 }); |
2755 TameBackedNode.prototype.insertBefore = nodeMethod( | 3129 TameBackedNode.prototype.insertBefore = nodeMethod( |
2756 function(toInsert, child) { | 3130 function(toInsert, child) { |
2757 toInsert = TameNodeT.coerce(toInsert); | 3131 toInsert = TameNodeT.coerce(toInsert); |
2758 if (child === void 0) { child = null; } | 3132 if (child === void 0) { child = null; } |
3133 | |
2759 if (child !== null) { | 3134 if (child !== null) { |
2760 child = TameNodeT.coerce(child); | 3135 child = TameNodeT.coerce(child); |
2761 if (!np(child).editable) { | 3136 // TODO(kpreid): This child is not being mutated except for its |
2762 throw new Error(NOT_EDITABLE); | 3137 // previousSibling, so why are we rejecting here? |
2763 } | 3138 np(child).policy.requireEditable(); |
2764 } | 3139 } |
2765 if (!np(this).childrenEditable || !np(toInsert).editable) { | 3140 checkAdoption(this, toInsert); |
2766 throw new Error(NOT_EDITABLE); | 3141 |
2767 } | |
2768 np(this).feral.insertBefore( | 3142 np(this).feral.insertBefore( |
2769 np(toInsert).feral, child !== null ? np(child).feral : null); | 3143 np(toInsert).feral, child !== null ? np(child).feral : null); |
2770 return toInsert; | 3144 return toInsert; |
2771 }); | 3145 }); |
2772 TameBackedNode.prototype.removeChild = nodeMethod(function(child) { | 3146 TameBackedNode.prototype.removeChild = nodeMethod(function(child) { |
2773 child = TameNodeT.coerce(child); | 3147 child = TameNodeT.coerce(child); |
2774 if (!np(this).childrenEditable || !np(child).editable) { | 3148 np(this).policy.requireChildrenEditable(); |
2775 throw new Error(NOT_EDITABLE); | 3149 np(child).policy.requireEditable(); |
2776 } | |
2777 np(this).feral.removeChild(np(child).feral); | 3150 np(this).feral.removeChild(np(child).feral); |
2778 return child; | 3151 return child; |
2779 }); | 3152 }); |
2780 TameBackedNode.prototype.replaceChild = nodeMethod( | 3153 TameBackedNode.prototype.replaceChild = nodeMethod( |
2781 function(newChild, oldChild) { | 3154 function(newChild, oldChild) { |
2782 newChild = TameNodeT.coerce(newChild); | 3155 newChild = TameNodeT.coerce(newChild); |
2783 oldChild = TameNodeT.coerce(oldChild); | 3156 oldChild = TameNodeT.coerce(oldChild); |
2784 if (!np(this).childrenEditable || !np(newChild).editable | 3157 |
2785 || !np(oldChild).editable) { | 3158 checkAdoption(this, newChild); |
2786 throw new Error(NOT_EDITABLE); | 3159 np(oldChild).policy.requireEditable(); |
2787 } | 3160 |
2788 np(this).feral.replaceChild(np(newChild).feral, np(oldChild).feral); | 3161 np(this).feral.replaceChild(np(newChild).feral, np(oldChild).feral); |
2789 return oldChild; | 3162 return oldChild; |
2790 }); | 3163 }); |
2791 TameBackedNode.prototype.hasChildNodes = nodeMethod(function() { | 3164 TameBackedNode.prototype.hasChildNodes = nodeMethod(function() { |
2792 return !!np(this).feral.hasChildNodes(); | 3165 if (np(this).policy.childrenVisible) { |
3166 return !!np(this).feral.hasChildNodes(); | |
3167 } else { | |
3168 return false; | |
3169 } | |
2793 }); | 3170 }); |
2794 // http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget | 3171 // http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget |
2795 // "The EventTarget interface is implemented by all Nodes" | 3172 // "The EventTarget interface is implemented by all Nodes" |
2796 TameBackedNode.prototype.dispatchEvent = nodeMethod(function(evt) { | 3173 TameBackedNode.prototype.dispatchEvent = nodeMethod(function(evt) { |
2797 evt = TameEventT.coerce(evt); | 3174 evt = TameEventT.coerce(evt); |
2798 bridal.dispatchEvent(np(this).feral, TameEventConf.p(evt).feral); | 3175 bridal.dispatchEvent(np(this).feral, TameEventConf.p(evt).feral); |
2799 }); | 3176 }); |
2800 | 3177 |
2801 if (docEl.contains) { // typeof is 'object' on IE | 3178 if (docEl.contains) { // typeof is 'object' on IE |
2802 TameBackedNode.prototype.contains = nodeMethod(function (other) { | 3179 TameBackedNode.prototype.contains = nodeMethod(function (other) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2851 } | 3228 } |
2852 } | 3229 } |
2853 cajaVM.def(TameBackedNode); // and its prototype | 3230 cajaVM.def(TameBackedNode); // and its prototype |
2854 | 3231 |
2855 traceStartup("DT: about to make TamePseudoNode"); | 3232 traceStartup("DT: about to make TamePseudoNode"); |
2856 | 3233 |
2857 /** | 3234 /** |
2858 * A fake node that is not backed by a real DOM node. | 3235 * A fake node that is not backed by a real DOM node. |
2859 * @constructor | 3236 * @constructor |
2860 */ | 3237 */ |
2861 function TamePseudoNode(editable) { | 3238 function TamePseudoNode() { |
2862 TameNode.call(this, editable); | 3239 // Note inconsistency: we have an editable policy, for the sake of our |
3240 // children, but don't actually allow direct mutation. | |
3241 TameNode.call(this, nodePolicyEditable); | |
2863 | 3242 |
2864 if (domitaModules.proxiesAvailable) { | 3243 if (domitaModules.proxiesAvailable) { |
2865 // finishNode will wrap 'this' with an actual proxy later. | 3244 // finishNode will wrap 'this' with an actual proxy later. |
2866 np(this).proxyHandler = new ExpandoProxyHandler(this, editable, {}); | 3245 np(this).proxyHandler = new ExpandoProxyHandler(this, true, {}); |
2867 } | 3246 } |
2868 } | 3247 } |
2869 inertCtor(TamePseudoNode, TameNode); | 3248 inertCtor(TamePseudoNode, TameNode); |
2870 TamePseudoNode.prototype.appendChild = | 3249 TamePseudoNode.prototype.appendChild = |
2871 TamePseudoNode.prototype.insertBefore = | 3250 TamePseudoNode.prototype.insertBefore = |
2872 TamePseudoNode.prototype.removeChild = | 3251 TamePseudoNode.prototype.removeChild = |
2873 TamePseudoNode.prototype.replaceChild = nodeMethod(function () { | 3252 TamePseudoNode.prototype.replaceChild = nodeMethod(function () { |
2874 if (typeof console !== 'undefined') { | 3253 if (typeof console !== 'undefined') { |
2875 console.log("Node not editable; no action performed."); | 3254 console.log("Node not editable; no action performed."); |
2876 } | 3255 } |
(...skipping 28 matching lines...) Expand all Loading... | |
2905 var siblings = parentNode.childNodes; | 3284 var siblings = parentNode.childNodes; |
2906 for (var i = siblings.length; --i >= 1;) { | 3285 for (var i = siblings.length; --i >= 1;) { |
2907 if (siblings[+i] === self) { return siblings[i - 1]; } | 3286 if (siblings[+i] === self) { return siblings[i - 1]; } |
2908 } | 3287 } |
2909 return null; | 3288 return null; |
2910 })} | 3289 })} |
2911 }); | 3290 }); |
2912 cajaVM.def(TamePseudoNode); // and its prototype | 3291 cajaVM.def(TamePseudoNode); // and its prototype |
2913 | 3292 |
2914 traceStartup("DT: done fundamental nodes"); | 3293 traceStartup("DT: done fundamental nodes"); |
2915 traceStartup("DT: about to define makeRestrictedNodeType"); | |
2916 | |
2917 function makeRestrictedNodeType(whitelist) { | |
2918 function ForeignOrOpaqueNode(node, editable) { | |
2919 TameBackedNode.call(this, node, editable, editable); | |
2920 } | |
2921 var nodeType = ForeignOrOpaqueNode; // other name is for debug hint | |
2922 inherit(nodeType, TameBackedNode); | |
2923 for (var safe in whitelist) { | |
2924 // Any non-own property is overridden to be opaque below. | |
2925 var descriptor = (whitelist[safe] === 0) | |
2926 ? domitaModules.getPropertyDescriptor( | |
2927 TameBackedNode.prototype, safe) | |
2928 : { | |
2929 value: whitelist[safe], | |
2930 writable: false, | |
2931 configurable: false, | |
2932 enumerable: true | |
2933 }; | |
2934 Object.defineProperty(nodeType.prototype, safe, descriptor); | |
2935 } | |
2936 definePropertiesAwesomely(nodeType.prototype, { | |
2937 attributes: { | |
2938 enumerable: canHaveEnumerableAccessors, | |
2939 get: nodeMethod(function () { | |
2940 return new TameNodeList([], false, undefined); | |
2941 }) | |
2942 } | |
2943 }); | |
2944 function throwRestricted() { | |
2945 throw new Error('Node is restricted'); | |
2946 } | |
2947 cajaVM.def(throwRestricted); | |
2948 for (var i = tameNodePublicMembers.length; --i >= 0;) { | |
2949 var k = tameNodePublicMembers[+i]; | |
2950 if (!nodeType.prototype.hasOwnProperty(k)) { | |
2951 if (typeof TameBackedNode.prototype[k] === 'Function') { | |
2952 nodeType.prototype[k] = throwRestricted; | |
2953 } else { | |
2954 Object.defineProperty(nodeType.prototype, k, { | |
2955 enumerable: canHaveEnumerableAccessors, | |
2956 get: throwRestricted | |
2957 }); | |
2958 } | |
2959 } | |
2960 } | |
2961 return cajaVM.def(nodeType); // and its prototype | |
2962 } | |
2963 | 3294 |
2964 traceStartup("DT: about to make TameOpaqueNode"); | 3295 traceStartup("DT: about to make TameOpaqueNode"); |
2965 | 3296 |
2966 // An opaque node is traversible but not manipulable by guest code. This | 3297 // An opaque node is traversible but not manipulable by guest code. This |
2967 // is the default taming for unrecognized nodes or nodes not explicitly | 3298 // is the default taming for unrecognized nodes or nodes not explicitly |
2968 // whitelisted. | 3299 // whitelisted. |
2969 var TameOpaqueNode = makeRestrictedNodeType({ | 3300 function TameOpaqueNode(node) { |
2970 nodeValue: 0, | 3301 TameBackedNode.call(this, node, nodePolicyOpaque); |
2971 nodeType: 0, | 3302 } |
2972 nodeName: 0, | 3303 inertCtor(TameOpaqueNode, TameBackedNode); |
2973 nextSibling: 0, | 3304 cajaVM.def(TameOpaqueNode); |
2974 previousSibling: 0, | |
2975 firstChild: 0, | |
2976 lastChild: 0, | |
2977 parentNode: 0, | |
2978 childNodes: 0, | |
2979 ownerDocument: 0, | |
2980 hasChildNodes: 0 | |
2981 }); | |
2982 | |
2983 traceStartup("DT: about to make TameForeignNode"); | |
2984 | 3305 |
2985 // A foreign node is one supplied by some external system to the guest | 3306 // A foreign node is one supplied by some external system to the guest |
2986 // code, which the guest code may lay out within its own DOM tree but may | 3307 // code, which the guest code may lay out within its own DOM tree but may |
2987 // not traverse into in any way. | 3308 // not traverse into in any way. |
2988 // | 3309 function TameForeignNode(node) { |
2989 // TODO(ihab.awad): The taming chosen for foreign nodes is very | 3310 TameBackedNode.call(this, node, nodePolicyForeign); |
2990 // restrictive and could be relaxed, but only after careful consideration. | 3311 } |
2991 // The below choices are for simple safety, e.g., exposing a foreign | 3312 inertCtor(TameForeignNode, TameBackedNode); |
2992 // node's | 3313 TameForeignNode.prototype.getElementsByTagName = function (tagName) { |
2993 // siblings when the foreign node has been added to some DOM tree outside | 3314 // needed because TameForeignNode doesn't inherit TameElement |
2994 // this domicile might be dangerous. | 3315 return fakeNodeList([]); |
2995 var TameForeignNode = makeRestrictedNodeType({ | 3316 }; |
2996 nodeValue: 0, | 3317 TameForeignNode.prototype.getElementsByClassName = function (className) { |
2997 nodeType: 0, | 3318 // needed because TameForeignNode doesn't inherit TameElement |
2998 nodeName: 0, | 3319 return fakeNodeList([]); |
2999 nextSibling: undefined, | 3320 }; |
3000 previousSibling: undefined, | 3321 cajaVM.def(TameForeignNode); |
3001 firstChild: undefined, | |
3002 lastChild: undefined, | |
3003 parentNode: undefined, | |
3004 childNodes: Object.freeze([]), | |
3005 ownerDocument: undefined, | |
3006 getElementsByTagName: function() { return Object.freeze([]); }, | |
3007 getElementsByClassName: function() { return Object.freeze([]); }, | |
3008 hasChildNodes: function() { return false; } | |
3009 }); | |
3010 | 3322 |
3011 traceStartup("DT: about to make TameTextNode"); | 3323 traceStartup("DT: about to make TameTextNode"); |
3012 | 3324 |
3013 function TameTextNode(node, editable) { | 3325 function TameTextNode(node) { |
3014 assert(node.nodeType === 3); | 3326 assert(node.nodeType === 3); |
3015 | 3327 TameBackedNode.call(this, node); |
3016 // The below should not be strictly necessary since childrenEditable for | |
3017 // TameScriptElements is always false, but it protects against tameNode | |
3018 // being called naively on a text node from container code. | |
3019 var pn = node.parentNode; | |
3020 if (editable && pn) { | |
3021 if (1 === pn.nodeType | |
3022 && !htmlSchema.element(pn.tagName).allowed) { | |
3023 // Do not allow mutation of text inside script elements. | |
3024 // See the testScriptLoading testcase for examples of exploits. | |
3025 editable = false; | |
3026 } | |
3027 } | |
3028 | |
3029 TameBackedNode.call(this, node, editable, editable); | |
3030 } | 3328 } |
3031 inertCtor(TameTextNode, TameBackedNode, 'Text'); | 3329 inertCtor(TameTextNode, TameBackedNode, 'Text'); |
3032 var textAccessor = { | 3330 var textAccessor = { |
3033 enumerable: true, | 3331 enumerable: true, |
3034 get: nodeMethod(function () { | 3332 get: nodeMethod(function () { |
3035 return np(this).feral.nodeValue; | 3333 return np(this).feral.nodeValue; |
3036 }), | 3334 }), |
3037 set: nodeMethod(function (value) { | 3335 set: nodeMethod(function (value) { |
3038 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3336 np(this).policy.requireEditable(); |
3039 np(this).feral.nodeValue = String(value || ''); | 3337 np(this).feral.nodeValue = String(value || ''); |
3040 }) | 3338 }) |
3041 }; | 3339 }; |
3042 definePropertiesAwesomely(TameTextNode.prototype, { | 3340 definePropertiesAwesomely(TameTextNode.prototype, { |
3043 nodeValue: textAccessor, | 3341 nodeValue: textAccessor, |
3044 textContent: textAccessor, | 3342 textContent: textAccessor, |
3045 innerText: textAccessor, | 3343 innerText: textAccessor, |
3046 data: textAccessor | 3344 data: textAccessor |
3047 }); | 3345 }); |
3048 setOwn(TameTextNode.prototype, "toString", nodeMethod(function () { | 3346 setOwn(TameTextNode.prototype, "toString", nodeMethod(function () { |
3049 return '#text'; | 3347 return '#text'; |
3050 })); | 3348 })); |
3051 cajaVM.def(TameTextNode); // and its prototype | 3349 cajaVM.def(TameTextNode); // and its prototype |
3052 | 3350 |
3053 function TameCommentNode(node, editable) { | 3351 function TameCommentNode(node) { |
3054 assert(node.nodeType === 8); | 3352 assert(node.nodeType === 8); |
3055 TameBackedNode.call(this, node, editable, editable); | 3353 TameBackedNode.call(this, node); |
3056 } | 3354 } |
3057 inertCtor(TameCommentNode, TameBackedNode, 'CommentNode'); | 3355 inertCtor(TameCommentNode, TameBackedNode, 'CommentNode'); |
3058 setOwn(TameCommentNode.prototype, "toString", nodeMethod(function () { | 3356 setOwn(TameCommentNode.prototype, "toString", nodeMethod(function () { |
3059 return '#comment'; | 3357 return '#comment'; |
3060 })); | 3358 })); |
3061 cajaVM.def(TameCommentNode); // and its prototype | 3359 cajaVM.def(TameCommentNode); // and its prototype |
3062 | 3360 |
3063 traceStartup("DT: about to make TameBackedAttributeNode"); | 3361 traceStartup("DT: about to make TameBackedAttributeNode"); |
3064 /** | 3362 /** |
3065 * Plays the role of an Attr node for TameElement objects. | 3363 * Plays the role of an Attr node for TameElement objects. |
3066 */ | 3364 */ |
3067 function TameBackedAttributeNode(node, editable, ownerElement) { | 3365 function TameBackedAttributeNode(node, ownerElement) { |
3068 if (ownerElement === undefined) throw new Error( | 3366 if (ownerElement === undefined) throw new Error( |
3069 "ownerElement undefined"); | 3367 "ownerElement undefined"); |
3070 TameBackedNode.call(this, node, editable); | 3368 TameBackedNode.call(this, node, |
3369 np(defaultTameNode(ownerElement)).policy); | |
3071 np(this).ownerElement = ownerElement; | 3370 np(this).ownerElement = ownerElement; |
3072 } | 3371 } |
3073 inertCtor(TameBackedAttributeNode, TameBackedNode, 'Attr'); | 3372 inertCtor(TameBackedAttributeNode, TameBackedNode, 'Attr'); |
3074 setOwn(TameBackedAttributeNode.prototype, 'cloneNode', | 3373 setOwn(TameBackedAttributeNode.prototype, 'cloneNode', |
3075 nodeMethod(function (deep) { | 3374 nodeMethod(function (deep) { |
3076 var clone = bridal.cloneNode(np(this).feral, Boolean(deep)); | 3375 var clone = bridal.cloneNode(np(this).feral, Boolean(deep)); |
3077 // From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4 | 3376 // From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4 |
3078 // "Note that cloning an immutable subtree results in a mutable copy" | 3377 // "Note that cloning an immutable subtree results in a mutable copy" |
3079 return new TameBackedAttributeNode(clone, true, np(this).ownerElement); | 3378 return new TameBackedAttributeNode(clone, np(this).ownerElement); |
3080 })); | 3379 })); |
3081 var nameAccessor = { | 3380 var nameAccessor = { |
3082 enumerable: true, | 3381 enumerable: true, |
3083 get: nodeMethod(function () { | 3382 get: nodeMethod(function () { |
3084 var name = np(this).feral.name; | 3383 var name = np(this).feral.name; |
3085 if (cajaPrefRe.test(name)) { | 3384 if (cajaPrefRe.test(name)) { |
3086 name = name.substring(cajaPrefix.length); | 3385 name = name.substring(cajaPrefix.length); |
3087 } | 3386 } |
3088 return name; | 3387 return name; |
3089 }) | 3388 }) |
(...skipping 14 matching lines...) Expand all Loading... | |
3104 enumerable: true, | 3403 enumerable: true, |
3105 get: nodeMethod(function () { | 3404 get: nodeMethod(function () { |
3106 return this.ownerElement.hasAttribute(this.name); | 3405 return this.ownerElement.hasAttribute(this.name); |
3107 }) | 3406 }) |
3108 }, | 3407 }, |
3109 nodeValue: valueAccessor, | 3408 nodeValue: valueAccessor, |
3110 value: valueAccessor, | 3409 value: valueAccessor, |
3111 ownerElement: { | 3410 ownerElement: { |
3112 enumerable: true, | 3411 enumerable: true, |
3113 get: nodeMethod(function () { | 3412 get: nodeMethod(function () { |
3114 return defaultTameNode(np(this).ownerElement, np(this).editable); | 3413 return defaultTameNode(np(this).ownerElement); |
3115 }) | 3414 }) |
3116 }, | 3415 }, |
3117 nodeType: P_constant(2), | 3416 nodeType: P_constant(2), |
3118 firstChild: P_UNIMPLEMENTED, | 3417 firstChild: P_UNIMPLEMENTED, |
3119 lastChild: P_UNIMPLEMENTED, | 3418 lastChild: P_UNIMPLEMENTED, |
3120 nextSibling: P_UNIMPLEMENTED, | 3419 nextSibling: P_UNIMPLEMENTED, |
3121 previousSibling: P_UNIMPLEMENTED, | 3420 previousSibling: P_UNIMPLEMENTED, |
3122 parentNode: P_UNIMPLEMENTED, | 3421 parentNode: P_UNIMPLEMENTED, |
3123 childNodes: P_UNIMPLEMENTED, | 3422 childNodes: P_UNIMPLEMENTED, |
3124 attributes: P_UNIMPLEMENTED | 3423 attributes: P_UNIMPLEMENTED |
(...skipping 24 matching lines...) Expand all Loading... | |
3149 if (Object.prototype.hasOwnProperty.call( | 3448 if (Object.prototype.hasOwnProperty.call( |
3150 seenAlready, attribName)) { | 3449 seenAlready, attribName)) { |
3151 return; | 3450 return; |
3152 } | 3451 } |
3153 seenAlready[attribName] = true; | 3452 seenAlready[attribName] = true; |
3154 | 3453 |
3155 Object.defineProperty(tameElementPrototype, attribName, { | 3454 Object.defineProperty(tameElementPrototype, attribName, { |
3156 enumerable: canHaveEnumerableAccessors, | 3455 enumerable: canHaveEnumerableAccessors, |
3157 configurable: false, | 3456 configurable: false, |
3158 set: nodeMethod(function eventHandlerSetter(listener) { | 3457 set: nodeMethod(function eventHandlerSetter(listener) { |
3159 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3458 np(this).policy.requireEditable(); |
3160 if (!listener) { // Clear the current handler | 3459 if (!listener) { // Clear the current handler |
3161 np(this).feral[attribName] = null; | 3460 np(this).feral[attribName] = null; |
3162 } else { | 3461 } else { |
3163 // This handler cannot be copied from one node to another | 3462 // This handler cannot be copied from one node to another |
3164 // which is why getters are not yet supported. | 3463 // which is why getters are not yet supported. |
3165 np(this).feral[attribName] = makeEventHandlerWrapper( | 3464 np(this).feral[attribName] = makeEventHandlerWrapper( |
3166 np(this).feral, listener); | 3465 np(this).feral, listener); |
3167 } | 3466 } |
3168 return listener; | 3467 return listener; |
3169 }) | 3468 }) |
3170 }); | 3469 }); |
3171 })(html4Attrib.match(attrNameRe)[1]); | 3470 })(html4Attrib.match(attrNameRe)[1]); |
3172 } | 3471 } |
3173 } | 3472 } |
3174 } | 3473 } |
3175 | 3474 |
3176 traceStartup("DT: about to make TameElement"); | 3475 traceStartup("DT: about to make TameElement"); |
3177 /** | 3476 /** |
3178 * See comments on TameBackedNode regarding return value. | 3477 * See comments on TameBackedNode regarding return value. |
3179 * @constructor | 3478 * @constructor |
3180 */ | 3479 */ |
3181 function TameElement(node, editable, childrenEditable, opt_proxyType) { | 3480 function TameElement(node, opt_policy, opt_proxyType) { |
3182 assert(node.nodeType === 1); | 3481 assert(node.nodeType === 1); |
3183 var obj = TameBackedNode.call(this, node, editable, childrenEditable, | 3482 var obj = TameBackedNode.call(this, node, opt_policy, opt_proxyType); |
3184 opt_proxyType); | |
3185 np(this).geometryDelegate = node; | 3483 np(this).geometryDelegate = node; |
3186 return obj; | 3484 return obj; |
3187 } | 3485 } |
3188 nodeClasses.Element = inertCtor(TameElement, TameBackedNode, | 3486 nodeClasses.Element = inertCtor(TameElement, TameBackedNode, |
3189 'HTMLElement'); | 3487 'HTMLElement'); |
3190 registerElementScriptAttributeHandlers(TameElement.prototype); | 3488 registerElementScriptAttributeHandlers(TameElement.prototype); |
3191 TameElement.prototype.blur = nodeMethod(function () { | 3489 TameElement.prototype.blur = nodeMethod(function () { |
3192 np(this).feral.blur(); | 3490 np(this).feral.blur(); |
3193 }); | 3491 }); |
3194 TameElement.prototype.focus = nodeMethod(function () { | 3492 TameElement.prototype.focus = nodeMethod(function () { |
3195 if (domicile.isProcessingEvent) { | 3493 return domicile.handlingUserAction && np(this).feral.focus(); |
3196 np(this).feral.focus(); | |
3197 } | |
3198 }); | 3494 }); |
3199 // IE-specific method. Sets the element that will have focus when the | 3495 // IE-specific method. Sets the element that will have focus when the |
3200 // window has focus, without focusing the window. | 3496 // window has focus, without focusing the window. |
3201 if (docEl.setActive) { | 3497 if (docEl.setActive) { |
3202 TameElement.prototype.setActive = nodeMethod(function () { | 3498 TameElement.prototype.setActive = nodeMethod(function () { |
3203 if (domicile.isProcessingEvent) { | 3499 return domicile.handlingUserAction && np(this).feral.setActive(); |
3204 np(this).feral.setActive(); | |
3205 } | |
3206 }); | 3500 }); |
3207 } | 3501 } |
3208 // IE-specific method. | 3502 // IE-specific method. |
3209 if (docEl.hasFocus) { | 3503 if (docEl.hasFocus) { |
3210 TameElement.prototype.hasFocus = nodeMethod(function () { | 3504 TameElement.prototype.hasFocus = nodeMethod(function () { |
3211 return np(this).feral.hasFocus(); | 3505 return np(this).feral.hasFocus(); |
3212 }); | 3506 }); |
3213 } | 3507 } |
3214 TameElement.prototype.getAttribute = nodeMethod(function (attribName) { | 3508 TameElement.prototype.getAttribute = nodeMethod(function (attribName) { |
3509 if (!np(this).policy.attributesVisible) { return null; } | |
3215 var feral = np(this).feral; | 3510 var feral = np(this).feral; |
3216 attribName = String(attribName).toLowerCase(); | 3511 attribName = String(attribName).toLowerCase(); |
3217 if (/__$/.test(attribName)) { | 3512 if (/__$/.test(attribName)) { |
3218 throw new TypeError('Attributes may not end with __'); | 3513 throw new TypeError('Attributes may not end with __'); |
3219 } | 3514 } |
3220 var tagName = feral.tagName.toLowerCase(); | 3515 var tagName = feral.tagName.toLowerCase(); |
3221 var atype = htmlSchema.attribute(tagName, attribName).type; | 3516 var atype = htmlSchema.attribute(tagName, attribName).type; |
3222 if (atype === void 0) { | 3517 if (atype === void 0) { |
3223 return feral.getAttribute(cajaPrefix + attribName); | 3518 return feral.getAttribute(cajaPrefix + attribName); |
3224 } | 3519 } |
3225 var value = bridal.getAttribute(feral, attribName); | 3520 var value = bridal.getAttribute(feral, attribName); |
3226 if ('string' !== typeof value) { return value; } | 3521 if ('string' !== typeof value) { return value; } |
3227 return virtualizeAttributeValue(atype, value); | 3522 return virtualizeAttributeValue(atype, value); |
3228 }); | 3523 }); |
3229 TameElement.prototype.getAttributeNode = nodeMethod(function (name) { | 3524 TameElement.prototype.getAttributeNode = nodeMethod(function (name) { |
3525 if (!np(this).policy.attributesVisible) { return null; } | |
3230 var feral = np(this).feral; | 3526 var feral = np(this).feral; |
3231 var hostDomNode = feral.getAttributeNode(name); | 3527 var hostDomNode = feral.getAttributeNode(name); |
3232 if (hostDomNode === null) { return null; } | 3528 if (hostDomNode === null) { return null; } |
3233 return new TameBackedAttributeNode( | 3529 return new TameBackedAttributeNode(hostDomNode, feral); |
3234 hostDomNode, np(this).editable, feral); | |
3235 }); | 3530 }); |
3236 TameElement.prototype.hasAttribute = nodeMethod(function (attribName) { | 3531 TameElement.prototype.hasAttribute = nodeMethod(function (attribName) { |
3237 var feral = np(this).feral; | 3532 var feral = np(this).feral; |
3238 attribName = String(attribName).toLowerCase(); | 3533 attribName = String(attribName).toLowerCase(); |
3239 var tagName = feral.tagName.toLowerCase(); | 3534 var tagName = feral.tagName.toLowerCase(); |
3240 var atype = htmlSchema.attribute(tagName, attribName).type; | 3535 var atype = htmlSchema.attribute(tagName, attribName).type; |
3241 if (atype === void 0) { | 3536 if (atype === void 0) { |
3242 return bridal.hasAttribute(feral, cajaPrefix + attribName); | 3537 return bridal.hasAttribute(feral, cajaPrefix + attribName); |
3243 } else { | 3538 } else { |
3244 return bridal.hasAttribute(feral, attribName); | 3539 return bridal.hasAttribute(feral, attribName); |
3245 } | 3540 } |
3246 }); | 3541 }); |
3247 TameElement.prototype.setAttribute = nodeMethod( | 3542 TameElement.prototype.setAttribute = nodeMethod( |
3248 function (attribName, value) { | 3543 function (attribName, value) { |
3249 //console.debug("setAttribute", this, attribName, value); | 3544 //console.debug("setAttribute", this, attribName, value); |
3250 var feral = np(this).feral; | 3545 var feral = np(this).feral; |
3251 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3546 np(this).policy.requireEditable(); |
3252 attribName = String(attribName).toLowerCase(); | 3547 attribName = String(attribName).toLowerCase(); |
3253 if (/__$/.test(attribName)) { | 3548 if (/__$/.test(attribName)) { |
3254 throw new TypeError('Attributes may not end with __'); | 3549 throw new TypeError('Attributes may not end with __'); |
3255 } | 3550 } |
3551 if (!np(this).policy.attributesVisible) { return null; } | |
3256 var tagName = feral.tagName.toLowerCase(); | 3552 var tagName = feral.tagName.toLowerCase(); |
3257 var atype = htmlSchema.attribute(tagName, attribName).type; | 3553 var atype = htmlSchema.attribute(tagName, attribName).type; |
3258 if (atype === void 0) { | 3554 if (atype === void 0) { |
3259 bridal.setAttribute(feral, cajaPrefix + attribName, value); | 3555 bridal.setAttribute(feral, cajaPrefix + attribName, value); |
3260 } else { | 3556 } else { |
3261 var sanitizedValue = rewriteAttribute( | 3557 var sanitizedValue = rewriteAttribute( |
3262 tagName, attribName, atype, value); | 3558 tagName, attribName, atype, value); |
3263 if (sanitizedValue !== null) { | 3559 if (sanitizedValue !== null) { |
3264 bridal.setAttribute(feral, attribName, sanitizedValue); | 3560 bridal.setAttribute(feral, attribName, sanitizedValue); |
3265 if (html4.ATTRIBS.hasOwnProperty(tagName + '::target') && | 3561 if (html4.ATTRIBS.hasOwnProperty(tagName + '::target') && |
3266 atype === html4.atype.URI) { | 3562 atype === html4.atype.URI) { |
3267 if (sanitizedValue.charAt(0) === '#') { | 3563 if (sanitizedValue.charAt(0) === '#') { |
3268 feral.removeAttribute('target'); | 3564 feral.removeAttribute('target'); |
3269 } else { | 3565 } else { |
3270 bridal.setAttribute(feral, 'target', | 3566 bridal.setAttribute(feral, 'target', |
3271 getSafeTargetAttribute(tagName, 'target', | 3567 getSafeTargetAttribute(tagName, 'target', |
3272 bridal.getAttribute(feral, 'target'))); | 3568 bridal.getAttribute(feral, 'target'))); |
3273 } | 3569 } |
3274 } | 3570 } |
3275 } | 3571 } |
3276 } | 3572 } |
3277 return value; | 3573 return value; |
3278 }); | 3574 }); |
3279 TameElement.prototype.removeAttribute = nodeMethod(function (attribName) { | 3575 TameElement.prototype.removeAttribute = nodeMethod(function (attribName) { |
3280 var feral = np(this).feral; | 3576 var feral = np(this).feral; |
3281 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3577 np(this).policy.requireEditable(); |
3282 attribName = String(attribName).toLowerCase(); | 3578 attribName = String(attribName).toLowerCase(); |
3283 if (/__$/.test(attribName)) { | 3579 if (/__$/.test(attribName)) { |
3284 throw new TypeError('Attributes may not end with __'); | 3580 throw new TypeError('Attributes may not end with __'); |
3285 } | 3581 } |
3286 var tagName = feral.tagName.toLowerCase(); | 3582 var tagName = feral.tagName.toLowerCase(); |
3287 var atype = htmlSchema.attribute(tagName, attribName).type; | 3583 var atype = htmlSchema.attribute(tagName, attribName).type; |
3288 if (atype === void 0) { | 3584 if (atype === void 0) { |
3289 feral.removeAttribute(cajaPrefix + attribName); | 3585 feral.removeAttribute(cajaPrefix + attribName); |
3290 } else { | 3586 } else { |
3291 feral.removeAttribute(attribName); | 3587 feral.removeAttribute(attribName); |
3292 } | 3588 } |
3293 }); | 3589 }); |
3294 TameElement.prototype.getElementsByTagName = nodeMethod( | 3590 TameElement.prototype.getElementsByTagName = nodeMethod( |
3295 function(tagName) { | 3591 function(tagName) { |
3296 return tameGetElementsByTagName( | 3592 return tameGetElementsByTagName(np(this).feral, tagName); |
3297 np(this).feral, tagName, np(this).childrenEditable); | |
3298 }); | 3593 }); |
3299 TameElement.prototype.getElementsByClassName = nodeMethod( | 3594 TameElement.prototype.getElementsByClassName = nodeMethod( |
3300 function(className) { | 3595 function(className) { |
3301 return tameGetElementsByClassName( | 3596 return tameGetElementsByClassName(np(this).feral, className); |
3302 np(this).feral, className, np(this).childrenEditable); | |
3303 }); | 3597 }); |
3304 TameElement.prototype.getBoundingClientRect = nodeMethod(function () { | 3598 TameElement.prototype.getBoundingClientRect = nodeMethod(function () { |
3305 var feral = np(this).feral; | 3599 var feral = np(this).feral; |
3306 var elRect = bridal.getBoundingClientRect(feral); | 3600 var elRect = bridal.getBoundingClientRect(feral); |
3307 var vdoc = bridal.getBoundingClientRect( | 3601 var vdoc = bridal.getBoundingClientRect( |
3308 np(this.ownerDocument).feralContainerNode); | 3602 np(this.ownerDocument).feralContainerNode); |
3309 var vdocLeft = vdoc.left, vdocTop = vdoc.top; | 3603 var vdocLeft = vdoc.left, vdocTop = vdoc.top; |
3310 return ({ | 3604 return ({ |
3311 top: elRect.top - vdocTop, | 3605 top: elRect.top - vdocTop, |
3312 left: elRect.left - vdocLeft, | 3606 left: elRect.left - vdocLeft, |
3313 right: elRect.right - vdocLeft, | 3607 right: elRect.right - vdocLeft, |
3314 bottom: elRect.bottom - vdocTop | 3608 bottom: elRect.bottom - vdocTop |
3315 }); | 3609 }); |
3316 }); | 3610 }); |
3317 TameElement.prototype.updateStyle = nodeMethod(function (style) { | 3611 TameElement.prototype.updateStyle = nodeMethod(function (style) { |
3318 var feral = np(this).feral; | 3612 var feral = np(this).feral; |
3319 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3613 np(this).policy.requireEditable(); |
3320 var cssPropertiesAndValues = cssSealerUnsealerPair.unseal(style); | 3614 var cssPropertiesAndValues = cssSealerUnsealerPair.unseal(style); |
3321 if (!cssPropertiesAndValues) { throw new Error(); } | 3615 if (!cssPropertiesAndValues) { throw new Error(); } |
3322 | 3616 |
3323 var styleNode = feral.style; | 3617 var styleNode = feral.style; |
3324 for (var i = 0; i < cssPropertiesAndValues.length; i += 2) { | 3618 for (var i = 0; i < cssPropertiesAndValues.length; i += 2) { |
3325 var propName = cssPropertiesAndValues[+i]; | 3619 var propName = cssPropertiesAndValues[+i]; |
3326 var propValue = cssPropertiesAndValues[i + 1]; | 3620 var propValue = cssPropertiesAndValues[i + 1]; |
3327 // If the propertyName differs between DOM and CSS, there will | 3621 // If the propertyName differs between DOM and CSS, there will |
3328 // be a semicolon between the two. | 3622 // be a semicolon between the two. |
3329 // E.g., 'background-color;backgroundColor' | 3623 // E.g., 'background-color;backgroundColor' |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3365 extendedAccessors: true, | 3659 extendedAccessors: true, |
3366 enumerable: true, | 3660 enumerable: true, |
3367 get: nodeMethod(function (prop) { | 3661 get: nodeMethod(function (prop) { |
3368 return np(this).geometryDelegate[prop]; | 3662 return np(this).geometryDelegate[prop]; |
3369 }) | 3663 }) |
3370 }; | 3664 }; |
3371 var geometryDelegatePropertySettable = | 3665 var geometryDelegatePropertySettable = |
3372 Object.create(geometryDelegateProperty); | 3666 Object.create(geometryDelegateProperty); |
3373 geometryDelegatePropertySettable.set = | 3667 geometryDelegatePropertySettable.set = |
3374 nodeMethod(function (value, prop) { | 3668 nodeMethod(function (value, prop) { |
3375 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 3669 np(this).policy.requireEditable(); |
3376 np(this).geometryDelegate[prop] = +value; | 3670 np(this).geometryDelegate[prop] = +value; |
3377 }); | 3671 }); |
3378 definePropertiesAwesomely(TameElement.prototype, { | 3672 definePropertiesAwesomely(TameElement.prototype, { |
3379 clientLeft: geometryDelegateProperty, | 3673 clientLeft: geometryDelegateProperty, |
3380 clientTop: geometryDelegateProperty, | 3674 clientTop: geometryDelegateProperty, |
3381 clientWidth: geometryDelegateProperty, | 3675 clientWidth: geometryDelegateProperty, |
3382 clientHeight: geometryDelegateProperty, | 3676 clientHeight: geometryDelegateProperty, |
3383 offsetLeft: geometryDelegateProperty, | 3677 offsetLeft: geometryDelegateProperty, |
3384 offsetTop: geometryDelegateProperty, | 3678 offsetTop: geometryDelegateProperty, |
3385 offsetWidth: geometryDelegateProperty, | 3679 offsetWidth: geometryDelegateProperty, |
3386 offsetHeight: geometryDelegateProperty, | 3680 offsetHeight: geometryDelegateProperty, |
3387 scrollLeft: geometryDelegatePropertySettable, | 3681 scrollLeft: geometryDelegatePropertySettable, |
3388 scrollTop: geometryDelegatePropertySettable, | 3682 scrollTop: geometryDelegatePropertySettable, |
3389 scrollWidth: geometryDelegateProperty, | 3683 scrollWidth: geometryDelegateProperty, |
3390 scrollHeight: geometryDelegateProperty | 3684 scrollHeight: geometryDelegateProperty |
3391 }); | 3685 }); |
3392 })(); | 3686 })(); |
3393 var innerTextProp = { | 3687 var textContentProp = { |
3394 enumerable: true, | 3688 enumerable: true, |
3395 get: nodeMethod(function () { | 3689 get: nodeMethod(function () { |
3396 var text = []; | 3690 var text = []; |
3397 innerTextOf(np(this).feral, text); | 3691 innerTextOf(np(this).feral, text); |
3398 return text.join(''); | 3692 return text.join(''); |
3399 }), | 3693 }), |
3400 set: nodeMethod(function (newText) { | 3694 set: nodeMethod(function (newText) { |
3401 // This operation changes the child node list (but not other | 3695 // This operation changes the child node list (but not other |
3402 // properties | 3696 // properties of the element) so it checks childrenEditable. Note that |
3403 // of the element) so it checks childrenEditable. Note that this check | 3697 // this check is critical to security, as else a client can set the |
3404 // is critical to security, as else a client can set the innerHTML of | 3698 // textContent of a <script> element to execute scripts. |
3405 // a <script> element to execute scripts. | 3699 np(this).policy.requireChildrenEditable(); |
3406 if (!np(this).childrenEditable) { throw new Error(NOT_EDITABLE); } | |
3407 var newTextStr = newText != null ? String(newText) : ''; | 3700 var newTextStr = newText != null ? String(newText) : ''; |
3408 var el = np(this).feral; | 3701 var el = np(this).feral; |
3409 for (var c; (c = el.firstChild);) { el.removeChild(c); } | 3702 for (var c; (c = el.firstChild);) { el.removeChild(c); } |
3410 if (newTextStr) { | 3703 if (newTextStr) { |
3411 el.appendChild(el.ownerDocument.createTextNode(newTextStr)); | 3704 el.appendChild(el.ownerDocument.createTextNode(newTextStr)); |
3412 } | 3705 } |
3413 }) | 3706 }) |
3414 }; | 3707 }; |
3415 var tagNameAttr = { | 3708 var tagNameAttr = { |
3416 enumerable: true, | 3709 enumerable: true, |
3417 get: nodeMethod(function () { | 3710 get: nodeMethod(function () { |
3418 return realToVirtualElementName(String(np(this).feral.tagName)); | 3711 return realToVirtualElementName(String(np(this).feral.tagName)); |
3419 }) | 3712 }) |
3420 }; | 3713 }; |
3421 definePropertiesAwesomely(TameElement.prototype, { | 3714 definePropertiesAwesomely(TameElement.prototype, { |
3422 id: NP.filterAttr(defaultToEmptyStr, identity), | 3715 id: NP.filterAttr(defaultToEmptyStr, identity), |
3423 className: { | 3716 className: { |
3424 enumerable: true, | 3717 enumerable: true, |
3425 get: nodeMethod(function () { | 3718 get: nodeMethod(function () { |
3426 return this.getAttribute('class') || ''; | 3719 return this.getAttribute('class') || ''; |
3427 }), | 3720 }), |
3428 set: nodeMethod(function (classes) { | 3721 set: nodeMethod(function (classes) { |
3429 return this.setAttribute('class', String(classes)); | 3722 return this.setAttribute('class', String(classes)); |
3430 }) | 3723 }) |
3431 }, | 3724 }, |
3432 title: NP.filterAttr(defaultToEmptyStr, String), | 3725 title: NP.filterAttr(defaultToEmptyStr, String), |
3433 dir: NP.filterAttr(defaultToEmptyStr, String), | 3726 dir: NP.filterAttr(defaultToEmptyStr, String), |
3434 innerText: innerTextProp, | 3727 textContent: textContentProp, |
3435 textContent: innerTextProp, | 3728 innerText: textContentProp, |
3729 // Note: Per MDN, innerText is actually subtly different than | |
3730 // textContent, in that innerText does not include text hidden via | |
3731 // styles, per MDN. We do not implement this difference. | |
3436 nodeName: tagNameAttr, | 3732 nodeName: tagNameAttr, |
3437 tagName: tagNameAttr, | 3733 tagName: tagNameAttr, |
3438 style: NP.filter( | 3734 style: NP.filter( |
3439 false, | 3735 false, |
3440 nodeMethod(function (styleNode) { | 3736 nodeMethod(function (styleNode) { |
3441 TameStyle || buildTameStyle(); | 3737 TameStyle || buildTameStyle(); |
3442 return new TameStyle(styleNode, np(this).editable, this); | 3738 return new TameStyle(styleNode, np(this).policy.editable, this); |
3443 }), | 3739 }), |
3444 true, identity), | 3740 true, identity), |
3445 innerHTML: { | 3741 innerHTML: { |
3446 enumerable: true, | 3742 enumerable: true, |
3447 get: nodeMethod(function () { | 3743 get: nodeMethod(function () { |
3448 var node = np(this).feral; | 3744 return htmlFragmentSerialization(this); |
3449 var tagName = node.tagName.toLowerCase(); | |
3450 var schemaElem = htmlSchema.element(tagName); | |
3451 if (!schemaElem.allowed) { | |
3452 return ''; // unknown node | |
3453 } | |
3454 var innerHtml = node.innerHTML; | |
3455 if (schemaElem.contentIsCDATA) { | |
3456 innerHtml = html.escapeAttrib(innerHtml); | |
3457 } else if (schemaElem.contentIsRCDATA) { | |
3458 // Make sure we return PCDATA. | |
3459 // For RCDATA we only need to escape & if they're not part of an | |
3460 // entity. | |
3461 innerHtml = html.normalizeRCData(innerHtml); | |
3462 } else { | |
3463 // If we blessed the resulting HTML, then this would round trip | |
3464 // better but it would still not survive appending, and it would | |
3465 // propagate event handlers where the setter of innerHTML does not | |
3466 // expect it to. | |
3467 innerHtml = tameInnerHtml(innerHtml); | |
3468 } | |
3469 return innerHtml; | |
3470 }), | 3745 }), |
3471 set: nodeMethod(function (htmlFragment) { | 3746 set: nodeMethod(function (htmlFragment) { |
3472 // This operation changes the child node list (but not other | 3747 // This operation changes the child node list (but not other |
3473 // properties of the element) so it checks childrenEditable. Note | 3748 // properties of the element) so it checks childrenEditable. Note |
3474 // that | 3749 // that this check is critical to security, as else a client can set |
3475 // this check is critical to security, as else a client can set the | 3750 // the innerHTML of a <script> element to execute scripts. |
3476 // innerHTML of a <script> element to execute scripts. | 3751 np(this).policy.requireChildrenEditable(); |
3477 if (!np(this).childrenEditable) { throw new Error(NOT_EDITABLE); } | |
3478 var node = np(this).feral; | 3752 var node = np(this).feral; |
3479 var schemaElem = htmlSchema.element(node.tagName); | 3753 var schemaElem = htmlSchema.element(node.tagName); |
3480 if (!schemaElem.allowed) { throw new Error(); } | 3754 if (!schemaElem.allowed) { throw new Error(); } |
3481 var isRCDATA = schemaElem.contentIsRCDATA; | 3755 var isRCDATA = schemaElem.contentIsRCDATA; |
3482 var htmlFragmentString; | 3756 var htmlFragmentString; |
3483 if (!isRCDATA && htmlFragment instanceof Html) { | 3757 if (!isRCDATA && htmlFragment instanceof Html) { |
3484 htmlFragmentString = '' + safeHtml(htmlFragment); | 3758 htmlFragmentString = '' + safeHtml(htmlFragment); |
3485 } else if (htmlFragment === null) { | 3759 } else if (htmlFragment === null) { |
3486 htmlFragmentString = ''; | 3760 htmlFragmentString = ''; |
3487 } else { | 3761 } else { |
(...skipping 16 matching lines...) Expand all Loading... | |
3504 if (!feralOffsetParent) { | 3778 if (!feralOffsetParent) { |
3505 return feralOffsetParent; | 3779 return feralOffsetParent; |
3506 } else if (feralOffsetParent === containerNode) { | 3780 } else if (feralOffsetParent === containerNode) { |
3507 // Return the body if the node is contained in the body. This is | 3781 // Return the body if the node is contained in the body. This is |
3508 // emulating how browsers treat offsetParent and the real <BODY>. | 3782 // emulating how browsers treat offsetParent and the real <BODY>. |
3509 var feralBody = np(tameDocument.body).feral; | 3783 var feralBody = np(tameDocument.body).feral; |
3510 for (var ancestor = makeDOMAccessible(np(this).feral.parentNode); | 3784 for (var ancestor = makeDOMAccessible(np(this).feral.parentNode); |
3511 ancestor !== containerNode; | 3785 ancestor !== containerNode; |
3512 ancestor = makeDOMAccessible(ancestor.parentNode)) { | 3786 ancestor = makeDOMAccessible(ancestor.parentNode)) { |
3513 if (ancestor === feralBody) { | 3787 if (ancestor === feralBody) { |
3514 return defaultTameNode(feralBody, np(this).editable); | 3788 return defaultTameNode(feralBody); |
3515 } | 3789 } |
3516 } | 3790 } |
3517 return null; | 3791 return null; |
3518 } else { | 3792 } else { |
3519 return tameRelatedNode(feralOffsetParent, np(this).editable, | 3793 return tameRelatedNode(feralOffsetParent, defaultTameNode); |
3520 defaultTameNode); | |
3521 } | 3794 } |
3522 }) | 3795 }) |
3523 }, | 3796 }, |
3524 accessKey: NP.rw, | 3797 accessKey: NP.rw, |
3525 tabIndex: NP.rw | 3798 tabIndex: NP.rw |
3526 }); | 3799 }); |
3527 cajaVM.def(TameElement); // and its prototype | 3800 cajaVM.def(TameElement); // and its prototype |
3528 | 3801 |
3529 traceStartup("DT: starting defineElement"); | 3802 traceStartup("DT: starting defineElement"); |
3530 | 3803 |
3531 /** | 3804 /** |
3532 * Define a taming class for a subclass of HTMLElement. | 3805 * Define a taming class for a subclass of HTMLElement. |
3533 * | 3806 * |
3534 * @param {Array} record.superclass The tame superclass constructor | 3807 * @param {Array} record.superclass The tame superclass constructor |
3535 * (defaults to TameElement) with parameters (this, node, editable, | 3808 * (defaults to TameElement) with parameters (this, node, policy, |
3536 * childrenEditable, opt_proxyType). | 3809 * opt_proxyType). |
3537 * @param {Array} record.names The element names which should be tamed | 3810 * @param {Array} record.names The element names which should be tamed |
3538 * using this class. | 3811 * using this class. |
3539 * @param {String} record.domClass The DOM-specified class name. | 3812 * @param {String} record.domClass The DOM-specified class name. |
3540 * @param {Object} record.properties The custom properties this class | 3813 * @param {Object} record.properties The custom properties this class |
3541 * should have (in the format accepted by definePropertiesAwesomely). | 3814 * should have (in the format accepted by definePropertiesAwesomely). |
3542 * @param {function} record.construct Code to invoke at the end of | 3815 * @param {function} record.construct Code to invoke at the end of |
3543 * construction; takes and returns self. | 3816 * construction; takes and returns self. |
3544 * @param {boolean} record.forceChildrenNotEditable Whether to force the | 3817 * @param {boolean} record.forceChildrenNotEditable Whether to force the |
3545 * childrenEditable flag to be false regardless of the value of | 3818 * child node list and child nodes to not be mutable. |
3546 * editable. | |
3547 * @return {function} The constructor. | 3819 * @return {function} The constructor. |
3548 */ | 3820 */ |
3549 function defineElement(record) { | 3821 function defineElement(record) { |
3550 var superclass = record.superclass || TameElement; | 3822 var superclass = record.superclass || TameElement; |
3551 var proxyType = record.proxyType; | 3823 var proxyType = record.proxyType; |
3552 var construct = record.construct || identity; | 3824 var construct = record.construct || identity; |
3553 var shouldBeVirtualized = "virtualized" in record | 3825 var shouldBeVirtualized = "virtualized" in record |
3554 ? record.virtualized : false; | 3826 ? record.virtualized : false; |
3555 var forceChildrenNotEditable = record.forceChildrenNotEditable; | 3827 var opt_policy = record.forceChildrenNotEditable |
3556 function TameSpecificElement(node, editable, childrenEditable) { | 3828 ? nodePolicyReadOnlyChildren : null; |
3829 function TameSpecificElement(node) { | |
3557 var isVirtualized = htmlSchema.isVirtualizedElementName(node.tagName); | 3830 var isVirtualized = htmlSchema.isVirtualizedElementName(node.tagName); |
3558 if (shouldBeVirtualized !== null && | 3831 if (shouldBeVirtualized !== null && |
3559 !isVirtualized !== !shouldBeVirtualized) { | 3832 !isVirtualized !== !shouldBeVirtualized) { |
3560 throw new Error("Domado internal inconsistency: " + node.tagName + | 3833 throw new Error("Domado internal inconsistency: " + node.tagName + |
3561 " has inconsistent virtualization state with class " + | 3834 " has inconsistent virtualization state with class " + |
3562 record.domClass); | 3835 record.domClass); |
3563 } | 3836 } |
3564 superclass.call(this, | 3837 superclass.call(this, node, opt_policy, proxyType); |
3565 node, | |
3566 editable, | |
3567 childrenEditable && !forceChildrenNotEditable, | |
3568 proxyType); | |
3569 construct.call(this); | 3838 construct.call(this); |
3570 } | 3839 } |
3571 inertCtor(TameSpecificElement, superclass, record.domClass); | 3840 inertCtor(TameSpecificElement, superclass, record.domClass); |
3572 definePropertiesAwesomely(TameSpecificElement.prototype, | 3841 definePropertiesAwesomely(TameSpecificElement.prototype, |
3573 record.properties || {}); | 3842 record.properties || {}); |
3574 // Note: cajaVM.def will be applied to all registered node classes | 3843 // Note: cajaVM.def will be applied to all registered node classes |
3575 // later, so users of defineElement don't need to. | 3844 // later, so users of defineElement don't need to. |
3576 return TameSpecificElement; | 3845 return TameSpecificElement; |
3577 } | 3846 } |
3578 cajaVM.def(defineElement); | 3847 cajaVM.def(defineElement); |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3834 return Object.freeze(tameGradient); | 4103 return Object.freeze(tameGradient); |
3835 } | 4104 } |
3836 function enforceFinite(value, name) { | 4105 function enforceFinite(value, name) { |
3837 enforceType(value, 'number', name); | 4106 enforceType(value, 'number', name); |
3838 if (!isFinite(value)) { | 4107 if (!isFinite(value)) { |
3839 throw new Error("NOT_SUPPORTED_ERR"); | 4108 throw new Error("NOT_SUPPORTED_ERR"); |
3840 // TODO(kpreid): should be a DOMException per spec | 4109 // TODO(kpreid): should be a DOMException per spec |
3841 } | 4110 } |
3842 } | 4111 } |
3843 | 4112 |
3844 function TameCanvasElement(node, editable) { | 4113 function TameCanvasElement(node) { |
3845 // TODO(kpreid): review whether this can use defineElement | 4114 // TODO(kpreid): review whether this can use defineElement |
3846 TameElement.call(this, node, editable, editable); | 4115 TameElement.call(this, node); |
3847 | 4116 |
3848 // helpers for tame context | 4117 // helpers for tame context |
3849 var context = makeDOMAccessible(node.getContext('2d')); | 4118 var context = makeDOMAccessible(node.getContext('2d')); |
3850 function tameFloatsOp(count, verb) { | 4119 function tameFloatsOp(count, verb) { |
3851 var m = makeFunctionAccessible(context[verb]); | 4120 var m = makeFunctionAccessible(context[verb]); |
3852 return cajaVM.def(function () { | 4121 return cajaVM.def(function () { |
3853 if (arguments.length !== count) { | 4122 if (arguments.length !== count) { |
3854 throw new Error(verb + ' takes ' + count + ' args, not ' + | 4123 throw new Error(verb + ' takes ' + count + ' args, not ' + |
3855 arguments.length); | 4124 arguments.length); |
3856 } | 4125 } |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4205 StringTest([ | 4474 StringTest([ |
4206 "top", | 4475 "top", |
4207 "hanging", | 4476 "hanging", |
4208 "middle", | 4477 "middle", |
4209 "alphabetic", | 4478 "alphabetic", |
4210 "ideographic", | 4479 "ideographic", |
4211 "bottom" | 4480 "bottom" |
4212 ])) | 4481 ])) |
4213 }); | 4482 }); |
4214 TameContext2DConf.confide(tameContext2d); | 4483 TameContext2DConf.confide(tameContext2d); |
4215 TameContext2DConf.p(tameContext2d).editable = np(this).editable; | 4484 TameContext2DConf.p(tameContext2d).policy = np(this).policy; |
4216 TameContext2DConf.p(tameContext2d).feral = context; | 4485 TameContext2DConf.p(tameContext2d).feral = context; |
4217 cajaVM.def(tameContext2d); | 4486 cajaVM.def(tameContext2d); |
4218 taming.permitUntaming(tameContext2d); | 4487 taming.permitUntaming(tameContext2d); |
4219 } // end of TameCanvasElement | 4488 } // end of TameCanvasElement |
4220 inertCtor(TameCanvasElement, TameElement, 'HTMLCanvasElement'); | 4489 inertCtor(TameCanvasElement, TameElement, 'HTMLCanvasElement'); |
4221 TameCanvasElement.prototype.getContext = function (contextId) { | 4490 TameCanvasElement.prototype.getContext = function (contextId) { |
4222 | 4491 |
4223 // TODO(kpreid): We can refine this by inventing a | 4492 // TODO(kpreid): We can refine this by inventing a ReadOnlyCanvas |
4224 // ReadOnlyCanvas object | 4493 // object to return in this situation, which allows getImageData and |
4225 // to return in this situation, which allows getImageData and | 4494 // so on but not any drawing. Not bothering to do that for now; if |
4226 // so on but | 4495 // you have a use for it let us know. |
4227 // not any drawing. Not bothering to do that for now; if | 4496 np(this).policy.requireEditable(); |
4228 // you have a use | |
4229 // for it let us know. | |
4230 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | |
4231 | 4497 |
4232 enforceType(contextId, 'string', 'contextId'); | 4498 enforceType(contextId, 'string', 'contextId'); |
4233 switch (contextId) { | 4499 switch (contextId) { |
4234 case '2d': | 4500 case '2d': |
4235 return np(this).tameContext2d; | 4501 return np(this).tameContext2d; |
4236 default: | 4502 default: |
4237 // http://dev.w3.org/html5/spec/the-canvas-element.html#the-canvas -element | 4503 // http://dev.w3.org/html5/spec/the-canvas-element.html#the-canvas -element |
4238 // says: The getContext(contextId, args...) method of the canvas | 4504 // says: The getContext(contextId, args...) method of the canvas |
4239 // element, when invoked, must run the following steps: | 4505 // element, when invoked, must run the following steps: |
4240 // [...] | 4506 // [...] |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4302 | 4568 |
4303 var TameFormElement = defineElement({ | 4569 var TameFormElement = defineElement({ |
4304 domClass: 'HTMLFormElement', | 4570 domClass: 'HTMLFormElement', |
4305 proxyType: FormElementAndExpandoProxyHandler, | 4571 proxyType: FormElementAndExpandoProxyHandler, |
4306 properties: { | 4572 properties: { |
4307 action: NP.filterAttr(defaultToEmptyStr, String), | 4573 action: NP.filterAttr(defaultToEmptyStr, String), |
4308 elements: { | 4574 elements: { |
4309 enumerable: true, | 4575 enumerable: true, |
4310 get: nodeMethod(function () { | 4576 get: nodeMethod(function () { |
4311 return tameHTMLCollection( | 4577 return tameHTMLCollection( |
4312 np(this).feral.elements, np(this).editable, defaultTameNode); | 4578 np(this).feral.elements, defaultTameNode); |
4313 }) | 4579 }) |
4314 }, | 4580 }, |
4315 enctype: NP.filterAttr(defaultToEmptyStr, String), | 4581 enctype: NP.filterAttr(defaultToEmptyStr, String), |
4316 method: NP.filterAttr(defaultToEmptyStr, String), | 4582 method: NP.filterAttr(defaultToEmptyStr, String), |
4317 target: NP.filterAttr(defaultToEmptyStr, String) | 4583 target: NP.filterAttr(defaultToEmptyStr, String) |
4318 }, | 4584 }, |
4319 construct: function () { | 4585 construct: function () { |
4320 // Freeze length at creation time since we aren't live. | 4586 // Freeze length at creation time since we aren't live. |
4321 // TODO(kpreid): Revise this when we have live node lists. | 4587 // TODO(kpreid): Revise this when we have live node lists. |
4322 Object.defineProperty(this, "length", { | 4588 Object.defineProperty(this, "length", { |
4323 value: np(this).feral.length | 4589 value: np(this).feral.length |
4324 }); | 4590 }); |
4325 } | 4591 } |
4326 }); | 4592 }); |
4593 // TODO(felix8a): need to test handlingUserAction. | |
4327 TameFormElement.prototype.submit = nodeMethod(function () { | 4594 TameFormElement.prototype.submit = nodeMethod(function () { |
4328 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4595 np(this).policy.requireEditable(); |
4329 return np(this).feral.submit(); | 4596 return domicile.handlingUserAction && np(this).feral.submit(); |
4330 }); | 4597 }); |
4331 TameFormElement.prototype.reset = nodeMethod(function () { | 4598 TameFormElement.prototype.reset = nodeMethod(function () { |
4332 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4599 np(this).policy.requireEditable(); |
4333 return np(this).feral.reset(); | 4600 return np(this).feral.reset(); |
4334 }); | 4601 }); |
4335 | 4602 |
4336 defineTrivialElement('HTMLHeadingElement'); | 4603 defineTrivialElement('HTMLHeadingElement'); |
4337 defineTrivialElement('HTMLHRElement'); | 4604 defineTrivialElement('HTMLHRElement'); |
4338 | 4605 |
4339 defineElement({ | 4606 defineElement({ |
4340 virtualized: true, | 4607 virtualized: true, |
4341 domClass: 'HTMLHeadElement' | 4608 domClass: 'HTMLHeadElement' |
4342 }); | 4609 }); |
(...skipping 17 matching lines...) Expand all Loading... | |
4360 construct: function () { | 4627 construct: function () { |
4361 np(this).childrenEditable = false; | 4628 np(this).childrenEditable = false; |
4362 }, | 4629 }, |
4363 properties: { | 4630 properties: { |
4364 align: { | 4631 align: { |
4365 enumerable: true, | 4632 enumerable: true, |
4366 get: nodeMethod(function () { | 4633 get: nodeMethod(function () { |
4367 return np(this).feral.align; | 4634 return np(this).feral.align; |
4368 }), | 4635 }), |
4369 set: nodeMethod(function (alignment) { | 4636 set: nodeMethod(function (alignment) { |
4370 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4637 np(this).policy.requireEditable(); |
4371 alignment = String(alignment); | 4638 alignment = String(alignment); |
4372 if (alignment === 'left' || | 4639 if (alignment === 'left' || |
4373 alignment === 'right' || | 4640 alignment === 'right' || |
4374 alignment === 'center') { | 4641 alignment === 'center') { |
4375 np(this).feral.align = alignment; | 4642 np(this).feral.align = alignment; |
4376 } | 4643 } |
4377 }) | 4644 }) |
4378 }, | 4645 }, |
4379 frameBorder: { | 4646 frameBorder: { |
4380 enumerable: true, | 4647 enumerable: true, |
4381 get: nodeMethod(function () { | 4648 get: nodeMethod(function () { |
4382 return np(this).feral.frameBorder; | 4649 return np(this).feral.frameBorder; |
4383 }), | 4650 }), |
4384 set: nodeMethod(function (border) { | 4651 set: nodeMethod(function (border) { |
4385 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4652 np(this).policy.requireEditable(); |
4386 border = String(border).toLowerCase(); | 4653 border = String(border).toLowerCase(); |
4387 if (border === '0' || border === '1' || | 4654 if (border === '0' || border === '1' || |
4388 border === 'no' || border === 'yes') { | 4655 border === 'no' || border === 'yes') { |
4389 np(this).feral.frameBorder = border; | 4656 np(this).feral.frameBorder = border; |
4390 } | 4657 } |
4391 }) | 4658 }) |
4392 }, | 4659 }, |
4393 height: NP.filterProp(identity, Number), | 4660 height: NP.filterProp(identity, Number), |
4394 width: NP.filterProp(identity, Number), | 4661 width: NP.filterProp(identity, Number), |
4395 src: P_blacklist, | 4662 src: P_blacklist, |
(...skipping 10 matching lines...) Expand all Loading... | |
4406 } | 4673 } |
4407 return null; | 4674 return null; |
4408 })); | 4675 })); |
4409 setOwn(TameIFrameElement.prototype, 'setAttribute', | 4676 setOwn(TameIFrameElement.prototype, 'setAttribute', |
4410 nodeMethod(function (attr, value) { | 4677 nodeMethod(function (attr, value) { |
4411 var attrLc = String(attr).toLowerCase(); | 4678 var attrLc = String(attr).toLowerCase(); |
4412 // The 'name' and 'src' attributes are whitelisted for all tags in | 4679 // The 'name' and 'src' attributes are whitelisted for all tags in |
4413 // html4-attributes-whitelist.json, since they're needed on tags | 4680 // html4-attributes-whitelist.json, since they're needed on tags |
4414 // like <img>. Because there's currently no way to filter attributes | 4681 // like <img>. Because there's currently no way to filter attributes |
4415 // based on the tag, we have to blacklist these two here. | 4682 // based on the tag, we have to blacklist these two here. |
4683 // TODO(kpreid): Don't we have per-attribute filtering now? | |
4416 if (attrLc !== 'name' && attrLc !== 'src') { | 4684 if (attrLc !== 'name' && attrLc !== 'src') { |
4417 return TameElement.prototype.setAttribute.call(this, attr, value); | 4685 return TameElement.prototype.setAttribute.call(this, attr, value); |
4418 } | 4686 } |
4419 if (typeof console !== 'undefined') | 4687 if (typeof console !== 'undefined') |
4420 console.error('Cannot set the [' + attrLc + | 4688 console.error('Cannot set the [' + attrLc + |
4421 '] attribute of an iframe.'); | 4689 '] attribute of an iframe.'); |
4422 return value; | 4690 return value; |
4423 })); | 4691 })); |
4424 | 4692 |
4425 var TameImageElement = defineElement({ | 4693 var TameImageElement = defineElement({ |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4486 | 4754 |
4487 defineElement({ | 4755 defineElement({ |
4488 superclass: TameFormField, | 4756 superclass: TameFormField, |
4489 domClass: 'HTMLSelectElement', | 4757 domClass: 'HTMLSelectElement', |
4490 properties: { | 4758 properties: { |
4491 multiple: NP.rw, | 4759 multiple: NP.rw, |
4492 options: { | 4760 options: { |
4493 enumerable: true, | 4761 enumerable: true, |
4494 get: nodeMethod(function () { | 4762 get: nodeMethod(function () { |
4495 return new TameOptionsList( | 4763 return new TameOptionsList( |
4496 np(this).feral.options, | 4764 np(this).feral.options, defaultTameNode, 'name'); |
4497 np(this).editable, | |
4498 defaultTameNode, 'name'); | |
4499 }) | 4765 }) |
4500 }, | 4766 }, |
4501 selectedIndex: NP.filterProp(identity, toInt), | 4767 selectedIndex: NP.filterProp(identity, toInt), |
4502 type: NP.ro | 4768 type: NP.ro |
4503 } | 4769 } |
4504 }); | 4770 }); |
4505 | 4771 |
4506 defineElement({ | 4772 defineElement({ |
4507 superclass: TameFormField, | 4773 superclass: TameFormField, |
4508 domClass: 'HTMLTextAreaElement', | 4774 domClass: 'HTMLTextAreaElement', |
(...skipping 22 matching lines...) Expand all Loading... | |
4531 // TODO(kpreid): Justify these specialized filters. | 4797 // TODO(kpreid): Justify these specialized filters. |
4532 value: NP.filterProp( | 4798 value: NP.filterProp( |
4533 function (x) { return x == null ? null : String(x); }, | 4799 function (x) { return x == null ? null : String(x); }, |
4534 function (x) { return x == null ? '' : '' + x; }) | 4800 function (x) { return x == null ? '' : '' + x; }) |
4535 } | 4801 } |
4536 }); | 4802 }); |
4537 ······ | 4803 ······ |
4538 defineTrivialElement('HTMLParagraphElement'); | 4804 defineTrivialElement('HTMLParagraphElement'); |
4539 defineTrivialElement('HTMLPreElement'); | 4805 defineTrivialElement('HTMLPreElement'); |
4540 | 4806 |
4541 function dynamicCodeDispatchMaker(that) { | 4807 function dynamicCodeDispatchMaker(privates) { |
4542 window.cajaDynamicScriptCounter = | 4808 window.cajaDynamicScriptCounter = |
4543 window.cajaDynamicScriptCounter ? | 4809 window.cajaDynamicScriptCounter ? |
4544 window.cajaDynamicScriptCounter + 1 : 0; | 4810 window.cajaDynamicScriptCounter + 1 : 0; |
4545 var name = "caja_dynamic_script" + | 4811 var name = "caja_dynamic_script" + |
4546 window.cajaDynamicScriptCounter + '___'; | 4812 window.cajaDynamicScriptCounter + '___'; |
4547 window[name] = function() { | 4813 window[name] = function() { |
4548 try { | 4814 try { |
4549 if (that.src && | 4815 if (privates.src && |
4550 'function' === typeof domicile.evaluateUntrustedExternalScript) { | 4816 'function' === typeof domicile.evaluateUntrustedExternalScript) { |
4551 domicile.evaluateUntrustedExternalScript(that.src); | 4817 domicile.evaluateUntrustedExternalScript(privates.src); |
4552 } | 4818 } |
4553 } finally { | 4819 } finally { |
4554 window[name] = undefined; | 4820 window[name] = undefined; |
4555 } | 4821 } |
4556 }; | 4822 }; |
4557 return name + "();"; | 4823 return name + "();"; |
4558 } | 4824 } |
4559 | 4825 |
4560 var TameScriptElement = defineElement({ | 4826 var TameScriptElement = defineElement({ |
4561 domClass: 'HTMLScriptElement', | 4827 domClass: 'HTMLScriptElement', |
4562 forceChildrenNotEditable: true, | 4828 forceChildrenNotEditable: true, |
4563 properties: { | 4829 properties: { |
4564 src: NP.filter(false, identity, true, identity) | 4830 src: NP.filter(false, identity, true, identity) |
4565 }, | 4831 }, |
4566 construct: function () { | 4832 construct: function () { |
4567 var script = np(this); | 4833 var privates = np(this); |
4568 script.feral.appendChild( | 4834 privates.feral.appendChild( |
4569 document.createTextNode( | 4835 document.createTextNode( |
4570 dynamicCodeDispatchMaker(script))); | 4836 dynamicCodeDispatchMaker(privates))); |
4571 } | 4837 } |
4572 }); | 4838 }); |
4573 | 4839 |
4574 setOwn(TameScriptElement.prototype, 'setAttribute', nodeMethod( | 4840 setOwn(TameScriptElement.prototype, 'setAttribute', nodeMethod( |
4575 function (attrib, value) { | 4841 function (attrib, value) { |
4576 var feral = np(this).feral; | 4842 var feral = np(this).feral; |
4577 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4843 np(this).policy.requireEditable(); |
4578 TameElement.prototype.setAttribute.call(this, attrib, value); | 4844 TameElement.prototype.setAttribute.call(this, attrib, value); |
4579 var attribName = String(attrib).toLowerCase(); | 4845 var attribName = String(attrib).toLowerCase(); |
4580 if ("src" === attribName) { | 4846 if ("src" === attribName) { |
4581 np(this).src = String(value); | 4847 np(this).src = String(value); |
4582 } | 4848 } |
4583 })); | 4849 })); |
4584 | 4850 |
4585 defineTrivialElement('HTMLSpanElement'); | 4851 defineTrivialElement('HTMLSpanElement'); |
4586 | 4852 |
4587 defineElement({ | 4853 defineElement({ |
4588 domClass: 'HTMLTableColElement', | 4854 domClass: 'HTMLTableColElement', |
4589 properties: { | 4855 properties: { |
4590 align: NP.filterProp(identity, identity), | 4856 align: NP.filterProp(identity, identity), |
4591 vAlign: NP.filterProp(identity, identity) | 4857 vAlign: NP.filterProp(identity, identity) |
4592 } | 4858 } |
4593 }); | 4859 }); |
4594 ······ | 4860 ······ |
4595 defineTrivialElement('HTMLCaptionElement'); | 4861 defineTrivialElement('HTMLTableCaptionElement'); |
4596 ······ | 4862 ······ |
4597 var TameTableCellElement = defineElement({ | 4863 var TameTableCellElement = defineElement({ |
4598 domClass: 'HTMLTableCellElement', | 4864 domClass: 'HTMLTableCellElement', |
4599 properties: { | 4865 properties: { |
4600 colSpan: NP.filterProp(identity, identity), | 4866 colSpan: NP.filterProp(identity, identity), |
4601 rowSpan: NP.filterProp(identity, identity), | 4867 rowSpan: NP.filterProp(identity, identity), |
4602 cellIndex: NP.ro, | 4868 cellIndex: NP.ro, |
4603 noWrap: NP.filterProp(identity, identity) // HTML5 Obsolete | 4869 noWrap: NP.filterProp(identity, identity) // HTML5 Obsolete |
4604 } | 4870 } |
4605 }); | 4871 }); |
(...skipping 14 matching lines...) Expand all Loading... | |
4620 | 4886 |
4621 var TameTableRowElement = defineElement({ | 4887 var TameTableRowElement = defineElement({ |
4622 domClass: 'HTMLTableRowElement', | 4888 domClass: 'HTMLTableRowElement', |
4623 properties: { | 4889 properties: { |
4624 cells: { | 4890 cells: { |
4625 // TODO(kpreid): It would be most pleasing to find a way to generali ze | 4891 // TODO(kpreid): It would be most pleasing to find a way to generali ze |
4626 // all the accessors which are of the form | 4892 // all the accessors which are of the form |
4627 // return new TameNodeList(np(this).feral...., ..., ...) | 4893 // return new TameNodeList(np(this).feral...., ..., ...) |
4628 enumerable: true, | 4894 enumerable: true, |
4629 get: nodeMethod(function () { | 4895 get: nodeMethod(function () { |
4630 return new TameNodeList( | 4896 return new TameNodeList(np(this).feral.cells, defaultTameNode); |
4631 np(this).feral.cells, np(this).editable, defaultTameNode); | |
4632 }) | 4897 }) |
4633 }, | 4898 }, |
4634 rowIndex: NP.ro, | 4899 rowIndex: NP.ro, |
4635 sectionRowIndex: NP.ro | 4900 sectionRowIndex: NP.ro |
4636 } | 4901 } |
4637 }); | 4902 }); |
4638 TameTableRowElement.prototype.insertCell = nodeMethod(function (index) { | 4903 TameTableRowElement.prototype.insertCell = nodeMethod(function (index) { |
4639 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4904 np(this).policy.requireEditable(); |
4640 requireIntIn(index, -1, np(this).feral.cells.length); | 4905 requireIntIn(index, -1, np(this).feral.cells.length); |
4641 return defaultTameNode( | 4906 return defaultTameNode( |
4642 np(this).feral.insertCell(index), | 4907 np(this).feral.insertCell(index), |
4643 np(this).editable); | 4908 np(this).editable); |
4644 }); | 4909 }); |
4645 TameTableRowElement.prototype.deleteCell = nodeMethod(function (index) { | 4910 TameTableRowElement.prototype.deleteCell = nodeMethod(function (index) { |
4646 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4911 np(this).policy.requireEditable(); |
4647 requireIntIn(index, -1, np(this).feral.cells.length); | 4912 requireIntIn(index, -1, np(this).feral.cells.length); |
4648 np(this).feral.deleteCell(index); | 4913 np(this).feral.deleteCell(index); |
4649 }); | 4914 }); |
4650 | 4915 |
4651 var TameTableSectionElement = defineElement({ | 4916 var TameTableSectionElement = defineElement({ |
4652 domClass: 'HTMLTableSectionElement', | 4917 domClass: 'HTMLTableSectionElement', |
4653 properties: { | 4918 properties: { |
4654 rows: { | 4919 rows: { |
4655 enumerable: true, | 4920 enumerable: true, |
4656 get: nodeMethod(function () { | 4921 get: nodeMethod(function () { |
4657 return new TameNodeList( | 4922 return new TameNodeList(np(this).feral.rows, defaultTameNode); |
4658 np(this).feral.rows, np(this).editable, defaultTameNode); | |
4659 }) | 4923 }) |
4660 } | 4924 } |
4661 } | 4925 } |
4662 }); | 4926 }); |
4663 TameTableSectionElement.prototype.insertRow = nodeMethod(function(index) { | 4927 TameTableSectionElement.prototype.insertRow = nodeMethod(function(index) { |
4664 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4928 np(this).policy.requireEditable(); |
4665 requireIntIn(index, -1, np(this).feral.rows.length); | 4929 requireIntIn(index, -1, np(this).feral.rows.length); |
4666 return defaultTameNode(np(this).feral.insertRow(index), | 4930 return defaultTameNode(np(this).feral.insertRow(index)); |
4667 np(this).editable); | |
4668 }); | 4931 }); |
4669 TameTableSectionElement.prototype.deleteRow = nodeMethod(function(index) { | 4932 TameTableSectionElement.prototype.deleteRow = nodeMethod(function(index) { |
4670 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4933 np(this).policy.requireEditable(); |
4671 requireIntIn(index, -1, np(this).feral.rows.length); | 4934 requireIntIn(index, -1, np(this).feral.rows.length); |
4672 np(this).feral.deleteRow(index); | 4935 np(this).feral.deleteRow(index); |
4673 }); | 4936 }); |
4674 | 4937 |
4675 var TameTableElement = defineElement({ | 4938 var TameTableElement = defineElement({ |
4676 superclass: TameTableSectionElement, // nonstandard but sound | 4939 superclass: TameTableSectionElement, // nonstandard but sound |
4677 domClass: 'HTMLTableElement', | 4940 domClass: 'HTMLTableElement', |
4678 properties: { | 4941 properties: { |
4679 tBodies: { | 4942 tBodies: { |
4680 enumerable: true, | 4943 enumerable: true, |
4681 get: nodeMethod(function () { | 4944 get: nodeMethod(function () { |
4682 return new TameNodeList( | 4945 if (np(this).policy.childrenVisible) { |
4683 np(this).feral.tBodies, np(this).editable, defaultTameNode); | 4946 return new TameNodeList(np(this).feral.tBodies, |
4947 defaultTameNode); | |
4948 } else { | |
4949 return fakeNodeList([]); | |
4950 } | |
4684 }) | 4951 }) |
4685 }, | 4952 }, |
4686 tHead: NP_tameDescendant, | 4953 tHead: NP_tameDescendant, |
4687 tFoot: NP_tameDescendant, | 4954 tFoot: NP_tameDescendant, |
4688 cellPadding: NP.filterAttr(Number, fromInt), | 4955 cellPadding: NP.filterAttr(Number, fromInt), |
4689 cellSpacing: NP.filterAttr(Number, fromInt), | 4956 cellSpacing: NP.filterAttr(Number, fromInt), |
4690 border: NP.filterAttr(Number, fromInt) | 4957 border: NP.filterAttr(Number, fromInt) |
4691 } | 4958 } |
4692 }); | 4959 }); |
4693 TameTableElement.prototype.createTHead = nodeMethod(function () { | 4960 TameTableElement.prototype.createTHead = nodeMethod(function () { |
4694 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4961 np(this).policy.requireEditable(); |
4695 return defaultTameNode(np(this).feral.createTHead(), np(this).editable); | 4962 return defaultTameNode(np(this).feral.createTHead()); |
4696 }); | 4963 }); |
4697 TameTableElement.prototype.deleteTHead = nodeMethod(function () { | 4964 TameTableElement.prototype.deleteTHead = nodeMethod(function () { |
4698 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4965 np(this).policy.requireEditable(); |
4699 np(this).feral.deleteTHead(); | 4966 np(this).feral.deleteTHead(); |
4700 }); | 4967 }); |
4701 TameTableElement.prototype.createTFoot = nodeMethod(function () { | 4968 TameTableElement.prototype.createTFoot = nodeMethod(function () { |
4702 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4969 np(this).policy.requireEditable(); |
4703 return defaultTameNode(np(this).feral.createTFoot(), np(this).editable); | 4970 return defaultTameNode(np(this).feral.createTFoot()); |
4704 }); | 4971 }); |
4705 TameTableElement.prototype.deleteTFoot = nodeMethod(function () { | 4972 TameTableElement.prototype.deleteTFoot = nodeMethod(function () { |
4706 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4973 np(this).policy.requireEditable(); |
4707 np(this).feral.deleteTFoot(); | 4974 np(this).feral.deleteTFoot(); |
4708 }); | 4975 }); |
4709 TameTableElement.prototype.createCaption = nodeMethod(function () { | 4976 TameTableElement.prototype.createCaption = nodeMethod(function () { |
4710 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4977 np(this).policy.requireEditable(); |
4711 return defaultTameNode(np(this).feral.createCaption(), np(this).editable ); | 4978 return defaultTameNode(np(this).feral.createCaption()); |
4712 }); | 4979 }); |
4713 TameTableElement.prototype.deleteCaption = nodeMethod(function () { | 4980 TameTableElement.prototype.deleteCaption = nodeMethod(function () { |
4714 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4981 np(this).policy.requireEditable(); |
4715 np(this).feral.deleteCaption(); | 4982 np(this).feral.deleteCaption(); |
4716 }); | 4983 }); |
4717 TameTableElement.prototype.insertRow = nodeMethod(function (index) { | 4984 TameTableElement.prototype.insertRow = nodeMethod(function (index) { |
4718 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4985 np(this).policy.requireEditable(); |
4719 requireIntIn(index, -1, np(this).feral.rows.length); | 4986 requireIntIn(index, -1, np(this).feral.rows.length); |
4720 return defaultTameNode(np(this).feral.insertRow(index), | 4987 return defaultTameNode(np(this).feral.insertRow(index)); |
4721 np(this).editable); | |
4722 }); | 4988 }); |
4723 TameTableElement.prototype.deleteRow = nodeMethod(function (index) { | 4989 TameTableElement.prototype.deleteRow = nodeMethod(function (index) { |
4724 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 4990 np(this).policy.requireEditable(); |
4725 requireIntIn(index, -1, np(this).feral.rows.length); | 4991 requireIntIn(index, -1, np(this).feral.rows.length); |
4726 np(this).feral.deleteRow(index); | 4992 np(this).feral.deleteRow(index); |
4727 }); | 4993 }); |
4728 | 4994 |
4729 defineElement({ | 4995 defineElement({ |
4730 virtualized: true, | 4996 virtualized: true, |
4731 domClass: 'HTMLTitleElement' | 4997 domClass: 'HTMLTitleElement' |
4732 }); | 4998 }); |
4733 ······ | 4999 ······ |
4734 defineTrivialElement('HTMLUListElement'); | 5000 defineTrivialElement('HTMLUListElement'); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4780 } | 5046 } |
4781 return taming.tame(event); | 5047 return taming.tame(event); |
4782 } | 5048 } |
4783 | 5049 |
4784 var ep = TameEventConf.p.bind(TameEventConf); | 5050 var ep = TameEventConf.p.bind(TameEventConf); |
4785 | 5051 |
4786 var EP_RELATED = { | 5052 var EP_RELATED = { |
4787 enumerable: true, | 5053 enumerable: true, |
4788 extendedAccessors: true, | 5054 extendedAccessors: true, |
4789 get: eventMethod(function (prop) { | 5055 get: eventMethod(function (prop) { |
4790 // TODO(kpreid): Isn't it unsafe to be always editable=true here? | 5056 return tameRelatedNode(ep(this).feral[prop], defaultTameNode); |
4791 return tameRelatedNode(ep(this).feral[prop], true, | |
4792 defaultTameNode); | |
4793 }) | 5057 }) |
4794 }; | 5058 }; |
4795 | 5059 |
4796 function P_e_view(transform) { | 5060 function P_e_view(transform) { |
4797 return { | 5061 return { |
4798 enumerable: true, | 5062 enumerable: true, |
4799 extendedAccessors: true, | 5063 extendedAccessors: true, |
4800 get: eventMethod(function (prop) { | 5064 get: eventMethod(function (prop) { |
4801 return transform(ep(this).feral[prop]); | 5065 return transform(ep(this).feral[prop]); |
4802 }) | 5066 }) |
(...skipping 12 matching lines...) Expand all Loading... | |
4815 enumerable: true, | 5079 enumerable: true, |
4816 get: eventMethod(function () { | 5080 get: eventMethod(function () { |
4817 return bridal.untameEventType(String(ep(this).feral.type)); | 5081 return bridal.untameEventType(String(ep(this).feral.type)); |
4818 }) | 5082 }) |
4819 }, | 5083 }, |
4820 target: { | 5084 target: { |
4821 enumerable: true, | 5085 enumerable: true, |
4822 get: eventMethod(function () { | 5086 get: eventMethod(function () { |
4823 var event = ep(this).feral; | 5087 var event = ep(this).feral; |
4824 return tameRelatedNode( | 5088 return tameRelatedNode( |
4825 event.target || event.srcElement, true, defaultTameNode); | 5089 event.target || event.srcElement, defaultTameNode); |
4826 }) | 5090 }) |
4827 }, | 5091 }, |
4828 srcElement: { | 5092 srcElement: { |
4829 enumerable: true, | 5093 enumerable: true, |
4830 get: eventMethod(function () { | 5094 get: eventMethod(function () { |
4831 return tameRelatedNode(ep(this).feral.srcElement, true, | 5095 return tameRelatedNode(ep(this).feral.srcElement, defaultTameNode); |
4832 defaultTameNode); | |
4833 }) | 5096 }) |
4834 }, | 5097 }, |
4835 currentTarget: { | 5098 currentTarget: { |
4836 enumerable: true, | 5099 enumerable: true, |
4837 get: eventMethod(function () { | 5100 get: eventMethod(function () { |
4838 var e = ep(this).feral; | 5101 var e = ep(this).feral; |
4839 return tameRelatedNode(e.currentTarget, true, defaultTameNode); | 5102 return tameRelatedNode(e.currentTarget, defaultTameNode); |
4840 }) | 5103 }) |
4841 }, | 5104 }, |
4842 relatedTarget: { | 5105 relatedTarget: { |
4843 enumerable: true, | 5106 enumerable: true, |
4844 get: eventMethod(function () { | 5107 get: eventMethod(function () { |
4845 var e = ep(this).feral; | 5108 var e = ep(this).feral; |
4846 var t = e.relatedTarget; | 5109 var t = e.relatedTarget; |
4847 if (!t) { | 5110 if (!t) { |
4848 if (e.type === 'mouseout') { | 5111 if (e.type === 'mouseout') { |
4849 t = e.toElement; | 5112 t = e.toElement; |
4850 } else if (e.type === 'mouseover') { | 5113 } else if (e.type === 'mouseover') { |
4851 t = e.fromElement; | 5114 t = e.fromElement; |
4852 } | 5115 } |
4853 } | 5116 } |
4854 return tameRelatedNode(t, true, defaultTameNode); | 5117 return tameRelatedNode(t, defaultTameNode); |
4855 }), | 5118 }), |
4856 // relatedTarget is read-only. this dummy setter is because some code | 5119 // relatedTarget is read-only. this dummy setter is because some code |
4857 // tries to workaround IE by setting a relatedTarget when it's not | 5120 // tries to workaround IE by setting a relatedTarget when it's not |
4858 // set. | 5121 // set. |
4859 // code in a sandbox can't tell the difference between "falsey because | 5122 // code in a sandbox can't tell the difference between "falsey because |
4860 // relatedTarget is not supported" and "falsey because relatedTarget | 5123 // relatedTarget is not supported" and "falsey because relatedTarget |
4861 // is outside sandbox". | 5124 // is outside sandbox". |
4862 set: eventMethod(function () {}) | 5125 set: eventMethod(function () {}) |
4863 }, | 5126 }, |
4864 fromElement: EP_RELATED, | 5127 fromElement: EP_RELATED, |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4920 inherit(TameCustomHTMLEvent, TameEvent); | 5183 inherit(TameCustomHTMLEvent, TameEvent); |
4921 TameCustomHTMLEvent.prototype.initEvent | 5184 TameCustomHTMLEvent.prototype.initEvent |
4922 = eventMethod(function (type, bubbles, cancelable) { | 5185 = eventMethod(function (type, bubbles, cancelable) { |
4923 bridal.initEvent(ep(this).feral, type, bubbles, cancelable); | 5186 bridal.initEvent(ep(this).feral, type, bubbles, cancelable); |
4924 }); | 5187 }); |
4925 setOwn(TameCustomHTMLEvent.prototype, "toString", eventMethod(function () { | 5188 setOwn(TameCustomHTMLEvent.prototype, "toString", eventMethod(function () { |
4926 return '[Fake CustomEvent]'; | 5189 return '[Fake CustomEvent]'; |
4927 })); | 5190 })); |
4928 cajaVM.def(TameCustomHTMLEvent); // and its prototype | 5191 cajaVM.def(TameCustomHTMLEvent); // and its prototype |
4929 | 5192 |
4930 function TameHTMLDocument(doc, container, domain, editable) { | 5193 function TameHTMLDocument(doc, container, domain) { |
4931 traceStartup("DT: TameHTMLDocument begin"); | 5194 traceStartup("DT: TameHTMLDocument begin"); |
4932 TamePseudoNode.call(this, editable); | 5195 TamePseudoNode.call(this); |
4933 | 5196 |
4934 np(this).feralDoc = doc; | 5197 np(this).feralDoc = doc; |
4935 np(this).feralContainerNode = container; | 5198 np(this).feralContainerNode = container; |
4936 np(this).onLoadListeners = []; | 5199 np(this).onLoadListeners = []; |
4937 np(this).onDCLListeners = []; | 5200 np(this).onDCLListeners = []; |
4938 | 5201 |
4939 traceStartup("DT: TameHTMLDocument done private"); | 5202 traceStartup("DT: TameHTMLDocument done private"); |
4940 | 5203 |
4941 var tameContainer = defaultTameNode(container, editable); | 5204 var tameContainer = defaultTameNode(container); |
4942 np(this).tameContainerNode = tameContainer; | 5205 np(this).tameContainerNode = tameContainer; |
4943 | 5206 |
4944 definePropertiesAwesomely(this, { | 5207 definePropertiesAwesomely(this, { |
4945 domain: P_constant(domain) | 5208 domain: P_constant(domain) |
4946 }); | 5209 }); |
4947 | 5210 |
4948 installLocation(this); | 5211 installLocation(this); |
4949 } | 5212 } |
4950 inertCtor(TameHTMLDocument, TamePseudoNode, 'HTMLDocument'); | 5213 inertCtor(TameHTMLDocument, TamePseudoNode, 'HTMLDocument'); |
4951 definePropertiesAwesomely(TameHTMLDocument.prototype, { | 5214 definePropertiesAwesomely(TameHTMLDocument.prototype, { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4983 for (n = this.firstChild; n; n = n.nextSibling) { | 5246 for (n = this.firstChild; n; n = n.nextSibling) { |
4984 if (n.nodeType === 1) { return n; } | 5247 if (n.nodeType === 1) { return n; } |
4985 } | 5248 } |
4986 // None of our children are elements, fail | 5249 // None of our children are elements, fail |
4987 return null; | 5250 return null; |
4988 })}, | 5251 })}, |
4989 forms: { enumerable: true, get: nodeMethod(function () { | 5252 forms: { enumerable: true, get: nodeMethod(function () { |
4990 var tameForms = []; | 5253 var tameForms = []; |
4991 for (var i = 0; i < document.forms.length; i++) { | 5254 for (var i = 0; i < document.forms.length; i++) { |
4992 var tameForm = tameRelatedNode( | 5255 var tameForm = tameRelatedNode( |
4993 makeDOMAccessible(document.forms).item(i), | 5256 makeDOMAccessible(document.forms).item(i), defaultTameNode); |
4994 np(this).editable, defaultTameNode); | |
4995 // tameRelatedNode returns null if the node is not part of | 5257 // tameRelatedNode returns null if the node is not part of |
4996 // this node's virtual document. | 5258 // this node's virtual document. |
4997 if (tameForm !== null) { tameForms.push(tameForm); } | 5259 if (tameForm !== null) { tameForms.push(tameForm); } |
4998 } | 5260 } |
4999 return fakeNodeList(tameForms); | 5261 return fakeNodeList(tameForms); |
5000 })}, | 5262 })}, |
5001 title: { | 5263 title: { |
5002 // TODO(kpreid): get the title element pointer in conformant way | 5264 // TODO(kpreid): get the title element pointer in conformant way |
5003 | 5265 |
5004 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.htm l#document.title | 5266 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.htm l#document.title |
5005 // as of 2012-08-14 | 5267 // as of 2012-08-14 |
5006 enumerable: true, | 5268 enumerable: true, |
5007 get: nodeMethod(function() { | 5269 get: nodeMethod(function() { |
5008 var titleEl = this.getElementsByTagName('title')[0]; | 5270 var titleEl = this.getElementsByTagName('title')[0]; |
5009 return trimHTML5Spaces(titleEl.textContent); | 5271 return trimHTML5Spaces(titleEl.textContent); |
5010 }), | 5272 }), |
5011 set: nodeMethod(function(value) { | 5273 set: nodeMethod(function(value) { |
5012 var titleEl = this.getElementsByTagName('title')[0]; | 5274 var titleEl = this.getElementsByTagName('title')[0]; |
5013 titleEl.textContent = value; | 5275 titleEl.textContent = value; |
5014 }) | 5276 }) |
5015 }, | 5277 }, |
5016 compatMode: P_constant('CSS1Compat'), | 5278 compatMode: P_constant('CSS1Compat'), |
5017 ownerDocument: P_constant(null) | 5279 ownerDocument: P_constant(null) |
5018 }); | 5280 }); |
5019 TameHTMLDocument.prototype.getElementsByTagName = nodeMethod( | 5281 TameHTMLDocument.prototype.getElementsByTagName = nodeMethod( |
5020 function (tagName) { | 5282 function (tagName) { |
5021 tagName = String(tagName).toLowerCase(); | 5283 tagName = String(tagName).toLowerCase(); |
5022 return tameGetElementsByTagName( | 5284 return tameGetElementsByTagName(np(this).feralContainerNode, tagName); |
5023 np(this).feralContainerNode, tagName, np(this).editable); | |
5024 }); | 5285 }); |
5025 TameHTMLDocument.prototype.getElementsByClassName = nodeMethod( | 5286 TameHTMLDocument.prototype.getElementsByClassName = nodeMethod( |
5026 function (className) { | 5287 function (className) { |
5027 return tameGetElementsByClassName( | 5288 return tameGetElementsByClassName( |
5028 np(this).feralContainerNode, className, np(this).editable); | 5289 np(this).feralContainerNode, className); |
5029 }); | 5290 }); |
5030 TameHTMLDocument.prototype.addEventListener = | 5291 TameHTMLDocument.prototype.addEventListener = |
5031 nodeMethod(function (name, listener, useCapture) { | 5292 nodeMethod(function (name, listener, useCapture) { |
5032 if (name === 'DOMContentLoaded') { | 5293 if (name === 'DOMContentLoaded') { |
5033 domitaModules.ensureValidCallback(listener); | 5294 domitaModules.ensureValidCallback(listener); |
5034 np(tameDocument).onDCLListeners.push(listener); | 5295 np(tameDocument).onDCLListeners.push(listener); |
5035 } else { | 5296 } else { |
5036 return np(this).tameContainerNode.addEventListener( | 5297 return np(this).tameContainerNode.addEventListener( |
5037 name, listener, useCapture); | 5298 name, listener, useCapture); |
5038 } | 5299 } |
5039 }); | 5300 }); |
5040 TameHTMLDocument.prototype.removeEventListener = | 5301 TameHTMLDocument.prototype.removeEventListener = |
5041 nodeMethod(function (name, listener, useCapture) { | 5302 nodeMethod(function (name, listener, useCapture) { |
5042 return np(this).tameContainerNode.removeEventListener( | 5303 return np(this).tameContainerNode.removeEventListener( |
5043 name, listener, useCapture); | 5304 name, listener, useCapture); |
5044 }); | 5305 }); |
5045 TameHTMLDocument.prototype.createComment = nodeMethod(function (text) { | 5306 TameHTMLDocument.prototype.createComment = nodeMethod(function (text) { |
5046 return defaultTameNode(np(this).feralDoc.createComment(" "), true); | 5307 return defaultTameNode(np(this).feralDoc.createComment(" ")); |
5047 }); | 5308 }); |
5048 TameHTMLDocument.prototype.createDocumentFragment = nodeMethod(function () { | 5309 TameHTMLDocument.prototype.createDocumentFragment = nodeMethod(function () { |
5049 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 5310 np(this).policy.requireEditable(); |
5050 return defaultTameNode(np(this).feralDoc.createDocumentFragment(), true) ; | 5311 return defaultTameNode(np(this).feralDoc.createDocumentFragment()); |
5051 }); | 5312 }); |
5052 TameHTMLDocument.prototype.createElement = nodeMethod(function (tagName) { | 5313 TameHTMLDocument.prototype.createElement = nodeMethod(function (tagName) { |
5053 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 5314 np(this).policy.requireEditable(); |
5054 tagName = String(tagName).toLowerCase(); | 5315 tagName = String(tagName).toLowerCase(); |
5055 tagName = htmlSchema.virtualToRealElementName(tagName); | 5316 tagName = htmlSchema.virtualToRealElementName(tagName); |
5056 var newEl = np(this).feralDoc.createElement(tagName); | 5317 var newEl = np(this).feralDoc.createElement(tagName); |
5057 if ("canvas" == tagName) { | 5318 if ("canvas" == tagName) { |
5058 bridal.initCanvasElement(newEl); | 5319 bridal.initCanvasElement(newEl); |
5059 } | 5320 } |
5060 if (elementPolicies.hasOwnProperty(tagName)) { | 5321 if (elementPolicies.hasOwnProperty(tagName)) { |
5061 var attribs = elementPolicies[tagName]([]); | 5322 var attribs = elementPolicies[tagName]([]); |
5062 if (attribs) { | 5323 if (attribs) { |
5063 for (var i = 0; i < attribs.length; i += 2) { | 5324 for (var i = 0; i < attribs.length; i += 2) { |
5064 bridal.setAttribute(newEl, attribs[+i], attribs[i + 1]); | 5325 bridal.setAttribute(newEl, attribs[+i], attribs[i + 1]); |
5065 } | 5326 } |
5066 } | 5327 } |
5067 } | 5328 } |
5068 return defaultTameNode(newEl, true); | 5329 return defaultTameNode(newEl); |
5069 }); | 5330 }); |
5070 TameHTMLDocument.prototype.createTextNode = nodeMethod(function (text) { | 5331 TameHTMLDocument.prototype.createTextNode = nodeMethod(function (text) { |
5071 if (!np(this).editable) { throw new Error(NOT_EDITABLE); } | 5332 np(this).policy.requireEditable(); |
5072 return defaultTameNode(np(this).feralDoc.createTextNode( | 5333 return defaultTameNode(np(this).feralDoc.createTextNode( |
5073 text !== null && text !== void 0 ? '' + text : ''), true); | 5334 text !== null && text !== void 0 ? '' + text : '')); |
5074 }); | 5335 }); |
5075 TameHTMLDocument.prototype.getElementById = nodeMethod(function (id) { | 5336 TameHTMLDocument.prototype.getElementById = nodeMethod(function (id) { |
5076 id += idSuffix; | 5337 id += idSuffix; |
5077 var node = np(this).feralDoc.getElementById(id); | 5338 var node = np(this).feralDoc.getElementById(id); |
5078 return defaultTameNode(node, np(this).editable); | 5339 return defaultTameNode(node); |
5079 }); | 5340 }); |
5080 // http://www.w3.org/TR/DOM-Level-2-Events/events.html | 5341 // http://www.w3.org/TR/DOM-Level-2-Events/events.html |
5081 // #Events-DocumentEvent-createEvent | 5342 // #Events-DocumentEvent-createEvent |
5082 TameHTMLDocument.prototype.createEvent = nodeMethod(function (type) { | 5343 TameHTMLDocument.prototype.createEvent = nodeMethod(function (type) { |
5083 type = String(type); | 5344 type = String(type); |
5084 if (type !== 'HTMLEvents') { | 5345 if (type !== 'HTMLEvents') { |
5085 // See https://developer.mozilla.org/en/DOM/document.createEvent#Notes | 5346 // See https://developer.mozilla.org/en/DOM/document.createEvent#Notes |
5086 // for a long list of event ypes. | 5347 // for a long list of event ypes. |
5087 // See http://www.w3.org/TR/DOM-Level-2-Events/events.html | 5348 // See http://www.w3.org/TR/DOM-Level-2-Events/events.html |
5088 // #Events-eventgroupings | 5349 // #Events-eventgroupings |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5151 } | 5412 } |
5152 listeners = np(self).onLoadListeners; | 5413 listeners = np(self).onLoadListeners; |
5153 np(self).onLoadListeners = []; | 5414 np(self).onLoadListeners = []; |
5154 for (var i = 0, n = listeners.length; i < n; ++i) { | 5415 for (var i = 0, n = listeners.length; i < n; ++i) { |
5155 window.setTimeout(listeners[+i], 0); | 5416 window.setTimeout(listeners[+i], 0); |
5156 } | 5417 } |
5157 }); | 5418 }); |
5158 | 5419 |
5159 // For JavaScript handlers. See function dispatchEvent below | 5420 // For JavaScript handlers. See function dispatchEvent below |
5160 domicile.handlers = []; | 5421 domicile.handlers = []; |
5161 domicile.TameHTMLDocument = TameHTMLDocument; // Exposed for testing | |
5162 domicile.tameNode = cajaVM.def(defaultTameNode); | 5422 domicile.tameNode = cajaVM.def(defaultTameNode); |
5163 domicile.feralNode = cajaVM.def(function (tame) { | 5423 domicile.feralNode = cajaVM.def(function (tame) { |
5164 return np(tame).feral; // NOTE: will be undefined for pseudo nodes | 5424 return np(tame).feral; // NOTE: will be undefined for pseudo nodes |
5165 }); | 5425 }); |
5166 domicile.tameEvent = cajaVM.def(tameEvent); | 5426 domicile.tameEvent = cajaVM.def(tameEvent); |
5167 domicile.blessHtml = cajaVM.def(blessHtml); | 5427 domicile.blessHtml = cajaVM.def(blessHtml); |
5168 domicile.blessCss = cajaVM.def(function (var_args) { | 5428 domicile.blessCss = cajaVM.def(function (var_args) { |
5169 var arr = []; | 5429 var arr = []; |
5170 for (var i = 0, n = arguments.length; i < n; ++i) { | 5430 for (var i = 0, n = arguments.length; i < n; ++i) { |
5171 arr[+i] = arguments[+i]; | 5431 arr[+i] = arguments[+i]; |
(...skipping 403 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5575 }); | 5835 }); |
5576 } | 5836 } |
5577 | 5837 |
5578 traceStartup("DT: about to do TameHTMLDocument"); | 5838 traceStartup("DT: about to do TameHTMLDocument"); |
5579 var tameDocument = new TameHTMLDocument( | 5839 var tameDocument = new TameHTMLDocument( |
5580 document, | 5840 document, |
5581 containerNode, | 5841 containerNode, |
5582 // TODO(jasvir): Properly wire up document.domain | 5842 // TODO(jasvir): Properly wire up document.domain |
5583 // by untangling the cyclic dependence between | 5843 // by untangling the cyclic dependence between |
5584 // TameWindow and TameDocument | 5844 // TameWindow and TameDocument |
5585 String(undefined || 'nosuchhost.invalid'), | 5845 String(undefined || 'nosuchhost.invalid')); |
5586 true); | |
5587 traceStartup("DT: finished TameHTMLDocument"); | 5846 traceStartup("DT: finished TameHTMLDocument"); |
5588 domicile.htmlEmitterTarget = containerNode; | 5847 domicile.htmlEmitterTarget = containerNode; |
5589 | 5848 |
5590 // See spec at http://www.whatwg.org/specs/web-apps/current-work/multipage /browsers.html#navigator | 5849 // See spec at http://www.whatwg.org/specs/web-apps/current-work/multipage /browsers.html#navigator |
5591 // We don't attempt to hide or abstract userAgent details since | 5850 // We don't attempt to hide or abstract userAgent details since |
5592 // they are discoverable via side-channels we don't control. | 5851 // they are discoverable via side-channels we don't control. |
5593 var navigator = makeDOMAccessible(window.navigator); | 5852 var navigator = makeDOMAccessible(window.navigator); |
5594 var tameNavigator = cajaVM.def({ | 5853 var tameNavigator = cajaVM.def({ |
5595 appName: String(navigator.appName), | 5854 appName: String(navigator.appName), |
5596 appVersion: String(navigator.appVersion), | 5855 appVersion: String(navigator.appVersion), |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5652 * <p> | 5911 * <p> |
5653 * We can't provide access to the tamed window directly from document | 5912 * We can't provide access to the tamed window directly from document |
5654 * since it is the global scope of valija code, and so access to another | 5913 * since it is the global scope of valija code, and so access to another |
5655 * module's tamed window provides an unbounded amount of authority. | 5914 * module's tamed window provides an unbounded amount of authority. |
5656 * <p> | 5915 * <p> |
5657 * Instead, we expose styling, positioning, and sizing properties | 5916 * Instead, we expose styling, positioning, and sizing properties |
5658 * via this class. All of this authority is already available from the | 5917 * via this class. All of this authority is already available from the |
5659 * document. | 5918 * document. |
5660 */ | 5919 */ |
5661 function TameDefaultView() { | 5920 function TameDefaultView() { |
5662 // TODO(kpreid): The caller passes document's editable flag; this does n ot | |
5663 // take such a parameter. Which is right? | |
5664 // TODO(mikesamuel): Implement in terms of | 5921 // TODO(mikesamuel): Implement in terms of |
5665 // http://www.w3.org/TR/cssom-view/#the-windowview-interface | 5922 // http://www.w3.org/TR/cssom-view/#the-windowview-interface |
5666 // TODO: expose a read-only version of the document | 5923 // TODO: expose a read-only version of the document |
5667 this.document = tameDocument; | 5924 this.document = tameDocument; |
5668 // Exposing an editable default view that pointed to a read-only | 5925 // Exposing an editable default view that pointed to a read-only |
5669 // tameDocument via document.defaultView would allow escalation of | 5926 // tameDocument via document.defaultView would allow escalation of |
5670 // authority. | 5927 // authority. |
5671 assert(np(tameDocument).editable); | 5928 assert(np(tameDocument).policy.editable); |
5672 taming.permitUntaming(this); | 5929 taming.permitUntaming(this); |
5673 } | 5930 } |
5674 | 5931 |
5675 // Under ES53, the set/clear pairs get invoked with 'this' bound | 5932 // Under ES53, the set/clear pairs get invoked with 'this' bound |
5676 // to USELESS, which causes problems on Chrome unless they're wrpaped | 5933 // to USELESS, which causes problems on Chrome unless they're wrpaped |
5677 // this way. | 5934 // this way. |
5678 tameSetAndClear( | 5935 tameSetAndClear( |
5679 TameWindow.prototype, | 5936 TameWindow.prototype, |
5680 function (code, millis) { return window.setTimeout(code, millis); }, | 5937 function (code, millis) { return window.setTimeout(code, millis); }, |
5681 function (id) { return window.clearTimeout(id); }, | 5938 function (id) { return window.clearTimeout(id); }, |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5779 // events. | 6036 // events. |
5780 // opera: defined only on Opera. | 6037 // opera: defined only on Opera. |
5781 }, (function (propertyName, value) { | 6038 }, (function (propertyName, value) { |
5782 TameWindow.prototype[propertyName] = value; | 6039 TameWindow.prototype[propertyName] = value; |
5783 TameDefaultView.prototype[propertyName] = value; | 6040 TameDefaultView.prototype[propertyName] = value; |
5784 })); | 6041 })); |
5785 ······ | 6042 ······ |
5786 cajaVM.def(TameWindow); // and its prototype | 6043 cajaVM.def(TameWindow); // and its prototype |
5787 | 6044 |
5788 var tameWindow = new TameWindow(); | 6045 var tameWindow = new TameWindow(); |
5789 var tameDefaultView = new TameDefaultView(np(tameDocument).editable); | 6046 var tameDefaultView = new TameDefaultView(); |
5790 | 6047 |
5791 // Getters for properties which are installed on window AND defaultView. | 6048 // Getters for properties which are installed on window AND defaultView. |
5792 // See doc comment of TameDefaultView regarding authority to expose here. | 6049 // See doc comment of TameDefaultView regarding authority to expose here. |
5793 forOwnKeys({ | 6050 forOwnKeys({ |
5794 pageXOffset: function () { return this.scrollX; }, | 6051 pageXOffset: function () { return this.scrollX; }, |
5795 pageYOffset: function () { return this.scrollY; }, | 6052 pageYOffset: function () { return this.scrollY; }, |
5796 scrollX: function () { | 6053 scrollX: function () { |
5797 return np(tameDocument).feralContainerNode.scrollLeft; }, | 6054 return np(tameDocument).feralContainerNode.scrollLeft; }, |
5798 scrollY: function () { | 6055 scrollY: function () { |
5799 return np(tameDocument).feralContainerNode.scrollTop; }, | 6056 return np(tameDocument).feralContainerNode.scrollTop; }, |
(...skipping 16 matching lines...) Expand all Loading... | |
5816 // Attach reflexive properties to 'window' object | 6073 // Attach reflexive properties to 'window' object |
5817 var windowProps = ['top', 'self', 'opener', 'parent', 'window']; | 6074 var windowProps = ['top', 'self', 'opener', 'parent', 'window']; |
5818 var wpLen = windowProps.length; | 6075 var wpLen = windowProps.length; |
5819 for (var i = 0; i < wpLen; ++i) { | 6076 for (var i = 0; i < wpLen; ++i) { |
5820 var prop = windowProps[+i]; | 6077 var prop = windowProps[+i]; |
5821 tameWindow[prop] = tameWindow; | 6078 tameWindow[prop] = tameWindow; |
5822 } | 6079 } |
5823 | 6080 |
5824 Object.freeze(tameDefaultView); | 6081 Object.freeze(tameDefaultView); |
5825 | 6082 |
5826 if (np(tameDocument).editable) { | 6083 if (np(tameDocument).policy.editable) { |
5827 tameDocument.defaultView = tameDefaultView; | 6084 tameDocument.defaultView = tameDefaultView; |
5828 | 6085 |
5829 // Hook for document.write support. | 6086 // Hook for document.write support. |
5830 domicile.sanitizeAttrs = sanitizeAttrs; | 6087 domicile.sanitizeAttrs = sanitizeAttrs; |
5831 } | 6088 } |
5832 | 6089 |
5833 // Iterate over all node classes, assigning them to the Window object | 6090 // Iterate over all node classes, assigning them to the Window object |
5834 // under their DOM Level 2 standard name. They have been frozen above. | 6091 // under their DOM Level 2 standard name. They have been frozen above. |
5835 for (var name in nodeClasses) { | 6092 for (var name in nodeClasses) { |
5836 var ctor = nodeClasses[name]; | 6093 var ctor = nodeClasses[name]; |
5837 Object.defineProperty(tameWindow, name, { | 6094 Object.defineProperty(tameWindow, name, { |
5838 enumerable: true, | 6095 enumerable: true, |
5839 configurable: true, | 6096 configurable: true, |
5840 writable: true, | 6097 writable: true, |
5841 value: ctor | 6098 value: ctor |
5842 }); | 6099 }); |
5843 } | 6100 } |
5844 | 6101 |
5845 // TODO(ihab.awad): Build a more sophisticated virtual class hierarchy by | 6102 // TODO(ihab.awad): Build a more sophisticated virtual class hierarchy by |
5846 // creating a table of actual subclasses and instantiating tame nodes by | 6103 // having a table of subclass relationships and implementing them. |
5847 // table lookups. This will allow the client code to see a truly consisten t | 6104 |
5848 // DOM class hierarchy. | |
5849 | |
5850 // This is a list of all HTML-specific element node classes defined by | |
5851 // DOM Level 2 HTML, <http://www.w3.org/TR/DOM-Level-2-HTML/html.html>. | |
5852 // If a node class name in this list is not defined using defineElement or | 6105 // If a node class name in this list is not defined using defineElement or |
5853 // inertCtor above, then it will now be bound to the HTMLElement class. | 6106 // inertCtor above, then it will now be bound to the HTMLElement class. |
5854 var allDomNodeClasses = [ | 6107 var allDomNodeClasses = htmlSchema.getAllKnownScriptInterfaces(); |
5855 'HTMLAnchorElement', | |
5856 'HTMLAppletElement', | |
5857 'HTMLAreaElement', | |
5858 'HTMLBaseElement', | |
5859 'HTMLBaseFontElement', | |
5860 'HTMLBodyElement', | |
5861 'HTMLBRElement', | |
5862 'HTMLButtonElement', | |
5863 'HTMLDirectoryElement', | |
5864 'HTMLDivElement', | |
5865 'HTMLDListElement', | |
5866 'HTMLFieldSetElement', | |
5867 'HTMLFontElement', | |
5868 'HTMLFormElement', | |
5869 'HTMLFrameElement', | |
5870 'HTMLFrameSetElement', | |
5871 'HTMLHeadElement', | |
5872 'HTMLHeadingElement', | |
5873 'HTMLHRElement', | |
5874 'HTMLHtmlElement', | |
5875 'HTMLIFrameElement', | |
5876 'HTMLImageElement', | |
5877 'HTMLInputElement', | |
5878 'HTMLIsIndexElement', | |
5879 'HTMLLabelElement', | |
5880 'HTMLLegendElement', | |
5881 'HTMLLIElement', | |
5882 'HTMLLinkElement', | |
5883 'HTMLMapElement', | |
5884 'HTMLMenuElement', | |
5885 'HTMLMetaElement', | |
5886 'HTMLModElement', | |
5887 'HTMLNavElement', | |
5888 'HTMLObjectElement', | |
5889 'HTMLOListElement', | |
5890 'HTMLOptGroupElement', | |
5891 'HTMLOptionElement', | |
5892 'HTMLParagraphElement', | |
5893 'HTMLParamElement', | |
5894 'HTMLPreElement', | |
5895 'HTMLQuoteElement', | |
5896 'HTMLScriptElement', | |
5897 'HTMLSelectElement', | |
5898 'HTMLStyleElement', | |
5899 'HTMLTableCaptionElement', | |
5900 'HTMLTableCellElement', | |
Jasvir
2012/11/09 23:08:54
So should this become HTMLTableDataCell now? How
kpreid2
2012/11/12 20:29:10
No. HTMLTableCellElement is the superclass of HTML
| |
5901 'HTMLTableColElement', | |
5902 'HTMLTableElement', | |
5903 'HTMLTableRowElement', | |
5904 'HTMLTableSectionElement', | |
5905 'HTMLTextAreaElement', | |
5906 'HTMLTitleElement', | |
5907 'HTMLUListElement' | |
5908 ]; | |
5909 | |
5910 var defaultNodeClassCtor = nodeClasses.HTMLElement; | 6108 var defaultNodeClassCtor = nodeClasses.HTMLElement; |
5911 for (var i = 0; i < allDomNodeClasses.length; i++) { | 6109 for (var i = 0; i < allDomNodeClasses.length; i++) { |
5912 var className = allDomNodeClasses[+i]; | 6110 var className = allDomNodeClasses[+i]; |
5913 if (!(className in tameWindow)) { | 6111 if (!(className in tameWindow)) { |
5914 Object.defineProperty(tameWindow, className, { | 6112 Object.defineProperty(tameWindow, className, { |
5915 enumerable: true, | 6113 enumerable: true, |
5916 configurable: true, | 6114 configurable: true, |
5917 writable: true, | 6115 writable: true, |
5918 value: defaultNodeClassCtor | 6116 value: defaultNodeClassCtor |
5919 }); | 6117 }); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5972 | 6170 |
5973 traceStartup("DT: all done"); | 6171 traceStartup("DT: all done"); |
5974 | 6172 |
5975 return domicile; | 6173 return domicile; |
5976 } | 6174 } |
5977 | 6175 |
5978 /** | 6176 /** |
5979 * Function called from rewritten event handlers to dispatch an event safely . | 6177 * Function called from rewritten event handlers to dispatch an event safely . |
5980 */ | 6178 */ |
5981 function plugin_dispatchEvent(thisNode, event, pluginId, handler) { | 6179 function plugin_dispatchEvent(thisNode, event, pluginId, handler) { |
5982 event = makeDOMAccessible( | 6180 var window = bridalMaker.getWindow(thisNode, makeDOMAccessible); |
5983 event || bridalMaker.getWindow(thisNode, makeDOMAccessible).event); | 6181 event = makeDOMAccessible(event || window.event); |
5984 // support currentTarget on IE[678] | 6182 // support currentTarget on IE[678] |
5985 if (!event.currentTarget) { | 6183 if (!event.currentTarget) { |
5986 event.currentTarget = thisNode; | 6184 event.currentTarget = thisNode; |
5987 } | 6185 } |
5988 var imports = rulebreaker.getImports(pluginId); | 6186 var imports = rulebreaker.getImports(pluginId); |
5989 var domicile = windowToDomicile.get(imports); | 6187 var domicile = windowToDomicile.get(imports); |
5990 var node = domicile.tameNode(thisNode, true); | 6188 var node = domicile.tameNode(thisNode); |
6189 var isUserAction = eventIsUserAction(event, window); | |
5991 try { | 6190 try { |
5992 return plugin_dispatchToHandler( | 6191 return dispatch( |
5993 pluginId, handler, [ node, domicile.tameEvent(event), node ]); | 6192 isUserAction, pluginId, handler, |
6193 [ node, domicile.tameEvent(event), node ]); | |
5994 } catch (ex) { | 6194 } catch (ex) { |
5995 imports.onerror(ex.message, 'unknown', 0); | 6195 imports.onerror(ex.message, 'unknown', 0); |
5996 } | 6196 } |
5997 } | 6197 } |
5998 | 6198 |
6199 /** | |
6200 * Return true if event is a user action that can be expected to do | |
6201 * click(), focus(), etc. | |
6202 */ | |
6203 function eventIsUserAction(event, window) { | |
6204 if (!(event instanceof window.UIEvent)) { return false; } | |
6205 switch (event.type) { | |
6206 case 'click': | |
6207 case 'dblclick': | |
6208 case 'keypress': | |
6209 case 'keydown': | |
6210 case 'keyup': | |
6211 case 'mousedown': | |
6212 case 'mouseup': | |
6213 case 'touchstart': | |
6214 case 'touchend': | |
6215 return true; | |
6216 } | |
6217 return false; | |
6218 } | |
6219 | |
6220 /** | |
6221 * Called when user clicks on a javascript: link. | |
6222 */ | |
5999 function plugin_dispatchToHandler(pluginId, handler, args) { | 6223 function plugin_dispatchToHandler(pluginId, handler, args) { |
6000 var sig = ('' + handler).match(/^function\b[^\)]*\)/); | 6224 return dispatch(true, pluginId, handler, args); |
6225 } | |
6226 | |
6227 function dispatch(isUserAction, pluginId, handler, args) { | |
6001 var domicile = windowToDomicile.get(rulebreaker.getImports(pluginId)); | 6228 var domicile = windowToDomicile.get(rulebreaker.getImports(pluginId)); |
6002 if (domicile.domitaTrace & 0x1 && typeof console != 'undefined') { | 6229 if (domicile.domitaTrace & 0x1 && typeof console != 'undefined') { |
6230 var sig = ('' + handler).match(/^function\b[^\)]*\)/); | |
6003 console.log( | 6231 console.log( |
6004 'Dispatch pluginId=' + pluginId + | 6232 'Dispatch pluginId=' + pluginId + |
6005 ', handler=' + (sig ? sig[0] : handler) + | 6233 ', handler=' + (sig ? sig[0] : handler) + |
6006 ', args=' + args); | 6234 ', args=' + args); |
6007 } | 6235 } |
6008 switch (typeof handler) { | 6236 switch (typeof handler) { |
6009 case 'number': | 6237 case 'number': |
6010 handler = domicile.handlers[+handler]; | 6238 handler = domicile.handlers[+handler]; |
6011 break; | 6239 break; |
6012 case 'string': | 6240 case 'string': |
6013 var fn = void 0; | 6241 var fn = void 0; |
6014 fn = domicile.window[handler]; | 6242 fn = domicile.window[handler]; |
6015 handler = fn && typeof fn.call === 'function' ? fn : void 0; | 6243 handler = fn && typeof fn.call === 'function' ? fn : void 0; |
6016 break; | 6244 break; |
6017 case 'function': case 'object': break; | 6245 case 'function': case 'object': break; |
6018 default: | 6246 default: |
6019 throw new Error( | 6247 throw new Error( |
6020 'Expected function as event handler, not ' + typeof handler); | 6248 'Expected function as event handler, not ' + typeof handler); |
6021 } | 6249 } |
6022 domicile.isProcessingEvent = true; | 6250 domicile.handlingUserAction = isUserAction; |
6023 try { | 6251 try { |
6024 return handler.call.apply(handler, args); | 6252 return handler.call.apply(handler, args); |
6025 } catch (ex) { | 6253 } catch (ex) { |
6026 // guard against IE discarding finally blocks | 6254 // guard against IE discarding finally blocks |
6255 domicile.handlingUserAction = false; | |
6027 throw ex; | 6256 throw ex; |
6028 } finally { | 6257 } finally { |
6029 domicile.isProcessingEvent = false; | 6258 domicile.handlingUserAction = false; |
6030 } | 6259 } |
6031 } | 6260 } |
6032 | 6261 |
6033 return cajaVM.def({ | 6262 return cajaVM.def({ |
6034 attachDocument: attachDocument, | 6263 attachDocument: attachDocument, |
6035 plugin_dispatchEvent: plugin_dispatchEvent, | 6264 plugin_dispatchEvent: plugin_dispatchEvent, |
6036 plugin_dispatchToHandler: plugin_dispatchToHandler, | 6265 plugin_dispatchToHandler: plugin_dispatchToHandler, |
6037 getDomicileForWindow: windowToDomicile.get.bind(windowToDomicile) | 6266 getDomicileForWindow: windowToDomicile.get.bind(windowToDomicile) |
6038 }); | 6267 }); |
6039 }); | 6268 }); |
6040 })(); | 6269 })(); |
6041 | 6270 |
6042 // Exports for closure compiler. | 6271 // Exports for closure compiler. |
6043 if (typeof window !== 'undefined') { | 6272 if (typeof window !== 'undefined') { |
6044 window['Domado'] = Domado; | 6273 window['Domado'] = Domado; |
6045 } | 6274 } |
LEFT | RIGHT |