| Index: pkg/shadow_dom/lib/shadow_dom.debug.js
 | 
| diff --git a/pkg/shadow_dom/lib/shadow_dom.debug.js b/pkg/shadow_dom/lib/shadow_dom.debug.js
 | 
| index 447f29c3a9926d8f8527e63278ded6ed60aa7876..4e84b7e70823a98b65ab07f7f4a10b6b3ce0e890 100644
 | 
| --- a/pkg/shadow_dom/lib/shadow_dom.debug.js
 | 
| +++ b/pkg/shadow_dom/lib/shadow_dom.debug.js
 | 
| @@ -592,6 +592,8 @@ if (!HTMLElement.prototype.createShadowRoot
 | 
|      this.isObserved = true;
 | 
|    }
 | 
|  
 | 
| +  // TODO(rafaelw): Consider surfacing a way to avoid observing prototype
 | 
| +  // ancestors which are expected not to change (e.g. Element, Node...).
 | 
|    var objProto = Object.getPrototypeOf({});
 | 
|    var arrayProto = Object.getPrototypeOf([]);
 | 
|    ObservedSet.prototype = {
 | 
| @@ -734,6 +736,8 @@ if (!HTMLElement.prototype.createShadowRoot
 | 
|    CompoundPathObserver.prototype = createObject({
 | 
|      __proto__: PathObserver.prototype,
 | 
|  
 | 
| +    // TODO(rafaelw): Consider special-casing when |object| is a PathObserver
 | 
| +    // and path 'value' to avoid explicit observation.
 | 
|      addPath: function(object, path) {
 | 
|        if (this.started)
 | 
|          throw Error('Cannot add more paths once started.');
 | 
| @@ -798,7 +802,8 @@ if (!HTMLElement.prototype.createShadowRoot
 | 
|  
 | 
|          this.reportArgs = [this.value, this.oldValue];
 | 
|        } else {
 | 
| -        this.reportArgs = [this.values, this.oldValues, this.changeFlags];
 | 
| +        this.reportArgs = [this.values, this.oldValues, this.changeFlags,
 | 
| +                           this.observed];
 | 
|        }
 | 
|  
 | 
|        return true;
 | 
| @@ -1947,22 +1952,18 @@ var ShadowDOMPolyfill = {};
 | 
|      return false;
 | 
|    }
 | 
|  
 | 
| -  function isMutationEvent(type) {
 | 
| -    switch (type) {
 | 
| -      case 'DOMAttrModified':
 | 
| -      case 'DOMAttributeNameChanged':
 | 
| -      case 'DOMCharacterDataModified':
 | 
| -      case 'DOMElementNameChanged':
 | 
| -      case 'DOMNodeInserted':
 | 
| -      case 'DOMNodeInsertedIntoDocument':
 | 
| -      case 'DOMNodeRemoved':
 | 
| -      case 'DOMNodeRemovedFromDocument':
 | 
| -      case 'DOMSubtreeModified':
 | 
| -        return true;
 | 
| -    }
 | 
| -    return false;
 | 
| +  var mutationEventsAreSilenced = 0;
 | 
| +
 | 
| +  function muteMutationEvents() {
 | 
| +    mutationEventsAreSilenced++;
 | 
| +  }
 | 
| +
 | 
| +  function unmuteMutationEvents() {
 | 
| +    mutationEventsAreSilenced--;
 | 
|    }
 | 
|  
 | 
| +  var OriginalMutationEvent = window.MutationEvent;
 | 
| +
 | 
|    function dispatchOriginalEvent(originalEvent) {
 | 
|      // Make sure this event is only dispatched once.
 | 
|      if (handledEventsTable.get(originalEvent))
 | 
| @@ -1972,8 +1973,12 @@ var ShadowDOMPolyfill = {};
 | 
|      // Don't do rendering if this is a mutation event since rendering might
 | 
|      // mutate the DOM which would fire more events and we would most likely
 | 
|      // just iloop.
 | 
| -    if (!isMutationEvent(originalEvent.type))
 | 
| +    if (originalEvent instanceof OriginalMutationEvent) {
 | 
| +      if (mutationEventsAreSilenced)
 | 
| +        return;
 | 
| +    } else {
 | 
|        scope.renderAllPending();
 | 
| +    }
 | 
|  
 | 
|      var target = wrap(originalEvent.target);
 | 
|      var event = wrap(originalEvent);
 | 
| @@ -2102,7 +2107,7 @@ var ShadowDOMPolyfill = {};
 | 
|          if (window.onerror)
 | 
|            window.onerror(ex.message);
 | 
|          else
 | 
| -          console.error(ex);
 | 
| +          console.error(ex, ex.stack);
 | 
|        }
 | 
|      }
 | 
|  
 | 
| @@ -2491,6 +2496,8 @@ var ShadowDOMPolyfill = {};
 | 
|    scope.elementFromPoint = elementFromPoint;
 | 
|    scope.getEventHandlerGetter = getEventHandlerGetter;
 | 
|    scope.getEventHandlerSetter = getEventHandlerSetter;
 | 
| +  scope.muteMutationEvents = muteMutationEvents;
 | 
| +  scope.unmuteMutationEvents = unmuteMutationEvents;
 | 
|    scope.wrapEventTargetMethods = wrapEventTargetMethods;
 | 
|    scope.wrappers.CustomEvent = CustomEvent;
 | 
|    scope.wrappers.Event = Event;
 | 
| @@ -3602,8 +3609,10 @@ var ShadowDOMPolyfill = {};
 | 
|    var HTMLElement = scope.wrappers.HTMLElement;
 | 
|    var getInnerHTML = scope.getInnerHTML;
 | 
|    var mixin = scope.mixin;
 | 
| +  var muteMutationEvents = scope.muteMutationEvents;
 | 
|    var registerWrapper = scope.registerWrapper;
 | 
|    var setInnerHTML = scope.setInnerHTML;
 | 
| +  var unmuteMutationEvents = scope.unmuteMutationEvents;
 | 
|    var unwrap = scope.unwrap;
 | 
|    var wrap = scope.wrap;
 | 
|  
 | 
| @@ -3632,9 +3641,11 @@ var ShadowDOMPolyfill = {};
 | 
|      var doc = getTemplateContentsOwner(templateElement.ownerDocument);
 | 
|      var df = unwrap(doc.createDocumentFragment());
 | 
|      var child;
 | 
| +    muteMutationEvents();
 | 
|      while (child = templateElement.firstChild) {
 | 
|        df.appendChild(child);
 | 
|      }
 | 
| +    unmuteMutationEvents();
 | 
|      return df;
 | 
|    }
 | 
|  
 | 
| @@ -3808,7 +3819,9 @@ var ShadowDOMPolyfill = {};
 | 
|    var assert = scope.assert;
 | 
|    var getHostForShadowRoot = scope.getHostForShadowRoot;
 | 
|    var mixin = scope.mixin;
 | 
| +  var muteMutationEvents = scope.muteMutationEvents;
 | 
|    var oneOf = scope.oneOf;
 | 
| +  var unmuteMutationEvents = scope.unmuteMutationEvents;
 | 
|    var unwrap = scope.unwrap;
 | 
|    var wrap = scope.wrap;
 | 
|  
 | 
| @@ -4119,7 +4132,7 @@ var ShadowDOMPolyfill = {};
 | 
|        }
 | 
|  
 | 
|        for (var i = lastIndex; i < newChildren.length; i++) {
 | 
| -        newChildren[i++].sync(added);
 | 
| +        newChildren[i].sync(added);
 | 
|        }
 | 
|      }
 | 
|    };
 | 
| @@ -4152,8 +4165,11 @@ var ShadowDOMPolyfill = {};
 | 
|          this.renderNode(shadowRoot, renderNode, node, false);
 | 
|        }
 | 
|  
 | 
| -      if (topMostRenderer)
 | 
| +      if (topMostRenderer) {
 | 
| +        //muteMutationEvents();
 | 
|          renderNode.sync();
 | 
| +        //unmuteMutationEvents();
 | 
| +      }
 | 
|  
 | 
|        this.dirty = false;
 | 
|      },
 | 
| @@ -5163,6 +5179,13 @@ var ShadowDOMPolyfill = {};
 | 
|        if (window.MutationObserver && (obj instanceof MutationObserver))
 | 
|            return 'MutationObserver';
 | 
|  
 | 
| +      // TODO(jmesserly): this prevents incorrect interaction between ShadowDOM
 | 
| +      // and dart:html's <template> polyfill. Essentially, ShadowDOM is
 | 
| +      // polyfilling native template, but our Dart polyfill fails to detect this
 | 
| +      // because the unwrapped node is an HTMLUnknownElement, leading it to
 | 
| +      // think the node has no content.
 | 
| +      if (obj instanceof HTMLTemplateElement) return 'HTMLTemplateElement';
 | 
| +
 | 
|        var unwrapped = unwrapIfNeeded(obj);
 | 
|        if (obj !== unwrapped) {
 | 
|          // Fix up class names for Firefox.
 | 
| 
 |