Merge "Update README.chromium to point to correct license file"
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index fb4e502..5ae4598 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -18,20 +18,23 @@
 
 type StateDraft = DraftObject<State>;
 
+
+function clearTraceState(state: StateDraft) {
+  state.traceTime = defaultTraceTime;
+  state.visibleTraceTime = defaultTraceTime;
+  state.pinnedTracks = [];
+  state.scrollingTracks = [];
+}
+
 export const StateActions = {
 
   navigate(state: StateDraft, args: {route: string}): void {
     state.route = args.route;
   },
 
-  // TODO(hjd): Factor common code from openTraceFromUrl.
   openTraceFromFile(state: StateDraft, args: {file: File}): void {
-    state.traceTime = defaultTraceTime;
-    state.visibleTraceTime = defaultTraceTime;
+    clearTraceState(state);
     const id = `${state.nextId++}`;
-    // Reset displayed tracks.
-    state.pinnedTracks = [];
-    state.scrollingTracks = [];
     state.engines[id] = {
       id,
       ready: false,
@@ -40,14 +43,9 @@
     state.route = `/viewer`;
   },
 
-  // TODO(hjd): Factor common code from openTraceFromFile.
   openTraceFromUrl(state: StateDraft, args: {url: string}): void {
-    state.traceTime = defaultTraceTime;
-    state.visibleTraceTime = defaultTraceTime;
+    clearTraceState(state);
     const id = `${state.nextId++}`;
-    // Reset displayed tracks.
-    state.pinnedTracks = [];
-    state.scrollingTracks = [];
     state.engines[id] = {
       id,
       ready: false,
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 15be904..9329053 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -57,26 +57,29 @@
     renderStats: new RunningStatistics(10),
   };
 
-  // attrs received in the most recent mithril redraw.
-  private attrs?: Attrs;
+  // Attrs received in the most recent mithril redraw. We receive a new vnode
+  // with new attrs on every redraw, and we cache it here so that resize
+  // listeners and canvas redraw callbacks can access it.
+  private attrs: Attrs;
 
-  private canvasOverdrawFactor: number;
   private ctx?: CanvasRenderingContext2D;
 
   private onResize: () => void = () => {};
   private parentOnScroll: () => void = () => {};
   private canvasRedrawer: () => void;
 
+  get canvasOverdrawFactor() {
+    return this.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
+  }
+
   constructor(vnode: m.CVnode<Attrs>) {
-    this.canvasOverdrawFactor =
-        vnode.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
+    this.attrs = vnode.attrs;
     this.canvasRedrawer = () => this.redrawCanvas();
     globals.rafScheduler.addRedrawCallback(this.canvasRedrawer);
     perfDisplay.addContainer(this);
   }
 
   oncreate(vnodeDom: m.CVnodeDOM<Attrs>) {
-    const attrs = vnodeDom.attrs;
     // Save the canvas context in the state.
     const canvas =
         vnodeDom.dom.querySelector('.main-canvas') as HTMLCanvasElement;
@@ -91,21 +94,18 @@
     this.parentWidth = clientRect.width;
     this.parentHeight = clientRect.height;
 
-    this.updatePanelHeightsFromDom(vnodeDom);
+    this.readPanelHeightsFromDom(vnodeDom.dom);
     (vnodeDom.dom as HTMLElement).style.height = `${this.totalPanelHeight}px`;
 
-    this.canvasHeight = this.getCanvasHeight(attrs.doesScroll);
-    this.updateCanvasDimensions(vnodeDom);
+    this.updateCanvasDimensions();
+    this.repositionCanvas();
 
     // Save the resize handler in the state so we can remove it later.
     // TODO: Encapsulate resize handling better.
     this.onResize = () => {
-      const clientRect =
-          assertExists(vnodeDom.dom.parentElement).getBoundingClientRect();
-      this.parentWidth = clientRect.width;
-      this.parentHeight = clientRect.height;
-      this.canvasHeight = this.getCanvasHeight(attrs.doesScroll);
-      this.updateCanvasDimensions(vnodeDom);
+      this.readParentSizeFromDom(vnodeDom.dom);
+      this.updateCanvasDimensions();
+      this.repositionCanvas();
       globals.rafScheduler.scheduleFullRedraw();
     };
 
@@ -113,10 +113,10 @@
     window.addEventListener('resize', this.onResize);
 
     // TODO(dproy): Handle change in doesScroll attribute.
-    if (vnodeDom.attrs.doesScroll) {
+    if (this.attrs.doesScroll) {
       this.parentOnScroll = () => {
-        this.scrollTop = vnodeDom.dom.parentElement!.scrollTop;
-        this.repositionCanvas(vnodeDom);
+        this.scrollTop = assertExists(vnodeDom.dom.parentElement).scrollTop;
+        this.repositionCanvas();
         globals.rafScheduler.scheduleRedraw();
       };
       vnodeDom.dom.parentElement!.addEventListener(
@@ -134,8 +134,6 @@
   }
 
   view({attrs}: m.CVnode<Attrs>) {
-    // We receive a new vnode object with new attrs on every mithril redraw. We
-    // store the latest attrs so redrawCanvas can use it.
     this.attrs = attrs;
     const renderPanel = (panel: m.Vnode) => perfDebug() ?
         m('.panel', panel, m('.debug-panel-border')) :
@@ -148,25 +146,27 @@
   }
 
   onupdate(vnodeDom: m.CVnodeDOM<Attrs>) {
-    this.repositionCanvas(vnodeDom);
+    const totalPanelHeightChanged = this.readPanelHeightsFromDom(vnodeDom.dom);
+    const parentSizeChanged = this.readParentSizeFromDom(vnodeDom.dom);
 
-    if (this.updatePanelHeightsFromDom(vnodeDom)) {
+    if (totalPanelHeightChanged) {
       (vnodeDom.dom as HTMLElement).style.height = `${this.totalPanelHeight}px`;
     }
 
-    // In non-scrolling case, canvas height can change if panel heights changed.
-    const canvasHeight = this.getCanvasHeight(vnodeDom.attrs.doesScroll);
-    if (this.canvasHeight !== canvasHeight) {
-      this.canvasHeight = canvasHeight;
-      this.updateCanvasDimensions(vnodeDom);
+    const canvasSizeShouldChange =
+        this.attrs.doesScroll ? parentSizeChanged : totalPanelHeightChanged;
+    if (canvasSizeShouldChange) {
+      this.updateCanvasDimensions();
+      this.repositionCanvas();
     }
   }
 
-  private updateCanvasDimensions(vnodeDom: m.CVnodeDOM<Attrs>) {
-    const canvas =
-        assertExists(vnodeDom.dom.querySelector('canvas.main-canvas')) as
-        HTMLCanvasElement;
+  private updateCanvasDimensions() {
+    this.canvasHeight = this.attrs.doesScroll ?
+        this.parentHeight * this.canvasOverdrawFactor :
+        this.totalPanelHeight;
     const ctx = assertExists(this.ctx);
+    const canvas = assertExists(ctx.canvas);
     canvas.style.height = `${this.canvasHeight}px`;
     const dpr = window.devicePixelRatio;
     ctx.canvas.width = this.parentWidth * dpr;
@@ -174,13 +174,36 @@
     ctx.scale(dpr, dpr);
   }
 
-  private updatePanelHeightsFromDom(vnodeDom: m.CVnodeDOM<Attrs>): boolean {
+  private repositionCanvas() {
+    const canvas = assertExists(assertExists(this.ctx).canvas);
+    const canvasYStart = this.scrollTop - this.getCanvasOverdrawHeightPerSide();
+    canvas.style.transform = `translateY(${canvasYStart}px)`;
+  }
+
+  /**
+   * Reads dimensions of parent node. Returns true if read dimensions are
+   * different from what was cached in the state.
+   */
+  private readParentSizeFromDom(dom: Element): boolean {
+    const oldWidth = this.parentWidth;
+    const oldHeight = this.parentHeight;
+    const clientRect = assertExists(dom.parentElement).getBoundingClientRect();
+    this.parentWidth = clientRect.width;
+    this.parentHeight = clientRect.height;
+    return this.parentHeight !== oldHeight || this.parentWidth !== oldWidth;
+  }
+
+  /**
+   * Reads dimensions of panels. Returns true if total panel height is different
+   * from what was cached in state.
+   */
+  private readPanelHeightsFromDom(dom: Element): boolean {
     const prevHeight = this.totalPanelHeight;
     this.panelHeights = [];
     this.totalPanelHeight = 0;
 
-    const panels = vnodeDom.dom.querySelectorAll('.panel');
-    assertTrue(panels.length === vnodeDom.attrs.panels.length);
+    const panels = dom.querySelectorAll('.panel');
+    assertTrue(panels.length === this.attrs.panels.length);
     for (let i = 0; i < panels.length; i++) {
       const height = panels[i].getBoundingClientRect().height;
       this.panelHeights[i] = height;
@@ -190,19 +213,6 @@
     return this.totalPanelHeight !== prevHeight;
   }
 
-  private getCanvasHeight(doesScroll: boolean) {
-    return doesScroll ? this.parentHeight * this.canvasOverdrawFactor :
-                        this.totalPanelHeight;
-  }
-
-  private repositionCanvas(vnodeDom: m.CVnodeDOM<Attrs>) {
-    const canvas =
-        assertExists(vnodeDom.dom.querySelector('canvas.main-canvas')) as
-        HTMLCanvasElement;
-    const canvasYStart = this.scrollTop - this.getCanvasOverdrawHeightPerSide();
-    canvas.style.transform = `translateY(${canvasYStart}px)`;
-  }
-
   private overlapsCanvas(yStart: number, yEnd: number) {
     return yEnd > 0 && yStart < this.canvasHeight;
   }