Revision: 4229
Author: [email protected]
Date: Fri Aug  6 22:04:29 2010
Log: [?1034hFixes issue 1228: support for innerText and textContent to domita.js
http://codereview.appspot.com/1689062

http://code.google.com/p/google-caja/issues/detail?id=1228

Adds to domita.js handlers setters and getters for textContent and
innerText for TameTextNode and TameElement.

[email protected]

http://code.google.com/p/google-caja/source/detail?r=4229

Modified:
 /trunk/src/com/google/caja/plugin/domita.js
 /trunk/tests/com/google/caja/plugin/domita_test.html
 /trunk/tests/com/google/caja/plugin/domita_test_untrusted.html

=======================================
--- /trunk/src/com/google/caja/plugin/domita.js Fri Aug  6 13:44:11 2010
+++ /trunk/src/com/google/caja/plugin/domita.js Fri Aug  6 22:04:29 2010
@@ -424,7 +424,7 @@
 /**
  * Add a tamed document implementation to a Gadget's global scope.
  *
- * Has the side effect of adding the classes "vdoc-body___" and
+ * Has the side effect of adding the classes "vdoc-body___" and
  * idSuffix.substring(1) to the pseudoBodyNode.
  *
  * @param {string} idSuffix a string suffix appended to all node IDs.
@@ -1163,6 +1163,7 @@
           throw 'Internal: Attr nodes cannot be generically wrapped';
           break;
         case 3:  // Text
+        case 4:  // CDATA Section Node
           tamed = new TameTextNode(node, editable);
           break;
         case 8:  // Comment
@@ -1449,7 +1450,7 @@
     var UNSAFE_TAGNAME = "Unsafe tag name.";
     var UNKNOWN_TAGNAME = "Unknown tag name.";
     var INDEX_SIZE_ERROR = "Index size error.";
-
+
     // Implementation of EventTarget::addEventListener
     function tameAddEventListener(name, listener, useCapture) {
       if (!this.editable___) { throw new Error(NOT_EDITABLE); }
@@ -2085,7 +2086,8 @@
       }

       TameBackedNode.call(this, node, editable, editable);
-      classUtils.exportFields(this, ['nodeValue', 'data']);
+      classUtils.exportFields(
+          this, ['nodeValue', 'data', 'textContent', 'innerText']);
     }
     inertCtor(TameTextNode, TameBackedNode, 'Text');
     TameTextNode.prototype.setNodeValue = function (value) {
@@ -2093,8 +2095,10 @@
       this.node___.nodeValue = String(value || '');
       return value;
     };
-    TameTextNode.prototype.getData = TameTextNode.prototype.getNodeValue;
-    TameTextNode.prototype.setData = TameTextNode.prototype.setNodeValue;
+ TameTextNode.prototype.getTextContent = TameTextNode.prototype.getInnerText + = TameTextNode.prototype.getData = TameTextNode.prototype.getNodeValue; + TameTextNode.prototype.setTextContent = TameTextNode.prototype.setInnerText + = TameTextNode.prototype.setData = TameTextNode.prototype.setNodeValue;
     TameTextNode.prototype.toString = function () {
       return '#text';
     };
@@ -2211,7 +2215,7 @@
       classUtils.exportFields(
           this,
           ['className', 'id', 'innerHTML', 'tagName', 'style',
-           'offsetParent', 'title', 'dir']);
+           'offsetParent', 'title', 'dir', 'innerText', 'textContent']);
       classUtils.applyAccessors(this, commonElementPropertyHandlers);
       registerElementScriptAttributeHandlers(this);
     }
@@ -2345,6 +2349,46 @@
       if (!this.editable___) { throw new Error(NOT_EDITABLE); }
       return this.setAttribute('dir', String(classes));
     };
+    function innerTextOf(rawNode, out) {
+      switch (rawNode.nodeType) {
+        case 1:  // Element
+          var tagName = rawNode.tagName.toLowerCase();
+          if (html4.ELEMENTS.hasOwnProperty(tagName)
+              && !(html4.ELEMENTS[tagName] & html4.eflags.UNSAFE)) {
+            // Not an opaque node.
+            for (var c = rawNode.firstChild; c; c = c.nextSibling) {
+              innerTextOf(c, out);
+            }
+          }
+          break;
+        case 3:  // Text Node
+        case 4:  // CDATA Section Node
+          out[out.length] = rawNode.data;
+          break;
+        case 11:  // Document Fragment
+          for (var c = rawNode.firstChild; c; c = c.nextSibling) {
+            innerTextOf(c, out);
+          }
+          break;
+      }
+    }
+ TameElement.prototype.getTextContent = TameElement.prototype.getInnerText
+        = function () {
+      var text = [];
+      innerTextOf(this.node___, text);
+      return text.join('');
+    };
+ TameElement.prototype.setTextContent = TameElement.prototype.setInnerText
+        = function (newText) {
+      if (!this.editable___) { throw new Error(NOT_EDITABLE); }
+      var newTextStr = newText != null ? String(newText) : '';
+      var el = this.node___;
+      for (var c; (c = el.firstChild);) { el.removeChild(c); }
+      if (newTextStr) {
+        el.appendChild(el.ownerDocument.createTextNode(newTextStr));
+      }
+      return newText;
+    };
TameElement.prototype.getTagName = TameBackedNode.prototype.getNodeName;
     TameElement.prototype.getInnerHTML = function () {
       var tagName = this.node___.tagName.toLowerCase();
@@ -4184,7 +4228,7 @@
   var imports = ___.getImports(pluginId);
   var node = imports.tameNode___(thisNode, true);
   return plugin_dispatchToHandler___(
-      pluginId, handler, [ node, imports.tameEvent___(event), node ]);
+      pluginId, handler, [ node, imports.tameEvent___(event), node ]);
 }

 function plugin_dispatchToHandler___(pluginId, handler, args) {
=======================================
--- /trunk/tests/com/google/caja/plugin/domita_test.html Thu Jul 29 17:32:52 2010 +++ /trunk/tests/com/google/caja/plugin/domita_test.html Fri Aug 6 22:04:29 2010
@@ -140,7 +140,7 @@

       <div class="testcontainer" id="test-opaque-nodes-xyz___"
><!-- Comment -->a<script id="howdy-script-xyz___">/* Howdy */</script
-       >b<object></object>c</div>
+       >b<object>obj</object>c</div>

       <!-- http://www.google.com/favicon.ico has been visited
            by the above IMG tag -->
=======================================
--- /trunk/tests/com/google/caja/plugin/domita_test_untrusted.html Thu Aug 5 13:23:18 2010 +++ /trunk/tests/com/google/caja/plugin/domita_test_untrusted.html Fri Aug 6 22:04:29 2010
@@ -524,6 +524,8 @@
 ></map
 ><img usemap="#foo" src="mappic.gif"></p>

+<p class="testcontainer" id="test-inner-text">Hello, <b>World!</b></p>
+
 <p class="testcontainer" id="test-document-body-appendChild"
>I should be the last element until something is appended to document.body</p>

@@ -3274,6 +3276,40 @@
   pass('test-usemap');
 });

+jsunitRegister('testInnerText',
+               function testInnerText() {
+  var testInnerTextNode = document.getElementById('test-inner-text');
+  assertEquals('IT1', 'Hello, World!', testInnerTextNode.innerText);
+  assertEquals('TC1', 'Hello, World!', testInnerTextNode.textContent);
+  assertEquals('IT1T', 'Hello, ', testInnerTextNode.firstChild.innerText);
+ assertEquals('TC1T', 'Hello, ', testInnerTextNode.firstChild.textContent); + assertEquals('IH1', 'Hello, <b>World!<\/b>', testInnerTextNode.innerHTML);
+
+  testInnerTextNode.innerText = 'Goodbye cruel plain <b>text<\/b>!';
+  assertEquals(
+      'IH2', 'Goodbye cruel plain &lt;b&gt;text&lt;/b&gt;!',
+      testInnerTextNode.innerHTML);
+  assertEquals(
+      'IT2', 'Goodbye cruel plain <b>text</b>!',
+      testInnerTextNode.firstChild.innerText);
+  assertEquals('CL2', 1, testInnerTextNode.childNodes.length);
+  testInnerTextNode.innerText = '';
+  assertEquals('CL3', 0, testInnerTextNode.childNodes.length);
+  assertEquals('IT3', '', testInnerTextNode.innerText);
+  testInnerTextNode.innerText = null;
+  assertEquals('CL4', 0, testInnerTextNode.childNodes.length);
+  testInnerTextNode.innerText = 42;
+  assertEquals('CL5', 1, testInnerTextNode.childNodes.length);
+  assertEquals('IT5', '42', testInnerTextNode.innerText);
+
+  // Opaque nodes appear to be empty.
+  assertEquals(
+      'ITON', 'abc',
+      document.getElementById('test-opaque-nodes').innerText);
+
+  pass('test-inner-text');
+});
+
 jsunitRegister('testDocumentBodyAppendChild',
                function testDocumentBodyAppendChild() {
   var notWhitespaceRe = new RegExp('\\S');

Reply via email to