@angular/platform-server 22.2.0-next.3 → 22.2.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -850,11 +850,17 @@ function requireNodeUtils () {
850
850
 
851
851
  function fallbackRawContentTags(node) {
852
852
  const tags = [];
853
- while (node?.nodeType === 1 /*ELEMENT_NODE*/) {
854
- if (node.namespaceURI === NAMESPACE.HTML && hasRawContentFallback[node.tagName]) {
855
- tags.push(node.localName);
853
+ while (node) {
854
+ if (node.nodeType === 1 /*ELEMENT_NODE*/) {
855
+ if (node.namespaceURI === NAMESPACE.HTML && hasRawContentFallback[node.tagName]) {
856
+ tags.push(node.localName);
857
+ }
858
+ node = node.parentNode;
859
+ } else if (node.nodeType === 11 /*DOCUMENT_FRAGMENT_NODE*/ && node._host) {
860
+ node = node._host;
861
+ } else {
862
+ node = node.parentNode;
856
863
  }
857
- node = node.parentNode;
858
864
  }
859
865
  return tags;
860
866
  }
@@ -938,18 +944,41 @@ function requireNodeUtils () {
938
944
 
939
945
  const CLOSING_COMMENT_REGEXP = /--!?>/;
940
946
 
947
+ /**
948
+ * Escapes a comment content that abruptly closes the comment.
949
+ *
950
+ * A comment can not carry content that starts with `>` or `->`: the parser
951
+ * closes the comment as soon as it comes across such a sequence right after
952
+ * `<!--` (the "abrupt-closing-of-empty-comment" parse error). For example,
953
+ * `#comment('><img src=x onerror=alert(1)>')` would otherwise serialize into
954
+ * `<!--><img src=x onerror=alert(1)>-->`, which de-serializes into an empty
955
+ * comment followed by a live `<img>` element. Escaping the leading `>` keeps
956
+ * the content inside the comment, where it stays inert.
957
+ */
958
+ function escapeAbruptClosingCommentTag(rawContent) {
959
+ if (rawContent.startsWith('>')) {
960
+ return '&gt;' + rawContent.slice(1);
961
+ }
962
+ if (rawContent.startsWith('->')) {
963
+ return '-&gt;' + rawContent.slice(2);
964
+ }
965
+ return rawContent; // fast path
966
+ }
967
+
941
968
  /**
942
969
  * Escapes closing comment tag in a comment content.
943
970
  *
944
971
  * For example, given `#comment('-->')`, the content of a comment would be
945
972
  * updated to `--&gt;` to avoid unexpected and unsafe behavior after
946
- * de-serialization.
973
+ * de-serialization. Content that abruptly closes an empty comment is
974
+ * escaped as well, see `escapeAbruptClosingCommentTag()`.
947
975
  */
948
976
  function escapeClosingCommentTag(rawContent) {
949
- if (!CLOSING_COMMENT_REGEXP.test(rawContent)) {
950
- return rawContent; // fast path
977
+ const content = escapeAbruptClosingCommentTag(rawContent);
978
+ if (!CLOSING_COMMENT_REGEXP.test(content)) {
979
+ return content; // fast path
951
980
  }
952
- return rawContent.replace(/(--\!?)>/g, '$1&gt;');
981
+ return content.replace(/(--\!?)>/g, '$1&gt;');
953
982
  }
954
983
 
955
984
  /**
@@ -1024,7 +1053,13 @@ function requireNodeUtils () {
1024
1053
  s += '<!--' + commentData + '-->';
1025
1054
  break;
1026
1055
  case 7: //PROCESSING_INSTRUCTION_NODE
1027
- const content = escapeProcessingInstructionContent(kid.data);
1056
+ let content = escapeProcessingInstructionContent(kid.data);
1057
+ if (content.includes('</')) {
1058
+ const fallbackTags = fallbackRawContentTags(parent);
1059
+ for (const fallbackTag of fallbackTags) {
1060
+ content = escapeMatchingClosingTag(content, fallbackTag);
1061
+ }
1062
+ }
1028
1063
  s += '<?' + kid.target + ' ' + content + '?>';
1029
1064
  break;
1030
1065
  case 10: //DOCUMENT_TYPE_NODE
@@ -8141,10 +8176,21 @@ function requireHtmlelts () {
8141
8176
  ctor: function HTMLTemplateElement(doc, localName, prefix) {
8142
8177
  HTMLElement.call(this, doc, localName, prefix);
8143
8178
  this._contentFragment = doc._templateDoc.createDocumentFragment();
8179
+ this._contentFragment._host = this;
8144
8180
  },
8145
8181
  props: {
8146
8182
  content: { get: function() { return this._contentFragment; } },
8147
- serialize: { value: function() { return this.content.serialize(); } }
8183
+ serialize: { value: function() { return this.content.serialize(); } },
8184
+ cloneNode: {
8185
+ value: function(deep) {
8186
+ var clone = HTMLElement.prototype.cloneNode.call(this, deep);
8187
+ if (deep) {
8188
+ clone._contentFragment = this._contentFragment.cloneNode(true);
8189
+ clone._contentFragment._host = clone;
8190
+ }
8191
+ return clone;
8192
+ }
8193
+ }
8148
8194
  }
8149
8195
  });
8150
8196