Package com.google.caja.demos.playground.client.ui

Source Code of com.google.caja.demos.playground.client.ui.PlaygroundView

// Copyright (C) 2009 Google Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.demos.playground.client.ui;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

import com.google.caja.demos.playground.client.Playground;
import com.google.caja.demos.playground.client.PlaygroundResource;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
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.ui.Anchor;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TreeItem;

/**
* GUI elements of the playground client
*
* @author Jasvir Nagra (jasvir@gmail.com)
*/
public class PlaygroundView {
  private final Playground controller;
  private final MultiWordSuggestOracle sourceExamples;
  private MultiWordSuggestOracle policyExamples;

  private final PlaygroundUI playgroundUI;

  private int idSeq = 0;

  private String genId() {
    return "CajaGadget" + (idSeq++) + "___";
  }

  public void setVersion(String v) {
    playgroundUI.version.setText(v);
  }

  public void setPolicyUrl(String url) {
    playgroundUI.policyAddressField.setText(url);
    policyExamples.add(url);
  }

  public void setUrl(String url) {
    playgroundUI.addressField.setText(url);
    sourceExamples.add(url);
  }

  public void selectTab(Tabs tab) {
    playgroundUI.editorPanel.selectTab(tab.ordinal());
  }

  private void initSourcePanel() {
    for (Example eg : Example.values()) {
      sourceExamples.add(eg.url);
    }
    playgroundUI.addressField.getTextBox().addFocusHandler(new FocusHandler() {
      public void onFocus(FocusEvent event) {
        playgroundUI.addressField.showSuggestionList();
      }
    });
    playgroundUI.addressField.setText("https://");

    playgroundUI.goButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        controller.loadSource(playgroundUI.addressField.getText());
      }
    });
    playgroundUI.cajoleButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        playgroundUI.runtimeMessages.clear();
        playgroundUI.renderPanel.setText("");
        playgroundUI.renderTime.setText("Unknown");
        controller.cajole(
            playgroundUI.addressField.getText(),
            playgroundUI.sourceText.getText(),
            playgroundUI.policyText.getText(),
            true /* debug */,
            genId()
        );
      }
    });
  }

  private void initFeedbackPanel() {
    playgroundUI.feedbackPanel.setHorizontalAlignment(
        HasHorizontalAlignment.ALIGN_RIGHT);
    for (Menu menu : Menu.values()) {
      Anchor menuItem = new Anchor();
      menuItem.setHTML(menu.description);
      menuItem.setHref(menu.url);
      menuItem.setWordWrap(false);
      menuItem.addStyleName("menuItems");
      playgroundUI.feedbackPanel.add(menuItem);
      playgroundUI.feedbackPanel.setCellWidth(menuItem, "100%");
    }
  }

  private void initPolicyPanel() {
    policyExamples = new MultiWordSuggestOracle();
    playgroundUI.policyAddressField = new SuggestBox(policyExamples);
    playgroundUI.policyAddressField.getTextBox().addFocusHandler(new FocusHandler() {
      public void onFocus(FocusEvent event) {
        playgroundUI.policyAddressField.showSuggestionList();
      }
    });
    playgroundUI.policyAddressField.setText("http://");

    playgroundUI.clearButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        controller.clearPolicy();
      }
    });

    playgroundUI.loadButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        controller.loadPolicy(playgroundUI.policyAddressField.getText());
      }
    });

    playgroundUI.defaultButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        setPolicySource(defaultPolicy());
      }
    });

    setPolicySource(defaultPolicy());
  }

  private static String defaultPolicy() {
    return PlaygroundResource.INSTANCE.defaultPolicy().getText();
  }

  native static String encodeURIComponent(String uri) /*-{
    return $wnd.encodeURIComponent(uri);
  }-*/;

  /**
   * Extracts the location map and original source from content cajoled in
   * debug mode.
   * The format is described at <a href=
   * "http://google-caja.googlecode.com/svn/trunk/doc/html/compiledModuleFormat/index.html"
   * ><tt>doc/html/compiledModuleFormat/index.html</tt></a>.
   */
  private static native boolean srcLocMapAndOriginalSrc(
      String source, String[] out) /*-{
    var str = "'(?:[^'\\\\]|\\\\.)*'";
    var colon = "\\s*:\\s*";
    var comma = "\\s*,\\s*";
    var block = str + colon + "\\{(?:\\s*" + str + colon + str + comma + ")*?"
        + "\\s*'content'" + colon + "\\[\\s*"
        + str + "(?:" + comma + str + ")*\\s*\\]\\s*\\}";
    // TODO(mikesamuel): extract this a better way once we're providing module
    // output in an easy to consume JSON format.
    var re = new RegExp(
        // sourceLocationMap in group 1
        "'sourceLocationMap'" + colon + "\\{"
        + "(?:\\s*" + str + colon + str + comma + ")*?"  // any number of pairs
        + "\\s*'content'" + colon + "\\[\\s*(" + str + "(?:" + comma + str
        + ")*)\\s*\\]\\s*\\}" + comma
        // originalSource in group 2
        + "'originalSource'" + colon + "\\{\\s*("
        + block + "(?:" + comma + block
        + ")*)\\s*\\}\\s*\\}\\s*\\)\\s*;?\\s*\\}\\s*<\\/script>\s*$");
    var match = source.match(re);
    if (match) {
      out[0] = match[0];
      out[1] = match[1];
      return true;
    } else {
      return false;
    }
  }-*/;

  /*
  private Widget createSpeedtracerPanel() {
    FlowPanel hp = new FlowPanel();
    hp.setSize("100%", "100%");
    speedtracerManifestButton = new Button("Manifest URI", new ClickHandler() {
      PopupPanel panel;
      Label uriLbl;

      private String getManifestUri() {
        String[] locMapAndSrc = new String[2];
        if (srcLocMapAndOriginalSrc(cajoledSource.getText(), locMapAndSrc)) {
          String json = "[[" + locMapAndSrc[0] + "],[" + locMapAndSrc[1] + "]]";
          return "data:text/plain," + encodeURIComponent(json);
        } else {
          return null;
        }
      }

      public void onClick(ClickEvent event) {
        String dataUri = getManifestUri();
        if (panel == null) {
          HorizontalPanel body = new HorizontalPanel();
          body.add(uriLbl = new Label());
          body.add(new Button("\u00d7", new ClickHandler() {
            public void onClick(ClickEvent ev) { panel.hide(); }
          }));
          panel = new PopupPanel();
          panel.setWidget(body);
          panel.setTitle("Manifest URI");
        }
        uriLbl.setText(dataUri);
        if (panel.isShowing()) {
          panel.hide();
        } else {
          panel.show();
        }
      }
    });
    hp.add(speedtracerManifestButton);
    return hp;
  }
  */

  private native void setupNativeSelectLineBridge() /*-{
    var that = this;
    $wnd.selectLine = function (uri, start, sOffset, end, eOffset) {
      that.@com.google.caja.demos.playground.client.ui.PlaygroundView::selectTab(Lcom/google/caja/demos/playground/client/ui/PlaygroundView$Tabs;)(
          @com.google.caja.demos.playground.client.ui.PlaygroundView.Tabs::SOURCE);
      that.@com.google.caja.demos.playground.client.ui.PlaygroundView::highlightSource(Ljava/lang/String;IIII)(uri, start, sOffset, end, eOffset);
    }
  }-*/;

  private void initEditor() {
    setupNativeSelectLineBridge();
    selectTab(Tabs.SOURCE);
  }

  private native void initPlusOne() /*-{
    try {
      $wnd.gapi.plusone.render("plusone",{size: "medium"});
    } catch (e) {
      // failure to initialize +1 button should not prevent load of page
    }
  }-*/;

  private void initUnsafe() {
    playgroundUI.unsafe.addValueChangeHandler(
        new ValueChangeHandler<Boolean>() {
      @Override
      public void onValueChange(ValueChangeEvent<Boolean> event) {
        setUnsafe(playgroundUI.unsafe.getValue());
      }
    });
  }

  private static TreeItem addExampleItem(Map<Example.Type, TreeItem> menu,
      Example eg) {
    if (!menu.containsKey(eg.type)) {
      TreeItem menuItem = new TreeItem(eg.type.description);
      menu.put(eg.type, menuItem);
    }
    TreeItem egItem = new TreeItem(eg.description);
    menu.get(eg.type).addItem(egItem);
    return egItem;
  }

  private void initExamples() {
    Map<Example.Type, TreeItem> menuMap = new EnumMap<Example.Type, TreeItem>(
        Example.Type.class);
    final Map<TreeItem, Example> entryMap = new HashMap<TreeItem, Example>();

    playgroundUI.exampleTree.setTitle("Select an example");
    for (Example eg : Example.values()) {
      TreeItem it = addExampleItem(menuMap, eg);
      entryMap.put(it, eg);
    }

    boolean first = true;
    for (TreeItem menuItem : menuMap.values()) {
      if (first) {
        first = false;
        menuItem.setState(true);
      }
      playgroundUI.exampleTree.addItem(menuItem);
    }

    playgroundUI.exampleTree.addSelectionHandler(new SelectionHandler<TreeItem>() {
      public void onSelection(SelectionEvent<TreeItem> event) {
        Example eg = entryMap.get(event.getSelectedItem());
        // No associated example - e.g. when opening a subtree menu
        if (null == eg) {
          return;
        }
        controller.loadSource(eg.url);
      }
    });
  }

  private native void initCaja(boolean debug) /*-{
    var that = this;
    function success(detail) {
    }
    function failed(e) {
        that.@com.google.caja.demos.playground.client.ui.PlaygroundView::addRuntimeMessage(Ljava/lang/String;)
          (e);
    }
    $wnd.caja.initialize({
      server: '.',
      debug: debug,
      // TODO(kpreid): Make sure we warn the user if the actual severity is
      // UNSAFE_SPEC_VIOLATION or worse, so that we don't silently appear to
      // have unknown security bugs.
      maxAcceptableSeverity: 'NEW_SYMPTOM'
    }, success, failed);
  }-*/;

  private native void setUnsafe(boolean unsafe) /*-{
    $wnd.caja.disableSecurityForDebugger(unsafe);
  }-*/;

  public PlaygroundView(Playground controller) {
    this.controller = controller;
    this.sourceExamples = new MultiWordSuggestOracle();
    this.policyExamples = new MultiWordSuggestOracle();

    this.playgroundUI =
      new com.google.caja.demos.playground.client.ui.PlaygroundUI(
          sourceExamples, policyExamples);
    RootLayoutPanel.get().add(playgroundUI);
    initSourcePanel();
    initPolicyPanel();
    initFeedbackPanel();
    initExamples();
    initEditor();
    initCaja(true);
    initPlusOne();
    initUnsafe();
  }

  public void setOriginalSource(String result) {
    if (result == null) {
      playgroundUI.sourceText.setText("");
    } else {
      playgroundUI.sourceText.setText(result);
    }
  }

  public void setPolicySource(String result) {
    if (result == null) {
      playgroundUI.policyText.setText("");
    } else {
      playgroundUI.policyText.setText(result);
    }
  }

  public void setLoading(boolean isLoading) {
    playgroundUI.loadingLabel.setVisible(isLoading);
  }

  private native String prettyPrint(String result, String lang) /*-{
    return $wnd.prettyPrintOne($wnd.indentAndWrapCode(result), lang);
  }-*/;

  public void setRenderedResult(String baseUrl,
      final String policy, final String html, final String js,
      final String idClass)
  {
    if (html == null && js == null) {
      playgroundUI.renderResult.setText("There were cajoling errors");
      return;
    }

    // Make the cajoled content visible so that the DOM will be laid out before
    // the script checks DOM geometry.
    selectTab(Tabs.RENDER);

    setRenderedResultNative(
        playgroundUI.renderPanel.getElement(),
        baseUrl,
        makeUriPolicy(),
        idClass,
        policy,
        html,
        js);
  }

  private native void setRenderedResultNative(
      Element element,
      String baseUrl,
      JavaScriptObject uriPolicy,
      String idClass,
      String policy,
      String html,
      String js) /*-{
    var startTime = Date.now();
    var that = this;
    $wnd.caja.load(
        element,
        uriPolicy,
        function(frame) {
          var api = that.@com.google.caja.demos.playground.client.ui.PlaygroundView::makeExtraImports(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;)($wnd.caja, frame, policy);
          frame = frame.api(api);
          frame = frame.code(baseUrl, "text/html", html);
          frame.run(function(r) {
            that.@com.google.caja.demos.playground.client.ui.PlaygroundView::setRenderedResult(Ljava/lang/String;)(r + '');
            that.@com.google.caja.demos.playground.client.ui.PlaygroundView::setRenderTime(I)(Date.now() - startTime);
          });
        },
        {
          idClass: idClass,
          title: 'Playground Untrusted Content'
        });
  }-*/;

  private void setRenderedResult(String result) {
    playgroundUI.renderResult.setText(result);
  }

  private void setRenderTime(int time) {
    playgroundUI.renderTime.setText(time + "ms");
  }

  private native JavaScriptObject makeExtraImports(
      JavaScriptObject caja,
      JavaScriptObject guestFrame,
      String policy) /*-{
    var that = this;
    var extraImports = {};
    try {
      var tamings___ = $wnd.eval(policy);
    } catch (e) {
      that.@com.google.caja.demos.playground.client.ui.PlaygroundView::addRuntimeError(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)
          (e, "evaluating policy");
    }
    for (var i=0; i < tamings___.length; i++) {
      try {
        tamings___[i].call(undefined, caja, extraImports);
      } catch (e) {
        that.@com.google.caja.demos.playground.client.ui.PlaygroundView::addRuntimeError(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)
            (e, "evaluating " + i + "th policy function");
      }
    }

    extraImports.onerror = caja.tame(caja.markFunction(
      function (message, source, lineNum) {
        that.@com.google.caja.demos.playground.client.ui.PlaygroundView::addRuntimeError(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)
            (message, source, lineNum);
      }));
    extraImports.alert = caja.tame(
      caja.markFunction(
        function(msg) { alert('Untrusted code says: ' + String(msg)); }));
    return extraImports;
  }-*/;

  private native JavaScriptObject makeUriPolicy() /*-{
        return {
          mitigate: function (uri) {
            // Skip rewriting jquery and jqueryui when loaded
            // from the google cdn
            if (uri.getDomain() === "ajax.googleapis.com" &&
                (uri.getPath().indexOf("/ajax/libs/jquery/") === 0 ||
                 uri.getPath().indexOf("/ajax/libs/jqueryui/") === 0))  {
              return uri;
            }
            return null;
          },
          fetch: $wnd.caja.policy.net.ALL.fetch,
          rewrite: function (uri, uriEffect, loaderType, hints) {
            if (uriEffect === $wnd.html4.ueffects.NEW_DOCUMENT) {
              return uri;
            }
            if (uriEffect === $wnd.html4.ueffects.SAME_DOCUMENT &&
                 (loaderType === $wnd.html4.ltypes.SANDBOXED ||
                   loaderType === $wnd.html4.ltypes.DATA)) {
              if (hints && hints.XHR) {
                return uri;
              }
              return "http://www.gmodules.com/gadgets/proxy"
                  + "?url=" + encodeURIComponent(uri.toString())
                  + "&container=caja";
            }
            return null;
          }
        };
  }-*/;

  public void addRuntimeError(String message, String source, String lineNum) {
    // Labels are texty, so no escaping needed
    Label i = new Label(
        "Uncaught script error: '" + message +
        "' in source: '" + source +
        "' at line: " + lineNum + "\n");
    playgroundUI.runtimeMessages.add(i);
  }

  public void addRuntimeMessage(String message) {
    // Labels are texty, so no escaping needed
    Label i = new Label(message);
    playgroundUI.runtimeMessages.add(i);
  }

  /** @param uri unused but provided for consistency with native GWT caller. */
  public void highlightSource(String uri,
      int start, int sOffset, int end, int eOffset) {
    playgroundUI.sourceText.setCursorPos(start);
    playgroundUI.sourceText.setSelectionRange(start, sOffset, end, eOffset);
  }

  public enum Tabs {
    SOURCE,
    POLICY,
    RENDER,
    COMPILE_WARNINGS,
    RUNTIME_WARNINGS,
    TAMING,
    MANIFEST;
  }
}
TOP

Related Classes of com.google.caja.demos.playground.client.ui.PlaygroundView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.