Cut dep from highlight onto comment model

Change-Id: Ifbe7dc9f08e6b9ebca04b5316ce51571fa5a4946
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 7f7ec72..862db10 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -280,10 +280,10 @@
         <gr-diff-highlight
             id="highlights"
             logged-in="[[loggedIn]]"
-            comments="{{comments}}">
+            comment-ranges="{{_commentRanges}}">
           <gr-diff-builder
               id="diffBuilder"
-              comments="[[comments]]"
+              comment-ranges="[[_commentRanges]]"
               project-name="[[projectName]]"
               diff="[[diff]]"
               diff-path="[[path]]"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 8b9c1d9..5c89647 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -43,6 +43,11 @@
    *             end_line: number, end_character: number}} */
   Gerrit.Range;
 
+  function isThreadEl(node) {
+    return node.nodeType === Node.ELEMENT_NODE &&
+        node.classList.contains('comment-thread');
+  }
+
   Polymer({
     is: 'gr-diff',
 
@@ -100,6 +105,11 @@
         type: Object,
         value: {left: [], right: []},
       },
+      /** @type {!Array<!Gerrit.HoveredRange>} */
+      _commentRanges: {
+        type: Array,
+        value: [],
+      },
       lineWrapping: {
         type: Boolean,
         value: false,
@@ -185,7 +195,19 @@
 
       _diffLength: Number,
 
-      /** @type {?PolymerDomApi.ObserveHandle} */
+      /**
+       * Observes comment nodes added or removed after the initial render.
+       * Can be used to unregister when the entire diff is (re-)rendered or upon
+       * detachment.
+       * @type {?PolymerDomApi.ObserveHandle}
+       */
+      _incrementalNodeObserver: Object,
+
+      /**
+       * Observes comment nodes added or removed at any point.
+       * Can be used to unregister upon detachment.
+       * @type {?PolymerDomApi.ObserveHandle}
+       */
       _nodeObserver: Object,
     },
 
@@ -201,10 +223,36 @@
       'render-content': '_handleRenderContent',
     },
 
+    attached() {
+      this._updateRangesWhenNodesChange();
+    },
+
     detached() {
+      this._unobserveIncrementalNodes();
       this._unobserveNodes();
     },
 
+    _updateRangesWhenNodesChange() {
+      function commentRangeFromThreadEl(threadEl) {
+        const side = threadEl.getAttribute('comment-side');
+        const range = JSON.parse(threadEl.getAttribute('range'));
+        return {side, range, hovering: false};
+      }
+
+      this._nodeObserver = Polymer.dom(this).observeNodes(info => {
+        const addedThreadEls = info.addedNodes.filter(isThreadEl);
+        const addedCommentRanges = addedThreadEls
+            .map(commentRangeFromThreadEl)
+            .filter(({range}) => range);
+        this.push('_commentRanges', ...addedCommentRanges);
+        // In principal we should also handle removed nodes, but I have not
+        // figured out how to do that yet without also catching all the removals
+        // caused by further redistribution. Right now, comments are never
+        // removed by no longer slotting them in, so I decided to not handle
+        // this situation until it occurs.
+      });
+    },
+
     /** Cancel any remaining diff builder rendering work. */
     cancel() {
       this.$.diffBuilder.cancel();
@@ -577,7 +625,7 @@
     },
 
     _renderDiffTable() {
-      this._unobserveNodes();
+      this._unobserveIncrementalNodes();
       if (!this.prefs) {
         this.dispatchEvent(new CustomEvent('render', {bubbles: true}));
         return;
@@ -595,9 +643,8 @@
     },
 
     _handleRenderContent() {
-      this._nodeObserver = Polymer.dom(this).observeNodes(info => {
-        const addedThreadEls = info.addedNodes.filter(
-            node => node.nodeType === Node.ELEMENT_NODE);
+      this._incrementalNodeObserver = Polymer.dom(this).observeNodes(info => {
+        const addedThreadEls = info.addedNodes.filter(isThreadEl);
         // In principal we should also handle removed nodes, but I have not
         // figured out how to do that yet without also catching all the removals
         // caused by further redistribution. Right now, comments are never
@@ -616,6 +663,12 @@
       });
     },
 
+    _unobserveIncrementalNodes() {
+      if (this._incrementalNodeObserver) {
+        Polymer.dom(this).unobserveNodes(this._incrementalNodeObserver);
+      }
+    },
+
     _unobserveNodes() {
       if (this._nodeObserver) {
         Polymer.dom(this).unobserveNodes(this._nodeObserver);
@@ -633,7 +686,7 @@
     },
 
     clearDiffContent() {
-      this._unobserveNodes();
+      this._unobserveIncrementalNodes();
       this.$.diffTable.innerHTML = null;
     },