Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

stripCustomNsAttrs performance #14928

Closed
Wintermoose opened this issue Jul 18, 2016 · 7 comments
Closed

stripCustomNsAttrs performance #14928

Wintermoose opened this issue Jul 18, 2016 · 7 comments

Comments

@Wintermoose
Copy link

Performance suggestion

stripCustomNsAttrs is currently using recursive to walk the siblings in the DOM tree, which can easily cause it to run out of stack space in Internet Explorer. That happens to us when sanitizing a string containing cca 3000 DOM elements (which we do as performance optimization over ng-repeat, normally we of course bind much smaller strings).

It seems rewriting the code to use iteration should be safe, and resolves the problem entirely:

function stripCustomNsAttrs(node) {
  while(node) {
      if (node.nodeType === window.Node.ELEMENT_NODE) {
        var attrs = node.attributes;
        for (var i = 0, l = attrs.length; i < l; i++) {
          var attrNode = attrs[i];
          var attrName = attrNode.name.toLowerCase();
          if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
            node.removeAttributeNode(attrNode);
            i--;
            l--;
          }
        }
      }

      var nextNode = node.firstChild;
      if (nextNode) {
        stripCustomNsAttrs(nextNode);
      }

      node = node.nextSibling;
  }
}

We experience the issue in Angular 1.5.7, Internet Explorer 11. The string bound is something like
<div class="entry"><div class="icon icontype"></div><span>Name</span></div> repeated 3000x

@Narretz
Copy link
Contributor

Narretz commented Jul 18, 2016

Thanks for raising this issue.
Can you create a runnable demo that shows this problem? Does IE crash or freeze when doing this? You can also open a PR with your suggested fix.

@Wintermoose
Copy link
Author

Hello. IE throws exception:
Error: Out of stack space
at stripCustomNsAttrs (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.js:492:3)
at stripCustomNsAttrs (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.js:507:5)
at stripCustomNsAttrs (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.js:512:5)
at stripCustomNsAttrs (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.js:512:5)
...

The test below should trip it reliably (it does on both of my test system, but feel free to increase the number of elements added in the loop);

<!doctype html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>IE11 ngSanitize test</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-sanitize.min.js"></script>
        <script type="text/javascript">
            function testerFn($scope) {
                var list = [];
                for (var i = 0; i < 3000; ++i) {
                    list.push('<div class="entry">');
                    list.push('<icon class="icon"></div>');
                    list.push('<span>Name</span>');
                    list.push('</div>');
                }
                $scope.source = list.join('');
            }
            testerFn.$inject = ["$scope"];

            angular.module("test", ["ngSanitize"])
            .controller("tester", testerFn);
        </script>
    </head>
    <body>
        <h1>stripCustomNsAttrs test</h1>
        <div ng-app="test" ng-controller="tester">
            <div ng-bind-html="source"></div>
        </div>
    </body>
</html>

For reference, Edge seems to manage just fine even with 8x the amount: I am not sure if they allocate bigger stack, or Chakra implements tail-recursion optimization

@gkalpak
Copy link
Member

gkalpak commented Jul 18, 2016

Fwiw, it seems to indeed crash on IE11, but only with DevTools open. If I close the DevTools, it works fine.

@Wintermoose
Copy link
Author

When you say "works fine", does it actually render the list (long column of "name" lines)? Because the exception is normally silent, but aborts the bind operation

@gkalpak
Copy link
Member

gkalpak commented Jul 18, 2016

does it actually render the list (long column of "name" lines)?

It does.
(To be clear, I still believe this should be fixed - especially, if the fix is a simple refactoring.)

@BobChao87
Copy link
Contributor

I used the script mentioned here along with something similar to the fix mentioned here and increased IE 11 from breaking on ~3k records to breaking on ~200k records. A grand improvement to be sure!

I am worried a bit about over-optimizing though, since the script and fix are heavily biased towards sibling nodes. I am wondering if that is something I should be concerned about. I have an idea bouncing around that could potentially address both child and sibling nodes, but if we are much more concerned with one than the other, the solution is significantly simpler to optimize for only one.

Chrome doesn't seem much affected either way by this script, managing ~1M records without too much issue.

@Wintermoose
Copy link
Author

The whole code could be changed to walk the tree up/down the same way as the digest loop does; but I am not really sure it's worth the effort - the stack depth should now correspond to the depth of the DOM tree, and I wouldn't really expect that to be an issue.

BobChao87 pushed a commit to BobChao87/angular.js that referenced this issue Aug 11, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less recursive
Reduce stack height of function causing out of space error

Closes angular#14928
BobChao87 pushed a commit to BobChao87/angular.js that referenced this issue Aug 16, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
BobChao87 added a commit to BobChao87/angular.js that referenced this issue Aug 29, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
BobChao87 added a commit to BobChao87/angular.js that referenced this issue Sep 4, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
@Narretz Narretz closed this as completed in 45129cf Sep 5, 2016
Narretz pushed a commit that referenced this issue Sep 8, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes #14928
Closes #15030
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Nov 21, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
Closes angular#15030
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Nov 21, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
Closes angular#15030
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Nov 21, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
Closes angular#15030
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Nov 21, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
Closes angular#15030
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Nov 21, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes angular#14928
Closes angular#15030
petebacondarwin pushed a commit that referenced this issue Nov 23, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes #14928
Closes #15030
petebacondarwin pushed a commit that referenced this issue Nov 24, 2016
Update Internet Explorer-only helper function stripCustomNsAttrs to be less
recursive. Reduce stack height of function causing out of stack space error.

Closes #14928
Closes #15030
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants