Revision: 5654
Author:   [email protected]
Date:     Tue Jan  7 21:20:26 2014 UTC
Log:      Backport to es53 branch: r5646, r5648, r5650
https://codereview.appspot.com/48500045

Backport features and SES compatibility:
5646 Add <canvas>.toDataURL().
5648 Adds some extra SES tests
5650 Repair and whitelisting for IE 11.

The merge command was
svn merge -c 5646,5648,5650 ^/trunk
and there was one conflict:

r5650 contained changes to the Typed Arrays taming, which is not present
on the ES5/3 branch, and those changes were omitted.

[email protected]

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

Modified:
 /branches/es53
 /branches/es53/src/com/google/caja/plugin/caja.js
 /branches/es53/src/com/google/caja/plugin/domado.js
 /branches/es53/src/com/google/caja/ses/WeakMap.js
 /branches/es53/src/com/google/caja/ses/debug.js
 /branches/es53/src/com/google/caja/ses/explicit.html
 /branches/es53/src/com/google/caja/ses/logger.js
 /branches/es53/src/com/google/caja/ses/repairES5.js
 /branches/es53/src/com/google/caja/ses/startSES.js
 /branches/es53/src/com/google/caja/ses/whitelist.js
 /branches/es53/tests/com/google/caja/plugin/test-domado-canvas-guest.html
 /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js

=======================================
--- /branches/es53/src/com/google/caja/plugin/caja.js Mon Aug 26 20:59:09 2013 UTC +++ /branches/es53/src/com/google/caja/plugin/caja.js Tue Jan 7 21:20:26 2014 UTC
@@ -455,7 +455,16 @@

           // safe given that we use exactly one SES frame
           'FREEZE_IS_FRAME_DEPENDENT': { 'permit': true },
-          'SYNTAX_ERRORS_ARENT_ALWAYS_EARLY': { 'permit': true }
+          'SYNTAX_ERRORS_ARENT_ALWAYS_EARLY': { 'permit': true },
+
+          // Only affects code with strict nested function defs, which
+          // violates the ES5.1 recommendation stated at
+ // http://wiki.ecmascript.org/doku.php?id=conventions:recommendations_for_implementors.
+          // Thus, the NESTED_STRICT_FUNCTIONS_LEAK
+          // doesn't affect SES as long as SES remains
+          // compatible with ES5 implementations that follow that
+          // recommendation.
+          'NESTED_STRICT_FUNCTIONS_LEAK': { 'permit': true }
         };
       }
       ses['mitigateSrcGotchas'] = function() {
=======================================
--- /branches/es53/src/com/google/caja/plugin/domado.js Wed Dec 11 01:15:23 2013 UTC +++ /branches/es53/src/com/google/caja/plugin/domado.js Tue Jan 7 21:20:26 2014 UTC
@@ -5427,6 +5427,25 @@
                   // user agent, return null and abort these steps."
                   return null;
               }
+            }),
+ toDataURL: Props.ampMethod(function(privates, opt_type, opt_arg) {
+              if (opt_type !== undefined) {
+                opt_type = String(opt_type).toLowerCase();
+              }
+              // Whitelist of types to be cautious, and because we need
+              // to sanitize the varargs
+              switch (opt_type) {
+                case 'image/png':
+                  return privates.feral.toDataURL('image/png');
+                case 'image/jpeg':
+                  return privates.feral.toDataURL('image/jpeg', +opt_arg);
+                default:
+ console.warn('Domado: Discarding unrecognized MIME type ' +
+                      opt_type + ' for canvas.toDataURL.');
+                  /* fall through */
+                case undefined:
+                  return privates.feral.toDataURL();
+              }
             })
           }; }
         });
=======================================
--- /branches/es53/src/com/google/caja/ses/WeakMap.js Tue Sep 10 23:47:04 2013 UTC +++ /branches/es53/src/com/google/caja/ses/WeakMap.js Tue Jan 7 21:20:26 2014 UTC
@@ -124,6 +124,10 @@
   if (typeof ses !== 'undefined') {
     ses.weakMapPermitHostObjects = weakMapPermitHostObjects;
   }
+
+  // IE 11 has no Proxy but has a broken WeakMap such that we need to patch
+  // it using DoubleWeakMap; this flag tells DoubleWeakMap so.
+  var doubleWeakMapCheckSilentFailure = false;

// Check if there is already a good-enough WeakMap implementation, and if so
   // exit without replacing it.
@@ -150,7 +154,16 @@

       // Fall through to installing our WeakMap.
     } else {
-      return;
+      // IE 11 bug: WeakMaps silently fail to store frozen objects.
+      var testMap = new HostWeakMap();
+      var testObject = Object.freeze({});
+      testMap.set(testObject, 1);
+      if (testMap.get(testObject) !== 1) {
+        doubleWeakMapCheckSilentFailure = true;
+        // Fall through to installing our WeakMap.
+      } else {
+        return;
+      }
     }
   }

@@ -516,6 +529,13 @@
// If we got here, then the platform has a WeakMap but we are concerned
       // that it may refuse to store some key types. Therefore, make a map
       // implementation which makes use of both as possible.
+
+ // In this mode we are always using double maps, so we are not proxy-safe. + // This combination does not occur in any known browser, but we had best
+      // be safe.
+ if (doubleWeakMapCheckSilentFailure && typeof Proxy !== 'undefined') {
+        Proxy = undefined;
+      }

       function DoubleWeakMap() {
if (!(this instanceof OurWeakMap)) { // approximate test for new ...()
@@ -536,6 +556,9 @@
// arbitrary WeakMaps to switch to using hidden properties, but only // those which need the ability, and unprivileged code is not allowed
         // to set the flag.
+        //
+        // (Except in doubleWeakMapCheckSilentFailure mode in which case we
+        // disable proxies.)
         var enableSwitching = false;

         function dget(key, opt_default) {
@@ -551,17 +574,28 @@
           return hmap.has(key) || (omap ? omap.has___(key) : false);
         }

-        function dset(key, value) {
-          if (enableSwitching) {
-            try {
-              hmap.set(key, value);
-            } catch (e) {
+        var dset;
+        if (doubleWeakMapCheckSilentFailure) {
+          dset = function(key, value) {
+            hmap.set(key, value);
+            if (!hmap.has(key)) {
               if (!omap) { omap = new OurWeakMap(); }
-              omap.set___(key, value);
+              omap.set(key, value);
             }
-          } else {
-            hmap.set(key, value);
-          }
+          };
+        } else {
+          dset = function(key, value) {
+            if (enableSwitching) {
+              try {
+                hmap.set(key, value);
+              } catch (e) {
+                if (!omap) { omap = new OurWeakMap(); }
+                omap.set___(key, value);
+              }
+            } else {
+              hmap.set(key, value);
+            }
+          };
         }

         function ddelete(key) {
=======================================
--- /branches/es53/src/com/google/caja/ses/debug.js Wed Aug 7 17:46:21 2013 UTC +++ /branches/es53/src/com/google/caja/ses/debug.js Tue Jan 7 21:20:26 2014 UTC
@@ -163,7 +163,7 @@
      (function() {
        var FFFramePattern = (/^([^@]*)@(.*?):?(\d*)$/);

-       // stacktracejs.org suggests that this indicates FF. Really?
+       // stacktracejs.com suggests that this indicates FF. Really?
        function getCWStack(err) {
          var stack = err.stack;
          if (!stack) { return void 0; }
=======================================
--- /branches/es53/src/com/google/caja/ses/explicit.html Fri Aug 23 20:27:28 2013 UTC +++ /branches/es53/src/com/google/caja/ses/explicit.html Tue Jan 7 21:20:26 2014 UTC
@@ -152,6 +152,31 @@
   })();
 </script>

+<script>
+  (function() {
+    "use strict";
+    if (!ses.ok()) {
+      return;
+    }
+
+    var src =
+        'function foo() { throw Error("Expand me to see stack"); }\n' +
+        'function foo2() { foo(); }\n' +
+        'function foo3() { foo2(); }\n' +
+        'foo3();\n';
+
+    try {
+      cajaVM.eval(src);
+    } catch (err) {
+      // The ses.logger installed by useHTMLLogger.js uses ses.getStack
+      // to display the stack, if any, associated with the err argument.
+      ses.logger.info('Expected error to test ses.getStack API: ', err);
+      return;
+    }
+    ses.logger.error('Missing expected error');
+  }());
+</script>
+
 <script src="makeQ.js"></script>
 <script src="makeFarResourceMaker.js"></script>
 <script src="compileExprLater.js"></script>
=======================================
--- /branches/es53/src/com/google/caja/ses/logger.js Thu Feb 14 22:31:30 2013 UTC +++ /branches/es53/src/com/google/caja/ses/logger.js Tue Jan 7 21:20:26 2014 UTC
@@ -163,8 +163,8 @@
       // We don't do "console.apply" because "console" is not a function
       // on IE 10 preview 2 and it has no apply method. But it is a
       // callable that Function.prototype.apply can successfully apply.
-      // This code most work on ES3 where there's no bind. When we
-      // decide support defensiveness in contexts (frames) with mutable
+      // This code must work on ES3 where there's no bind. When we
+      // decide to support defensiveness in realms with mutable
       // primordials, we will need to revisit the "call" below.
       apply.call(console[level], console, [''].concat(args));

=======================================
--- /branches/es53/src/com/google/caja/ses/repairES5.js Tue Sep 24 17:41:30 2013 UTC +++ /branches/es53/src/com/google/caja/ses/repairES5.js Tue Jan 7 21:20:26 2014 UTC
@@ -2748,6 +2748,41 @@
       delete ses.CANT_SAFELY_VERIFY_SYNTAX_canary;
     }
   }
+
+  /**
+   * Detects
+ * https://connect.microsoft.com/IE/feedback/details/811124/ie11-javascript-function-scoping-is-weird-with-respect-to-functions-and-try-catch
+   * in strict code.
+   *
+   * A strict nested function definition should either be a syntax
+   * error, as
+ * http://wiki.ecmascript.org/doku.php?id=conventions:recommendations_for_implementors
+   * recommends, or it should stay local to its block, as ES6
+   * specifies. Within that block, an assignment to that function's
+   * name should assign to the block-local variable defined by that
+   * function.
+   */
+  function test_NESTED_STRICT_FUNCTIONS_LEAK() {
+    try {
+      return unsafeEval(
+          '(function() {\n' +
+          '  "use strict";\n' +
+          '  var a = function good() { return false; };\n' +
+          '  try {\n' +
+          '    function a() { return true; }' +
+          '    a = function blah() {\n' +
+ ' return "Assignment skipped nested function definition";\n' +
+          '    };\n' +
+          '  } catch (x) {}\n' +
+          '  return a();\n' +
+          '})();\n');
+    } catch (err) {
+      if (err instanceof SyntaxError) {
+        return false;
+      }
+      return 'Unexpected error from strict nested function: ' + err;
+    }
+  }

   ////////////////////// Repairs /////////////////////
   //
@@ -4336,6 +4371,18 @@
       sections: ['15.3.2.1'],
       tests: []
     },
+    {
+      id: 'NESTED_STRICT_FUNCTIONS_LEAK',
+      description: 'Strict nested functions leak from block scope',
+      test: test_NESTED_STRICT_FUNCTIONS_LEAK,
+      repair: void 0,
+      preSeverity: severities.UNSAFE_SPEC_VIOLATION,
+      canRepair: false,
+ urls: ['https://connect.microsoft.com/IE/feedback/details/811124/ie11-javascript-function-scoping-is-weird-with-respect-to-functions-and-try-catch',
+             
'http://wiki.ecmascript.org/doku.php?id=conventions:recommendations_for_implementors'],
+      sections: [],
+      tests: []  // hopefully will be in ES6 tests
+    }
   ];

   // UNSHIFT_IGNORES_SEALED
=======================================
--- /branches/es53/src/com/google/caja/ses/startSES.js Wed Aug 28 17:45:51 2013 UTC +++ /branches/es53/src/com/google/caja/ses/startSES.js Tue Jan 7 21:20:26 2014 UTC
@@ -781,11 +781,11 @@
     ses.makeCompiledExpr = makeCompiledExpr;

     /**
-     * Compiles {@code src} as a strict expression into a function
+     * Compiles {@code exprSrc} as a strict expression into a function
      * of an {@code imports}, that when called evaluates {@code
      * exprSrc} in a virtual global environment whose {@code this} is
-     * bound to that {@code imports}, and whose free variables
-     * refer only to the properties of that {@code imports}.
+     * bound to that {@code imports}, and whose free variables refer
+     * only to the properties of that {@code imports}.
      *
      * <p>The optional {@code opt_mitigateOpts} can be used to control
      * which transformations are applied to src, if they are
@@ -1417,7 +1417,7 @@
    * process "*" inheritance using the whitelist, by walking actual
    * superclass chains.
    */
-  var whitelistSymbols = [true, '*', 'accessor'];
+  var whitelistSymbols = [true, '*', 'maybeAccessor'];
   var whiteTable = new WeakMap();
   function register(value, permit) {
     if (value !== Object(value)) { return; }
@@ -1434,8 +1434,8 @@
     whiteTable.set(value, permit);
     keys(permit).forEach(function(name) {
       // Use gopd to avoid invoking an accessor property.
-      // Mismatches between permit === 'accessor' and the property actually
-      // being an accessor property are caught later by clean().
+      // Accessor properties for which permit !== 'maybeAccessor'
+      // are caught later by clean().
       var desc = gopd(value, name);
       if (desc) {
         register(desc.value, permit[name]);
@@ -1618,19 +1618,12 @@
         if (hop.call(desc, 'value')) {
           // Is a data property
           var subValue = desc.value;
-          if (p === 'accessor') {
+          clean(subValue, path);
+        } else {
+          if (p !== 'maybeAccessor') {
             // We are not saying that it is safe for the prop to be
-            // unexpectedly not an accessor; rather, it will be deleted
+            // unexpectedly an accessor; rather, it will be deleted
             // and thus made safe.
-            reportProperty(ses.severities.SAFE_SPEC_VIOLATION,
-                           'Not an accessor property', path);
-            cleanProperty(value, name, path);
-          } else {
-            clean(subValue, path);
-          }
-        } else {
-          // Is an accessor property (note symmetry with above case)
-          if (p !== 'accessor') {
             reportProperty(ses.severities.SAFE_SPEC_VIOLATION,
                            'Not a data property', path);
             cleanProperty(value, name, path);
=======================================
--- /branches/es53/src/com/google/caja/ses/whitelist.js Wed Sep 4 04:54:27 2013 UTC +++ /branches/es53/src/com/google/caja/ses/whitelist.js Tue Jan 7 21:20:26 2014 UTC
@@ -51,10 +51,10 @@
  *     If the property is an accessor property, it is not
  *     whitelisted (as invoking an accessor might not be meaningful,
  *     yet the accessor might return a value needing taming).
- * <li>"accessor", in which case this accessor property is simply
+ * <li>"maybeAccessor", in which case this accessor property is simply
  *     whitelisted and its getter and/or setter are tamed according to
- *     inheritance. If the property is not an accessor property, it is
- *     not whitelisted.
+ *     inheritance. If the property is not an accessor property, its
+ *     value is tamed according to inheritance.
  * <li>"*", in which case this property on this object is whitelisted,
  *     as is this property as inherited by all objects that inherit
  *     from this object. The values associated with all such properties
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-domado-canvas-guest.html Wed Aug 14 04:56:12 2013 UTC +++ /branches/es53/tests/com/google/caja/plugin/test-domado-canvas-guest.html Tue Jan 7 21:20:26 2014 UTC
@@ -37,7 +37,17 @@
     assertEquals("set canvas width", 20, canvas.width);
     assertEquals("set canvas height", 10, canvas.height);

-    // Getting the context
+    pass('testCanvasElement');
+  });
+</script>
+
+<p id="testCanvasGetContext" class="testcontainer">
+  Canvas getContext
+  <canvas id="testCanvasGetContext-canvas">fallback</canvas>
+</p>
+<script type="text/javascript">
+  jsunitRegister('testCanvasGetContext', function testCanvasGetContext() {
+    var canvas = document.getElementById('testCanvasGetContext-canvas');
     expectFailure(
         function() {
           canvas.getContext();
@@ -53,12 +63,41 @@
     var context = canvas.getContext("2d");
     assertNotNull("context", context);
     assertNotUndefined("context", context);
-    assertEquals("context equals itself", context,
- document.getElementById('testCanvasElement-canvas').getContext("2d")); + assertEquals("context equals itself", context, canvas.getContext("2d"));
+    pass();
+  });
+</script>

-    // TODO: toDataURL
+<p id="testCanvasToDataURL" class="testcontainer">
+  Canvas toDataURL
+  <!-- Zoomed in for visual inspection -->
+  <canvas id="testCanvasToDataURL-canvas"
+          width="3" height="3"
+          style="width: 30px; height: 30px;">fallback</canvas>
+</p>
+<script type="text/javascript">
+  jsunitRegister('testCanvasToDataURL', function testCanvasToDataURL() {
+    var canvas = document.getElementById('testCanvasToDataURL-canvas');
+    var context = canvas.getContext('2d');
+    context.fillRect(1, 1, 1, 1);

-    pass('testCanvasElement');
+    var url = canvas.toDataURL();
+    assertTrue(url, /data:image\/png(?:;base64)?,/.test(url));
+
+ // TODO(kpreid): Once we support loading data: URIs, try writing the image
+    // back to a canvas. For now, displaying it as best we can:
+    document.getElementById('testCanvasToDataURL').appendChild(
+        document.createTextNode(url));
+
+    // Try optional arguments.
+    // Note: Assuming JPEG support which is not mandatory.
+    url = canvas.toDataURL('image/jpeg', 0.1);
+    assertTrue(url, /data:image\/jpeg(?:;base64)?,/.test(url));
+
+    // TODO(kpreid): Feature-test and test toDataURLHD and toBlob[HD].
+    // HD versions give full-resolution data
+
+    pass();
   });
 </script>

=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Wed Dec 11 01:15:23 2013 UTC +++ /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Tue Jan 7 21:20:26 2014 UTC
@@ -1043,6 +1043,10 @@
     argsByProp('getPropertyValue', genMethod(genCSSPropertyName));
argsByProp('getContext', genMethod(G.value(undefined, null, 'bogus', '2d',
         'webgl', 'experimental-webgl')));
+    argsByProp('toDataURL', G.any(
+        genMethod(),
+        genMethod(genMediaType),
+        genMethod(genMediaType, genSmallInteger)));
     argsByProp('querySelector', genMethod(genCSSSelector));
     argsByProp('querySelectorAll', freshResult(genMethod(genCSSSelector)));
     argsByProp('getElementById', genMethod(G.value(

--

--- 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