Reviewers: metaweta,

Description:
the chrome/webkit debugger issues are pretty annoying, so I've
added a checkbox to the playground that will make Caja totally
unsafe, but allow the js debugger to work, for both es53 and ses.

Please review this at http://codereview.appspot.com/6488081/

Affected files:
  M     src/com/google/caja/demos/playground/client/ui/PlaygroundUI.java
  M     src/com/google/caja/demos/playground/client/ui/PlaygroundUI.ui.xml
  M     src/com/google/caja/demos/playground/client/ui/PlaygroundView.java
  M     src/com/google/caja/es53.js
  M     src/com/google/caja/plugin/caja.js
  M     src/com/google/caja/plugin/es53-frame-group.js
  M     src/com/google/caja/plugin/ses-frame-group.js
  M     src/com/google/caja/ses/atLeastFreeVarNames.js
  M     src/com/google/caja/ses/startSES.js


Index: src/com/google/caja/demos/playground/client/ui/PlaygroundUI.java
===================================================================
--- src/com/google/caja/demos/playground/client/ui/PlaygroundUI.java (revision 5032) +++ src/com/google/caja/demos/playground/client/ui/PlaygroundUI.java (working copy)
@@ -8,6 +8,7 @@
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.AbsolutePanel;
 import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -47,6 +48,7 @@
   @UiField protected Tree exampleTree;
   @UiField protected HorizontalPanel feedbackPanel;
   @UiField protected ListBox mode;
+  @UiField protected CheckBox unsafe;

   @UiField(provided=true)
   protected SuggestBox addressField;
Index: src/com/google/caja/demos/playground/client/ui/PlaygroundUI.ui.xml
===================================================================
--- src/com/google/caja/demos/playground/client/ui/PlaygroundUI.ui.xml (revision 5032) +++ src/com/google/caja/demos/playground/client/ui/PlaygroundUI.ui.xml (working copy)
@@ -28,17 +28,21 @@
           <g:VerticalPanel styleName="{style.pginfo}">
             <g:Label>Caja Playground</g:Label>
             <g:Label ui:field="version">Unknown</g:Label>
-            <g:ListBox ui:field="mode" visibleItemCount="1">
-              <g:item value='0'>
-                Autodetect Mode
-              </g:item>
-              <g:item value='1'>
-                ES5 Mode (Experimental)
-              </g:item>
-              <g:item value='0'>
-                ES5/3 Mode
-              </g:item>
-            </g:ListBox>
+            <g:HorizontalPanel width="100%">
+              <g:ListBox ui:field="mode" visibleItemCount="1">
+                <g:item value='0'>
+                  Autodetect Mode
+                </g:item>
+                <g:item value='1'>
+                  ES5 Mode (Experimental)
+                </g:item>
+                <g:item value='0'>
+                  ES5/3 Mode
+                </g:item>
+              </g:ListBox>
+              <g:CheckBox
+                 ui:field="unsafe" text="Disable security"/>
+            </g:HorizontalPanel>
           </g:VerticalPanel>
           <g:HorizontalPanel ui:field="loadingLabel"
             styleName="{style.loadingLabel}" >
Index: src/com/google/caja/demos/playground/client/ui/PlaygroundView.java
===================================================================
--- src/com/google/caja/demos/playground/client/ui/PlaygroundView.java (revision 5032) +++ src/com/google/caja/demos/playground/client/ui/PlaygroundView.java (working copy)
@@ -29,6 +29,8 @@
 import com.google.gwt.event.dom.client.FocusHandler;
 import com.google.gwt.event.logical.shared.SelectionEvent;
 import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.HTML;
@@ -298,6 +300,15 @@
     });
   }

+  private void initUnsafe() {
+    playgroundUI.unsafe.addValueChangeHandler(new ValueChangeHandler() {
+      @Override
+      public void onValueChange(ValueChangeEvent event) {
+        setUnsafe(playgroundUI.unsafe.getValue());
+      }
+    });
+  }
+
   private static TreeItem addExampleItem(Map<Example.Type, TreeItem> menu,
       Example eg) {
     if (!menu.containsKey(eg.type)) {
@@ -351,6 +362,10 @@
     });
   }-*/;

+  private native void setUnsafe(boolean unsafe) /*-{
+    $wnd.caja.disableSecurityForDebugger(unsafe);
+  }-*/;
+
   public PlaygroundView(Playground controller, Boolean mode) {
     this.controller = controller;
     this.sourceExamples = new MultiWordSuggestOracle();
@@ -369,6 +384,7 @@
     initCaja(true, (mode == null) ? -1 : (mode ? 1 : 0));
     initPlusOne();
     initMode();
+    initUnsafe();
   }

   public void setOriginalSource(String result) {
Index: src/com/google/caja/es53.js
===================================================================
--- src/com/google/caja/es53.js (revision 5032)
+++ src/com/google/caja/es53.js (working copy)
@@ -2566,7 +2566,12 @@
       if (value === Function.prototype) {
         throw new Error('Cannot invoke Function.prototype.');
       }
-      throw new Error('Internal: toxic function encountered!\n' + value);
+      // webkit js debuggers rely on ambient Function.bind
+      // http://code.google.com/p/chromium/issues/detail?id=145871
+      if (!___.DISABLE_SECURITY_FOR_DEBUGGER) {
+        throw new Error('Internal: toxic function encountered!\n' + value);
+      }
+      log('Internal: toxic function encountered!\n' + value);
     }
     return value;
   }
@@ -5543,7 +5548,8 @@
       getter: getter,
       setter: setter,
       directConstructor: directConstructor,
-      BASE_OBJECT_CONSTRUCTOR: BASE_OBJECT_CONSTRUCTOR
+      BASE_OBJECT_CONSTRUCTOR: BASE_OBJECT_CONSTRUCTOR,
+      DISABLE_SECURITY_FOR_DEBUGGER: false
     };
   var cajaVMKeys = ownEnumKeys(cajaVM);
   for (var i = 0; i < cajaVMKeys.length; ++i) {
Index: src/com/google/caja/plugin/caja.js
===================================================================
--- src/com/google/caja/plugin/caja.js  (revision 5032)
+++ src/com/google/caja/plugin/caja.js  (working copy)
@@ -36,6 +36,7 @@
   var GUESS = 'GUESS';

   var ajaxCounter = 1;
+  var unsafe = false;

   var loaderDocument;
   function proxyFetchMaker(proxyServer) {
@@ -141,6 +142,7 @@
     'initFeralFrame': initFeralFrame,
     'makeFrameGroup': makeFrameGroup,
     'configure': makeFrameGroup,
+    'disableSecurityForDebugger': disableSecurityForDebugger,

     // unused, removed by Closure
     closureCanary: 1
@@ -163,6 +165,13 @@
     throw new Error('Calling taming function before Caja is ready');
   }

+  function disableSecurityForDebugger(value) {
+    unsafe = !!value;
+    if (defaultFrameGroup) {
+      defaultFrameGroup['disableSecurityForDebugger'](value);
+    }
+  }
+
   /**
    * Returns a URI policy that allows one URI and denies the rest.
    */
@@ -194,6 +203,7 @@
           caja[i] = frameGroup[i];
         }
       }
+      frameGroup['disableSecurityForDebugger'](unsafe);
       state = READY;
       whenReady(null);
     });
Index: src/com/google/caja/plugin/es53-frame-group.js
===================================================================
--- src/com/google/caja/plugin/es53-frame-group.js      (revision 5032)
+++ src/com/google/caja/plugin/es53-frame-group.js      (working copy)
@@ -70,6 +70,8 @@

   var domado = Domado(makeDomadoRuleBreaker());

+  var unsafe = false;
+
   var frameGroup = {

     makeDefensibleObject___: ___.makeDefensibleObject,
@@ -95,13 +97,21 @@
     USELESS: tamingWin.___.USELESS,
     iframe: window.frameElement,

-    makeES5Frame: makeES5Frame
+    makeES5Frame: makeES5Frame,
+    disableSecurityForDebugger: disableSecurityForDebugger
   };

   return frameGroup;

   //----------------

+  function disableSecurityForDebugger(value) {
+    unsafe = !!value;
+    if (tamingWin) {
+      tamingWin.___.DISABLE_SECURITY_FOR_DEBUGGER = unsafe;
+    }
+  }
+
   function applyFunction(f, dis, args) {
     return f.apply(dis, args);
   }
@@ -197,6 +207,7 @@
           es53run);
       gman._loader = guestWin.loadModuleMaker(
         cajaInt.documentBaseUrl(), cajoler, URI.utils);
+      guestWin.___.DISABLE_SECURITY_FOR_DEBUGGER = unsafe;
       es5ready(gman);
     });
   }
Index: src/com/google/caja/plugin/ses-frame-group.js
===================================================================
--- src/com/google/caja/plugin/ses-frame-group.js       (revision 5032)
+++ src/com/google/caja/plugin/ses-frame-group.js       (working copy)
@@ -60,6 +60,8 @@
   tamingWin.___.plugin_dispatchToHandler___ =
       domado.plugin_dispatchToHandler;

+  var unsafe = false;
+
   var frameGroup = {

     makeDefensibleObject___: makeDefensibleObject,
@@ -85,13 +87,21 @@
     USELESS: USELESS,
     iframe: window.frameElement,

-    makeES5Frame: makeES5Frame
+    makeES5Frame: makeES5Frame,
+    disableSecurityForDebugger: disableSecurityForDebugger
   };

   return frameGroup;

   //----------------

+  function disableSecurityForDebugger(value) {
+    unsafe = !!value;
+    if (tamingWin) {
+      tamingWin.ses.DISABLE_SECURITY_FOR_DEBUGGER = unsafe;
+    }
+  }
+
   function makeDefensibleObject(descriptors) {
     return Object.seal(Object.create(Object.prototype, descriptors));
   }
@@ -201,6 +211,7 @@
           frameTamingMembrane, divs, uriPolicy, guestWin);
       var gman = GuestManager(frameTamingSchema, frameTamingMembrane, divs,
           cajaInt.documentBaseUrl(), domicile, guestWin, USELESS, sesRun);
+      guestWin.ses.DISABLE_SECURITY_FOR_DEBUGGER = unsafe;
       es5ready(gman);
     });
   }
Index: src/com/google/caja/ses/atLeastFreeVarNames.js
===================================================================
--- src/com/google/caja/ses/atLeastFreeVarNames.js      (revision 5032)
+++ src/com/google/caja/ses/atLeastFreeVarNames.js      (working copy)
@@ -121,6 +121,8 @@

   //////////////// END KLUDGE SWITCHES ///////////

+  ses.DISABLE_SECURITY_FOR_DEBUGGER = false;
+
   ses.atLeastFreeVarNames = function atLeastFreeVarNames(programSrc) {
     programSrc = String(programSrc);
     programSrc = LIMIT_SRC(programSrc);
@@ -130,6 +132,11 @@
     // should say "... = Object.create(null);" rather than "... = {};"
     var result = [];
     var found = StringMap();
+    // webkit js debuggers rely on ambient global eval
+    // http://code.google.com/p/chromium/issues/detail?id=145871
+    if (ses.DISABLE_SECURITY_FOR_DEBUGGER) {
+      found.set('eval', true);
+    }
     var a;
     while ((a = regexp.exec(programSrc))) {
       // Note that we could have avoided the while loop by doing
Index: src/com/google/caja/ses/startSES.js
===================================================================
--- src/com/google/caja/ses/startSES.js (revision 5032)
+++ src/com/google/caja/ses/startSES.js (working copy)
@@ -527,7 +527,7 @@
               // behalf of a typeof expression, we'd return the string
               // "undefined" here instead. Unfortunately, without
               // parsing or proxies, that isn't possible.
-              throw new ReferenceError('"' + name + '" not in scope');
+              throw new ReferenceError('"' + name + '" blocked by Caja');
             },
             set: function scopedSet(newValue) {
               if (name in imports) {


Reply via email to