Package com.google.speedtracer.client.visualizations.view

Source Code of com.google.speedtracer.client.visualizations.view.EventWaterfallRowDetails$Resources

/*
* Copyright 2010 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.speedtracer.client.visualizations.view;

import com.google.gwt.coreext.client.DataBag;
import com.google.gwt.coreext.client.IterableFastStringMap;
import com.google.gwt.coreext.client.JSOArray;
import com.google.gwt.coreext.client.JsIntegerDoubleMap;
import com.google.gwt.coreext.client.DataBag.IterationCallback;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.HeadingElement;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.graphics.client.charts.ColorCodedDataList;
import com.google.gwt.graphics.client.charts.ColorCodedValue;
import com.google.gwt.graphics.client.charts.PieChart;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.topspin.ui.client.ClickEvent;
import com.google.gwt.topspin.ui.client.ClickListener;
import com.google.gwt.topspin.ui.client.Container;
import com.google.gwt.topspin.ui.client.DefaultContainerImpl;
import com.google.gwt.topspin.ui.client.Div;
import com.google.gwt.topspin.ui.client.ResizeEvent;
import com.google.gwt.topspin.ui.client.ResizeListener;
import com.google.gwt.topspin.ui.client.Table;
import com.google.gwt.topspin.ui.client.Widget;
import com.google.speedtracer.client.SourceViewer;
import com.google.speedtracer.client.SourceViewerServer;
import com.google.speedtracer.client.SymbolServerService;
import com.google.speedtracer.client.SourceViewer.SourcePresenter;
import com.google.speedtracer.client.SourceViewer.SourceViewerInitializedCallback;
import com.google.speedtracer.client.SourceViewer.SourceViewerLoadedCallback;
import com.google.speedtracer.client.SourceViewerServer.ActionCompletedCallback;
import com.google.speedtracer.client.model.CustomEvent;
import com.google.speedtracer.client.model.DataDispatcher;
import com.google.speedtracer.client.model.EvalScript;
import com.google.speedtracer.client.model.EventRecord;
import com.google.speedtracer.client.model.HintRecord;
import com.google.speedtracer.client.model.JavaScriptExecutionEvent;
import com.google.speedtracer.client.model.JavaScriptProfile;
import com.google.speedtracer.client.model.LogEvent;
import com.google.speedtracer.client.model.NetworkResource;
import com.google.speedtracer.client.model.NetworkEventDispatcher;
import com.google.speedtracer.client.model.PaintEvent;
import com.google.speedtracer.client.model.ParseHtmlEvent;
import com.google.speedtracer.client.model.ResourceDataReceivedEvent;
import com.google.speedtracer.client.model.StackFrame;
import com.google.speedtracer.client.model.TimerCleared;
import com.google.speedtracer.client.model.TimerFiredEvent;
import com.google.speedtracer.client.model.TimerInstalled;
import com.google.speedtracer.client.model.UiEvent;
import com.google.speedtracer.client.model.XhrLoadEvent;
import com.google.speedtracer.client.model.XhrReadyStateChangeEvent;
import com.google.speedtracer.client.util.Command;
import com.google.speedtracer.client.util.TimeStampFormatter;
import com.google.speedtracer.client.util.Url;
import com.google.speedtracer.client.util.dom.WindowExt;
import com.google.speedtracer.client.visualizations.view.FilteringScrollTable.RowDetails;
import com.google.speedtracer.client.visualizations.view.JavaScriptProfileRenderer.ResizeCallback;
import com.google.speedtracer.client.visualizations.view.Tree.ExpansionChangeListener;
import com.google.speedtracer.client.visualizations.view.Tree.Item;
import com.google.speedtracer.client.visualizations.view.Tree.SelectionChangeListener;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
* Details component for a UiEvent.
*
* This class contains all the widgetry for the expanded event views. It also
* exposes a method for getting an element containing aggregate statistics for
* the UiEvent.
*/
public class EventWaterfallRowDetails extends RowDetails implements
    SourcePresenter {
  /**
   * Styles.
   */
  public interface Css extends CssResource {
    String detailsKeyColumn();

    String detailsLayout();

    String detailsTable();

    String detailsTableKey();

    String eventBreakdownHeader();

    String hintletList();

    String pieChartContainer();

    int uiPadding();
  }

  /**
   * Externalized Resources.
   */
  public interface Resources extends PieChart.Resources,
      HintletRecordsTree.Resources, LazyEventTree.Resources,
      StackFrameRenderer.Resources, SourceViewer.Resources,
      JavaScriptProfileRenderer.Resources, ScopeBar.Resources,
      ColorCodedDataList.Resources, FilteringScrollTable.Resources {
    @Source("resources/EventWaterfallRowDetails.css")
    Css eventWaterfallRowDetailsCss();
  }

  /**
   * Class that handles clicks on the tab bar for the JavaScript Profiles.
   */
  private class ProfileClickListener implements ClickListener {
    private int profileType;

    private ProfileClickListener(int profileType) {
      this.profileType = profileType;
    }

    public void onClick(ClickEvent clickEvent) {
      jsProfileRenderer.show(profileType);
      Command.defer(new Command.Method() {
        public void execute() {
          fixHeightOfParentRow();
        }
      });
    }
  }

  private List<ColorCodedValue> data;

  private Table detailsTable;

  private Container detailsTableContainer;

  private TableCellElement eventTraceContainerCell;

  private final EventWaterfall eventWaterfall;

  private Command.Method heightFixer;

  private HintletRecordsTree hintletTree;

  // Profiles are processed in the background. This variable tells the click
  // handler if the profile needs to be refreshed.
  private boolean javaScriptProfileInProgress = false;

  private JavaScriptProfileRenderer jsProfileRenderer;

  private Div profileDiv;

  private final EventWaterfallRowDetails.Resources resources;

  private SourceViewer sourceViewer;

  private final SourceSymbolClickListener symbolClickListener;

  private DivElement treeDiv;

  protected EventWaterfallRowDetails(EventWaterfall eventWaterfall,
      EventWaterfallRow parent, EventWaterfallRowDetails.Resources resources) {
    eventWaterfall.super(parent);
    parent.setDetails(this);
    this.resources = resources;
    this.eventWaterfall = eventWaterfall;
    this.symbolClickListener = new SourceSymbolClickListener() {
      public void onSymbolClicked(String resourceUrl,
          SourceViewerServer sourceViewerServer, int lineNumber, int column,
          String absoluteFilePath) {
        showSource(resourceUrl, sourceViewerServer, lineNumber, column,
            absoluteFilePath);
      }
    };
  }

  @Override
  public EventWaterfallRow getParentRow() {
    return (EventWaterfallRow) super.getParentRow();
  }

  public boolean isJavaScriptProfileInProgress() {
    return this.javaScriptProfileInProgress;
  }

  /**
   * See if any of the data has changed and refresh the view.
   */
  public void refresh() {
    if (!isCreated()) {
      return;
    }
    if (hintletTree == null) {
      hintletTree = createHintletTree(treeDiv);
    } else {
      hintletTree.refresh(getParentRow().getEvent().getHintRecords());
    }
  }

  public void showSource(final String resourceUrl,
      SourceViewerServer sourceViewerServer, final int lineNumber,
      final int column, String absoluteFilePath) {
    // TODO(jaimeyap): Put up a spinner or something. It may take a while to
    // display the resource.

    // First try to jump to IDE.
    SourceViewerServer.jumpToIde(sourceViewerServer, absoluteFilePath,
        lineNumber, new ActionCompletedCallback() {

          public void onFail() {
            // Just show the source in our source viewer.
            showSourceInSourceViewer(resourceUrl,
                new SourceViewerLoadedCallback() {

                  public void onSourceFetchFail(int statusCode,
                      SourceViewer viewer) {
                    viewer.hide();
                  }

                  public void onSourceViewerLoaded(SourceViewer viewer) {
                    // The viewer should not be loaded at the URL we
                    // care about.
                    viewer.show();
                    viewer.highlightLine(lineNumber);
                    if (column > 0) {
                      viewer.markColumn(lineNumber, column);
                      viewer.scrollColumnMarkerIntoView();
                    } else {
                      viewer.scrollHighlightedLineIntoView();
                    }
                  }
                });
          }

          public void onSuccess() {
            // Do nothing. IDE should have focus.
          }

        });
  }

  @Override
  protected Element createElement() {
    Element elem = super.createElement();
    Container myContainer = new DefaultContainerImpl(elem);

    // Now we need to layout the rest of the row details
    Table detailsLayout = new Table(myContainer);
    detailsLayout.setFixedLayout(true);
    detailsLayout.getElement().setClassName(getCss().detailsLayout());

    // We have a 1 row, 2 column layout
    TableRowElement row = detailsLayout.insertRow(-1);

    // Create the first column.
    eventTraceContainerCell = row.insertCell(-1);

    // Add the piechart and detailsTable to the second column
    TableCellElement detailsTableCell = row.insertCell(-1);
    detailsTableCell.getStyle().setPropertyPx("paddingRight",
        getCss().uiPadding());

    // Attach the pie chart.
    detailsTableContainer = new DefaultContainerImpl(detailsTableCell);
    PieChart pieChart = createPieChart(detailsTableContainer);
    int pieChartHeight = pieChart.getElement().getOffsetHeight()
        + getCss().uiPadding();

    this.detailsTable = createDetailsTable(detailsTableContainer,
        pieChartHeight, getParentRow().getEvent());

    // Now we populate the first column.
    Container eventTraceContainer = new DefaultContainerImpl(
        eventTraceContainerCell);
    treeDiv = eventTraceContainer.getDocument().createDivElement();
    eventTraceContainerCell.appendChild(treeDiv);

    hintletTree = createHintletTree(treeDiv);
    createEventTrace(eventTraceContainer, pieChartHeight);

    profileDiv = new Div(eventTraceContainer);
    updateProfile();

    // Ensure that window resizes don't mess up our row size due to text
    // reflow. Things may need to grow or shrink.
    manageEventListener(ResizeEvent.addResizeListener(
        WindowExt.getHostWindow(), WindowExt.getHostWindow(),
        new ResizeListener() {
          public void onResize(ResizeEvent event) {
            if (heightFixer == null && getParentRow().isExpanded()) {
              heightFixer = new Command.Method() {
                public void execute() {
                  // We don't want to do this for each resize, but once at
                  // the end.
                  fixHeightOfParentRow();
                  heightFixer = null;
                }
              };

              Command.defer(heightFixer, 200);
            }
          }
        }));
    return elem;
  }

  /**
   * Conditionally puts the profile UI below the trace tree.
   */
  void updateProfile() {
    if (getParentRow().getEvent().hasJavaScriptProfile()) {
      javaScriptProfileInProgress = false;
      buildProfileUi();
    } else if (getParentRow().getEvent().processingJavaScriptProfile()) {
      this.javaScriptProfileInProgress = true;
      profileDiv.setHtml("<h3>Profile</h3><div><i>Processing...</i></div>");
    } else {
      profileDiv.setHtml("");
    }
  }

  private void buildProfileUi() {
    profileDiv.getElement().setInnerHTML("");
    Container container = new DefaultContainerImpl(profileDiv.getElement());
    HeadingElement profileHeading = container.getDocument().createHElement(3);
    profileDiv.getElement().appendChild(profileHeading);
    profileHeading.setInnerText("Profile");
    ScopeBar bar = new ScopeBar(container, resources);
    // TODO(jaimeyap): This will always return the most recent page we are
    // viewing. This is a bug. We should have ApplicationState know the current
    // TabDescription.
    String currentAppUrl = eventWaterfall.getVisualization().getModel().getDataDispatcher().getTabDescription().getUrl();
    jsProfileRenderer = new JavaScriptProfileRenderer(
        container,
        resources,
        this,
        SymbolServerService.getSymbolServerController(new Url(currentAppUrl)),
        this,
        eventWaterfall.getVisualization().getModel().getJavaScriptProfileForEvent(
            getParentRow().getEvent()), new SourceSymbolClickListener() {
          public void onSymbolClicked(String resourceUrl,
              SourceViewerServer sourceViewerServer, int lineNumber,
              int column, String absoluteFilePath) {
            showSource(resourceUrl, sourceViewerServer, lineNumber, 0,
                absoluteFilePath);
          }
        }, new ResizeCallback() {
          public void onResize() {
            fixHeightOfParentRow();
          }
        });

    Element flatProfile = bar.add("Flat", new ProfileClickListener(
        JavaScriptProfile.PROFILE_TYPE_FLAT));
    bar.add("Top Down", new ProfileClickListener(
        JavaScriptProfile.PROFILE_TYPE_TOP_DOWN));
    bar.add("Bottom Up", new ProfileClickListener(
        JavaScriptProfile.PROFILE_TYPE_BOTTOM_UP));
    bar.setSelected(flatProfile, true);
  }

  /**
   * Creates the details table information for a single UiEvent selected in the
   * event trace tree.
   *
   * @param parent The parent {@link Container} that we will be attaching the
   *          table to.
   * @param pieChartHeight The height in pixels of the piechart so that we can
   *          position ourselves accordingly.
   * @param e The {@link UiEvent} that we will be displaying the details of.
   *
   * @return The {@link Table} that contains the detail information
   */
  private Table createDetailsTable(Container parent, int pieChartHeight,
      final UiEvent e) {
    IterableFastStringMap<CellRenderer> detailsMap = getDetailsMapForEvent(e);
    final Table table = new Table(parent);

    detailsMap.iterate(new IterableFastStringMap.IterationCallBack<CellRenderer>() {
      private boolean hasRow = false;

      public void onIteration(String key, CellRenderer cellRenderer) {
        // If we have at least one piece of data for this table, we add a
        // header
        if (!hasRow) {
          // Establish column widths.
          Element keyCol = Document.get().createElement("th");
          keyCol.setClassName(getCss().detailsKeyColumn());
          Element valCol = Document.get().createElement("th");
          table.getTableHead().appendChild(keyCol);
          table.getTableHead().appendChild(valCol);

          // Now add the title
          Element titleRow = Document.get().createElement("tr");
          Element title = Document.get().createElement("th");
          title.setAttribute("colspan", "2");
          title.setAttribute("align", "left");
          title.setInnerText("Details for "
              + UiEvent.typeToDetailedTypeString(e));
          title.getStyle().setWidth(100, Unit.PX);
          titleRow.appendChild(title);
          table.getTableHead().appendChild(titleRow);
          hasRow = true;
        }

        TableRowElement row = table.appendRow();
        TableCellElement cell = row.insertCell(-1);
        cell.setClassName(getCss().detailsTableKey());
        String rowKey = key.substring(1);
        cell.setInnerText(rowKey);
        cell = row.insertCell(-1);
        cellRenderer.render(cell);
      }
    });

    table.addStyleName(getCss().detailsTable());
    // ensure that the table is positioned below the pieChart
    table.getElement().getStyle().setPropertyPx("marginTop", pieChartHeight);
    return table;
  }

  private Tree createEventTrace(Container parent, final int pieChartHeight) {
    Widget header = new Widget(parent.getDocument().createHElement(2), parent) {
    };
    header.setStyleName(getCss().eventBreakdownHeader());
    header.getElement().setInnerText("Event Trace");

    final LazyEventTree tree = new LazyEventTree(parent,
        eventWaterfall.getPresenter(), getParentRow().getEvent(),
        getParentRow().getEventBreakdown(), resources);

    // Hook listeners to tree list to monitor selection changes and
    // expansion changes.
    tree.addSelectionChangeListener(new SelectionChangeListener() {
      Element offsetParent = getParentRow().getElement();

      public void onSelectionChange(ArrayList<Item> selected) {
        // Wipe the old table
        detailsTable.destroy();

        // Sort the nodes by start time.
        Collections.sort(selected, new Comparator<Item>() {

          public int compare(Item node1, Item node2) {
            UiEvent e1 = (UiEvent) node1.getItemTarget();
            UiEvent e2 = (UiEvent) node2.getItemTarget();

            return Double.compare(e1.getTime(), e2.getTime());
          }
        });

        Item newSelection = selected.get(selected.size() - 1);
        // Find how far to move table down to current selection.
        // We have to recursively walk up to compute the correct offset top.
        // We will encounter the UI padding two extra times along the way
        // crossing the tree boundary and crossing the details div boundary,
        // totally 3 encounters with padding we have to account for.
        int minTableOffset = Math.max(pieChartHeight,
            recursiveGetOffsetTop(newSelection.getElement())
                - (3 * getCss().uiPadding()));

        if (selected.size() == 1) {
          // We have a single selection. Simply display the details for the
          // single node.
          detailsTable = createDetailsTable(detailsTableContainer,
              minTableOffset, (UiEvent) newSelection.getItemTarget());

        } else {
          // Display aggregate information over the range of nodes.
          detailsTable = createMultiNodeDetailsTable(detailsTableContainer,
              minTableOffset, selected);
        }

        fixHeightOfParentRow();
      }

      private int recursiveGetOffsetTop(Element node) {
        if (node == null || node.getOffsetParent() == null
            || node.equals(offsetParent)) {
          return 0;
        } else {
          return node.getOffsetTop()
              + recursiveGetOffsetTop(node.getOffsetParent());
        }
      }

    });

    tree.addExpansionChangeListener(new ExpansionChangeListener() {

      public void onExpansionChange(Item changedItem) {
        fixHeightOfParentRow();
      }

    });

    // We make sure to have the tree cleaned up when we clean up ourselves.
    manageEventListener(tree.getRemover());

    return tree;
  }

  private HintletRecordsTree createHintletTree(DivElement parent) {
    if (!getParentRow().getEvent().hasHintRecords()) {
      return null;
    }

    JSOArray<HintRecord> hintlets = getParentRow().getEvent().getHintRecords();
    parent.setClassName(getCss().hintletList());

    HintletRecordsTree tree = new HintletRecordsTree(new DefaultContainerImpl(
        parent), hintlets, resources);

    // Hook listener to tree list to monitor expansion changes.
    tree.addExpansionChangeListener(new ExpansionChangeListener() {

      public void onExpansionChange(Item changedItem) {
        fixHeightOfParentRow();
      }

    });

    // We make sure to have the tree cleaned up when we clean up ourselves.
    manageEventListener(tree.getRemover());

    return tree;
  }

  private Table createMultiNodeDetailsTable(Container parent,
      int pieChartHeight, ArrayList<Item> selectedNodes) {
    Table table = new Table(parent);
    table.setFixedLayout(true);
    table.addStyleName(getCss().detailsTable());

    // Assume that List is sorted.
    UiEvent earliest = (UiEvent) selectedNodes.get(0).getItemTarget();
    UiEvent latest = (UiEvent) selectedNodes.get(selectedNodes.size() - 1).getItemTarget();
    double delta = latest.getTime() - earliest.getTime();

    TableRowElement row = table.appendRow();
    TableCellElement cell = row.insertCell(-1);
    cell.setClassName(getCss().detailsTableKey());
    cell.setInnerText("Time Delta");
    cell = row.insertCell(-1);
    cell.setInnerText(TimeStampFormatter.formatMilliseconds(delta));

    // ensure that the table is positioned below the pieChart
    table.getElement().getStyle().setPropertyPx("marginTop", pieChartHeight);
    return table;
  }

  private PieChart createPieChart(Container parent) {
    ensureData();
    // We put an extra div in there to center our piechart and to apply
    // the rounded corners and backing layer styles underneath the piechart.
    Div centeringDiv = new Div(parent);
    centeringDiv.addStyleName(getCss().pieChartContainer());
    PieChart chart = new PieChart(centeringDiv, data, resources);
    chart.showLegend();

    return chart;
  }

  private void ensureData() {
    if (data == null) {
      data = new ArrayList<ColorCodedValue>();
      UiEvent event = getParentRow().getEvent();
      JsIntegerDoubleMap durations = event.getTypeDurations();

      assert (durations != null);

      durations.iterate(new JsIntegerDoubleMap.IterationCallBack() {
        public void onIteration(int key, double val) {
          if (val > 0) {
            data.add(new ColorCodedValue(EventRecord.typeToString(key)
                + " (" + TimeStampFormatter.format(val) + ")", val,
                EventRecordColors.getColorForType(key)));
          }
        }
      });
      Collections.sort(data);
    }
  }

  private void fixHeightOfParentRow() {
    // Our height should be the size of the details panel + the row height
    int height = getElement().getOffsetHeight()
        + resources.filteringScrollTableCss().rowHeight();
    getParentRow().getElement().getStyle().setPropertyPx("height", height);
  }

  private EventWaterfallRowDetails.Css getCss() {
    return resources.eventWaterfallRowDetailsCss();
  }

  /**
   * Goes to concrete implementation to construct details map for an event.
   *
   * @param e the {@link UiEvent}
   * @return the details Map for the UiEvent
   */
  private IterableFastStringMap<CellRenderer> getDetailsMapForEvent(UiEvent e) {
    final IterableFastStringMap<CellRenderer> details = new IterableFastStringMap<CellRenderer>();

    details.put("Description", new StringCellRenderer(e.getHelpString()));
    details.put("@", new StringCellRenderer(
        TimeStampFormatter.formatMilliseconds(e.getTime())));
    if (e.getDuration() > 0) {
      details.put("Duration", new StringCellRenderer(
          TimeStampFormatter.formatMilliseconds(e.getDuration(), 3)));
    }

    String currentAppUrl = eventWaterfall.getVisualization().getModel().getDataDispatcher().getTabDescription().getUrl();

    // This is the stackTrace output by newer versions of WebKit.
    JSOArray<StackFrame> stackTrace = e.getStackTrace();
    if (stackTrace != null) {
      details.put("Stack Trace", new StackTraceRenderer(stackTrace,
          symbolClickListener, this, currentAppUrl, this, true, resources));
    }
    // This is the caller location output by older versions of WebKit.
    // TODO(jaimeyap): remove this once stack trace API reaches Chromium beta.
    if (e.hasCallLocation()) {
      stackTrace = JSOArray.create();
      stackTrace.push(StackFrame.create(e.getCallerScriptName(),
          e.getCallerFunctionName(), e.getCallerScriptLine(), 0));
      details.put("Caused by", new StackTraceRenderer(stackTrace,
          symbolClickListener, this, currentAppUrl, this, true, resources));
    }

    switch (e.getType()) {
      case TimerFiredEvent.TYPE:
        TimerInstalled timerData = eventWaterfall.getSourceDispatcher().getTimerMetaData(
            e.<TimerInstalled> cast().getTimerId());
        populateDetailsForTimerInstall(timerData, details);
        break;
      case TimerInstalled.TYPE:
        populateDetailsForTimerInstall(e.<TimerInstalled> cast(), details);
        break;
      case TimerCleared.TYPE:
        details.put("Cleared Timer Id", new StringCellRenderer(
            e.<TimerCleared> cast().getTimerId() + ""));
        break;
      case PaintEvent.TYPE:
        PaintEvent paintEvent = e.cast();
        details.put("Origin", new StringCellRenderer(paintEvent.getX() + ", "
            + paintEvent.getY()));
        details.put("Size", new StringCellRenderer(paintEvent.getWidth()
            + " x " + paintEvent.getHeight()));
        break;
      case ParseHtmlEvent.TYPE:
        ParseHtmlEvent parseHtmlEvent = e.cast();
        details.put("Line Number", new StringCellRenderer(
            parseHtmlEvent.getStartLine() + ""));
        details.put("Length", new StringCellRenderer(parseHtmlEvent.getLength()
            + " characters"));
        break;
      case EvalScript.TYPE:
        EvalScript scriptTagEvent = e.cast();
        details.put("Url", new StringCellRenderer(scriptTagEvent.getURL()));
        details.put("Line Number", new StringCellRenderer(
            scriptTagEvent.getLineNumber() + ""));
        break;
      case XhrReadyStateChangeEvent.TYPE:
        XhrReadyStateChangeEvent xhrEvent = e.cast();
        details.put("Ready State", new StringCellRenderer(
            xhrEvent.getReadyState() + ""));
        details.put("Url", new StringCellRenderer(xhrEvent.getUrl()));
        break;
      case XhrLoadEvent.TYPE:
        details.put("Url", new StringCellRenderer(
            e.<XhrLoadEvent> cast().getUrl()));
        break;
      case LogEvent.TYPE:
        LogEvent logEvent = e.cast();
        details.put("Message", new StringCellRenderer(logEvent.getMessage()));
        break;
      case JavaScriptExecutionEvent.TYPE:
        JavaScriptExecutionEvent jsExecEvent = e.cast();
        JSOArray<StackFrame> function = JSOArray.create();
        function.push(StackFrame.create(jsExecEvent.getScriptName(),
            "[unknown]", jsExecEvent.getScriptLine(), 0));
        details.put("Function Call", new StackTraceRenderer(function,
            symbolClickListener, this, currentAppUrl, this, true, resources));
        break;
      case ResourceDataReceivedEvent.TYPE:
        ResourceDataReceivedEvent dataRecEvent = e.cast();
        DataDispatcher dataDispatcher = eventWaterfall.getVisualization().getModel().getDataDispatcher();
        NetworkEventDispatcher networkDispatcher = dataDispatcher.getNetworkEventDispatcher();
        NetworkResource resource = networkDispatcher.getResource(dataRecEvent.getRequestId());
        if (resource != null) {
          String resourceUrlStr = resource.getLastPathComponent();
          resourceUrlStr = "".equals(resourceUrlStr) ? resource.getUrl()
              : resourceUrlStr;
          details.put("Processing Resource", new StringCellRenderer(
              resourceUrlStr));
        }
        break;
      default:
        break;
    }

    if (e.getOverhead() > 0) {
      details.put("Overhead", new StringCellRenderer(
          TimeStampFormatter.formatMilliseconds(e.getOverhead(), 2)));
    }

    if (CustomEvent.isCustomEvent(e.getType())) {
      // Render key-value pairs for the custom event.
      // Simply iterate over the custom data on the CustomEvent.
      CustomEvent customEvent = e.cast();
      DataBag customData = customEvent.getCustomData();
      customData.iterate(new IterationCallback() {
        public void onIteration(String key, String value) {
          details.put(key, new StringCellRenderer(value));
        }
      });
    }

    return details;
  }

  private void populateDetailsForTimerInstall(TimerInstalled timerData,
      IterableFastStringMap<CellRenderer> details) {
    if (timerData != null) {
      details.put("Timer Id", new StringCellRenderer(timerData.getTimerId()
          + ""));
      details.put("Timer Type", new StringCellRenderer(timerData.isSingleShot()
          ? "setTimeout" : "setInterval"));
      details.put("Interval", new StringCellRenderer(timerData.getInterval()
          + "ms"));
    }
  }

  /**
   * Make sure the source viewer exists and is loaded at the specified resource
   * URL.
   */
  private void showSourceInSourceViewer(final String resourceUrl,
      final SourceViewerLoadedCallback callback) {
    if (sourceViewer == null) {
      // Attach the container above the table so that the source viewer is
      // positioned independent of the table scroll and of the currently
      // viewed row.
      SourceViewer.create(eventWaterfall.getTableContents().getParentElement(),
          resources, new SourceViewerInitializedCallback() {
            public void onSourceViewerInitialized(SourceViewer viewer) {
              EventWaterfallRowDetails.this.sourceViewer = viewer;
              // Position the source viewer so that it fills half the
              // details view. Below the table header, and flush with the
              // bottom of the window.
              viewer.getElement().getStyle().setTop(1, Unit.PX);
              // Half the width with a little space for border of the table.
              viewer.getElement().getStyle().setRight(51, Unit.PCT);

              // Now request the resource
              viewer.loadResource(resourceUrl, callback);
            }
          });
    } else {
      sourceViewer.loadResource(resourceUrl, callback);
    }
  }
}
TOP

Related Classes of com.google.speedtracer.client.visualizations.view.EventWaterfallRowDetails$Resources

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.