Revision: 5448
Author:   [email protected]
Date:     Thu Jun 13 12:07:58 2013
Log:      Micro cleanup/optimization.
https://codereview.appspot.com/10234049

* Added a cache mapping element names to taming classes, which
  bypasses going through both the HTML schema and taming class table
  when taming a node. Barely improves performance according to the
  Chrome profiler.
* In defineElement ctors, don't call isVirtualizedElementName unless
  the result will be used.
* Put simpler conditions first in repair_DEFINE_PROPERTY.
* Remove setOwn and definePropertyAllowOverride in Domado in favor
  of more explicit and specialized forms which do not need to do
  reflection to decide what to do.
* Added missing documentation for virtualized parameter to
  defineElement.

[email protected], [email protected]

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

Modified:
 /trunk/src/com/google/caja/plugin/domado.js
 /trunk/src/com/google/caja/ses/repairES5.js

=======================================
--- /trunk/src/com/google/caja/plugin/domado.js Wed Jun 12 15:14:07 2013
+++ /trunk/src/com/google/caja/plugin/domado.js Thu Jun 13 12:07:58 2013
@@ -396,39 +396,15 @@
   }

   /**
-   * Identical to Object.defineProperty except that if used to define a
- * non-writable data property, it converts it to an accessor as cajaVM.def
-   * does.
+   * Alias for a common pattern: non-enumerable toString method.
    */
-  function definePropertyAllowOverride(obj, prop, desc) {
-    var existing = Object.getOwnPropertyDescriptor(obj, prop);
- // .configurable test is necessary for e.g. <function>.prototype which is
-    // always own and non-configurable data.
-    if (!existing || existing.configurable) {
-      desc = allowNonWritableOverride(obj, prop, desc);
-    }
-    return Object.defineProperty(obj, prop, desc);
+  function setToString(obj, fn) {
+    Object.defineProperty(obj, 'toString',
+        allowNonWritableOverride(obj, 'toString', {value: fn}));
   }

   /**
- * Like object[propName] = value, but DWIMs enumerability and allows override.
-   *
- * The property's enumerability is inherited from the ancestor's property's
-   * descriptor. The property is not writable or configurable.
-   */
-  function setOwn(object, prop, value) {
-    prop += '';
-    // IE<=8, DOM objects are missing 'valueOf' property'
-    var desc = domitaModules.getPropertyDescriptor(object, prop);
-    definePropertyAllowOverride(object, prop, {
-      enumerable: desc ? desc.enumerable : false,
-      value: value
-    });
-  }
-
-  /**
-   * Shortcut for an unmodifiable property. Currently used only where
- * overriding is not of interest, so doesn't do definePropertyAllowOverride. + * Shortcut for a single unmodifiable property. No provision for override.
    */
   function setFinal(object, prop, value) {
     Object.defineProperty(object, prop, {
@@ -456,10 +432,12 @@
     //});
     // Workaround:
     if (opt_writableProto) {
-      // TODO(kpreid): Wrongly enumerable, see above.
       subCtor.prototype = inheritingProto;
     } else {
-      setOwn(subCtor, 'prototype', inheritingProto);
+      Object.defineProperty(subCtor, 'prototype', {
+        enumerable: false,
+        value: inheritingProto
+      });
     }

     Object.defineProperty(subCtor.prototype, 'constructor', {
@@ -569,9 +547,9 @@
             throw 'can\'t happen';
           }
         }
-        setOwn(amplifierMethod, 'toString', cajaVM.constFunc(function() {
+        amplifierMethod.toString = innocuous(function() {
           return '[' + typename + ']' + method.toString();
-        }));
+        });
         return cajaVM.constFunc(amplifierMethod);
       };

@@ -597,9 +575,9 @@

       this.typename = typename;
     }
-    setOwn(Confidence.prototype, "toString", Object.freeze(function () {
+    Confidence.prototype.toString = cajaVM.constFunc(function() {
       return this.typename + 'Confidence';
-    }));
+    });

     return cajaVM.def(Confidence);
   })();
@@ -1257,7 +1235,9 @@
       });
       inert.prototype = tamingCtor.prototype;
Object.freeze(inert); // not def, because inert.prototype must remain
-      setOwn(tamingCtor.prototype, "constructor", inert);
+      Object.defineProperty(tamingCtor.prototype, 'constructor', {
+        value: inert
+      });

       if (opt_name !== undefined) {
         setFinal(tamingCtors, opt_name, tamingCtor);
@@ -1796,7 +1776,7 @@
     //function TracedNodePolicy(policy, note, source) {
     //  var wrap = Object.create(policy);
     //  wrap.trace = [note, source].concat(source && source.trace || []);
-    //  setOwn(wrap, 'toString', function() {
+    //  wrap.toString = function() {
     //    return policy.toString() + "<<" + wrap.trace + ">>";
     //  });
     //  return wrap;
@@ -2692,21 +2672,24 @@
var NP_noArgEditMethodReturningNode = NP_NoArgEditMethod(defaultTameNode);

       var nodeClassNoImplWarnings = {};
-
+      var elementTamerCache = {};
       function makeTameNodeByType(node) {
         switch (node.nodeType) {
           case 1:  // Element
-            var tagName = node.tagName.toLowerCase();
+            var rawTagName = node.tagName;
+            if (Object.prototype.hasOwnProperty.call(
+                elementTamerCache, rawTagName)) {
+              return new elementTamerCache[rawTagName](node);
+            }
+            var tamer;
+            var tagName = rawTagName.toLowerCase();
             var schemaElem = htmlSchema.element(tagName);
             if (schemaElem.allowed || tagName === 'script') {
// <script> is specifically allowed because we make provisions
               // for controlling its content and src.
               var domInterfaceName = schemaElem.domInterface;
-              var specificTamer =
-                  tamingClassTable.getTamingCtor(domInterfaceName);
-              if (specificTamer) {
-                return new specificTamer(node);
-              } else {
+              tamer = tamingClassTable.getTamingCtor(domInterfaceName);
+              if (!tamer) {
                 if (!nodeClassNoImplWarnings[domInterfaceName]) {
                   nodeClassNoImplWarnings[domInterfaceName] = true;
                   if (typeof console !== 'undefined') {
@@ -2716,15 +2699,17 @@
htmlSchema.realToVirtualElementName(tagName) + ">.");
                   }
                 }
-                return new TameElement(node);
+                tamer = TameElement;
               }
             } else {
               // If an unrecognized or unsafe node, return a
               // placeholder that doesn't prevent tree navigation,
               // but that doesn't allow mutation or leak attribute
               // information.
-              return new TameOpaqueNode(node);
+              tamer = TameOpaqueNode;
             }
+            elementTamerCache[rawTagName] = tamer;
+            return new tamer(node);
           case 2:  // Attr
             // Cannot generically wrap since we must have access to the
             // owner element
@@ -3045,7 +3030,10 @@
           arrayLikeCtorUpdaters.forEach(function(f) { f(ArrayLike); });
         }
         var instance = ArrayLike(ctor.prototype, getItem, getLength);
-        setOwn(instance, 'item', cajaVM.constFunc(getItem));
+        Object.defineProperty(instance, 'item', {
+          enumerable: true, // checked against browser
+          value: cajaVM.constFunc(getItem)
+        });
         return instance;
       }

@@ -3064,7 +3052,7 @@
         return result;
       }
       registerArrayLikeClass(TameNodeList);
-      setOwn(TameNodeList.prototype, 'toString', cajaVM.def(function() {
+      setToString(TameNodeList.prototype, innocuous(function() {
         return '[domado object NodeList]';
       }));
       finishArrayLikeClass(TameNodeList);
@@ -3103,7 +3091,7 @@
         };
         // TODO(kpreid): Reorder code so exporting the name works
         inertCtor(TameNamedNodeMap, Object /*, 'NamedNodeMap' */);
- setOwn(TameNamedNodeMap.prototype, 'toString', cajaVM.def(function() {
+        setToString(TameNamedNodeMap.prototype, innocuous(function() {
           return '[domado object NamedNodeMap]';
         }));
         cajaVM.def(TameNamedNodeMap);
@@ -3167,7 +3155,7 @@
           return self;
         };
         registerArrayLikeClass(TameNamedNodeMap, TameNodeList);
- setOwn(TameNamedNodeMap.prototype, 'toString', cajaVM.def(function() {
+        setToString(TameNamedNodeMap.prototype, innocuous(function() {
           return '[domado object NamedNodeMap]';
         }));
         finishArrayLikeClass(TameNamedNodeMap);
@@ -3188,7 +3176,7 @@
         return result;
       }
       registerArrayLikeClass(TameOptionsList);
-      setOwn(TameOptionsList.prototype, 'toString', cajaVM.def(function() {
+      setToString(TameOptionsList.prototype, innocuous(function() {
         return '[domado object HTMLOptionsCollection]';
       }));
       finishArrayLikeClass(TameOptionsList);
@@ -4383,6 +4371,9 @@
        *     should have (in the format accepted by Props.define).
        * @param {function} record.construct Code to invoke at the end of
        *     construction; takes and returns self.
+ * @param {?boolean} record.virtualized Whether it should be expected that
+       *     elements tamed with this class should have virtualized names.
+       *     If null, no restriction.
* @param {boolean} record.forceChildrenNotEditable Whether to force the
        *     child node list and child nodes to not be mutable.
        * @return {function} The constructor.
@@ -4396,12 +4387,14 @@
         var opt_policy = record.forceChildrenNotEditable
             ? nodePolicyReadOnlyChildren : null;
         function TameSpecificElement(node) {
- var isVirtualized = htmlSchema.isVirtualizedElementName(node.tagName);
-          if (shouldBeVirtualized !== null &&
-              !isVirtualized !== !shouldBeVirtualized) {
- throw new Error("Domado internal inconsistency: " + node.tagName +
-                " has inconsistent virtualization state with class " +
-                record.domClass);
+          if (shouldBeVirtualized !== null) {
+            var isVirtualized =
+                htmlSchema.isVirtualizedElementName(node.tagName);
+            if (!isVirtualized !== !shouldBeVirtualized) {
+ throw new Error('Domado internal inconsistency: ' + node.tagName +
+                  ' has inconsistent virtualization state with class ' +
+                  record.domClass);
+            }
           }
           superclass.call(this, node, opt_policy, proxyType);
           construct.call(this);
@@ -6366,10 +6359,10 @@
               cssPropertyName);
           return privates.readByCanonicalName(canonName);
         });
-        setOwn(TameStyle.prototype, 'toString', innocuous(function() {
-          return '[domado object Style]';
-        }));
         Props.define(TameStyle.prototype, TameStyleConf, {
+          toString: Props.overridable(false, innocuous(function() {
+            return '[domado object Style]';
+          })),
           cssText: {
             enumerable: true,
             set: TameStyleConf.amplifying(function(privates, value) {
@@ -6631,8 +6624,9 @@
         defineLocationField('port', '80');
         defineLocationField('protocol', 'http:');
         defineLocationField('search', '');
-        setOwn(tameLocation, 'toString',
-          cajaVM.constFunc(function() { return tameLocation.href; }));
+        setToString(tameLocation, cajaVM.constFunc(function() {
+          return tameLocation.href;
+        }));
       }
       inertCtor(TameLocation, Object, 'Location');
       var tameLocation = new TameLocation();
=======================================
--- /trunk/src/com/google/caja/ses/repairES5.js Fri Jun  7 14:10:14 2013
+++ /trunk/src/com/google/caja/ses/repairES5.js Thu Jun 13 12:07:58 2013
@@ -2303,16 +2303,15 @@
    */
   function repair_DEFINE_PROPERTY() {
     function repairedDefineProperty(base, name, desc) {
-      if (typeof base === 'function' &&
-          name === 'prototype' &&
+      if (name === 'prototype' &&
+          typeof base === 'function' &&
           'value' in desc) {
         try {
           base.prototype = desc.value;
         } catch (err) {
           logger.warn('prototype fixup failed', err);
         }
-      }
-      if (!isExtensible(base) && name === '__proto__') {
+      } else if (name === '__proto__' && !isExtensible(base)) {
throw TypeError('Cannot redefine __proto__ on a non-extensible object');
       }
       return unsafeDefProp(base, name, desc);

--

--- You received this message because you are subscribed to the Google Groups "Google Caja Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to