|
820 | 820 | parseHTMLToFragment(String(html), fragment, targetDoc); |
821 | 821 | return fragment; |
822 | 822 | } |
| 823 | + let canDirectSetHTML = true; |
| 824 | + let canPolicySetHTML = true; |
| 825 | + let escapeHTMLPolicy; |
| 826 | + let escapeHTMLCreator; |
| 827 | + const MY_POLICY_NAME = 'searchjumper_default'; |
823 | 828 | const SVG_NS = 'http://www.w3.org/2000/svg'; |
824 | 829 | const VOID_TAGS = { |
825 | 830 | area: true, |
|
837 | 842 | track: true, |
838 | 843 | wbr: true |
839 | 844 | }; |
| 845 | + const RAW_TEXT_TAGS = { |
| 846 | + script: true, |
| 847 | + style: true, |
| 848 | + textarea: true, |
| 849 | + title: true, |
| 850 | + xmp: true, |
| 851 | + plaintext: true, |
| 852 | + noscript: true |
| 853 | + }; |
840 | 854 | const HTML_ENTITIES = { |
841 | 855 | amp: '&', |
842 | 856 | lt: '<', |
|
861 | 875 | } |
862 | 876 | function parseHTMLToFragment(html, fragment, doc) { |
863 | 877 | const stack = [fragment]; |
864 | | - const tokenRe = /<!--[\s\S]*?-->|<\/?[a-zA-Z][^>]*>|[^<]+/g; |
| 878 | + const tokenRe = /<!--[\s\S]*?-->|<!doctype[^>]*>|<\/?[a-zA-Z][^>]*>|[^<]+/gi; |
865 | 879 | let match; |
866 | 880 | while ((match = tokenRe.exec(html))) { |
867 | 881 | const token = match[0]; |
|
873 | 887 | if (token.indexOf('<!--') === 0) { |
874 | 888 | continue; |
875 | 889 | } |
| 890 | + if (/^<!doctype/i.test(token)) { |
| 891 | + continue; |
| 892 | + } |
876 | 893 | if (token[1] === '/') { |
877 | 894 | const tag = token.slice(2, -1).trim().toLowerCase(); |
878 | 895 | for (let i = stack.length - 1; i > 0; i--) { |
|
906 | 923 | const selfClosing = token.endsWith('/>'); |
907 | 924 | if (!selfClosing && !VOID_TAGS[tagName]) { |
908 | 925 | stack.push(el); |
| 926 | + if (RAW_TEXT_TAGS[tagName]) { |
| 927 | + const closeRe = new RegExp('<\\/\\s*' + tagName + '\\s*>', 'ig'); |
| 928 | + closeRe.lastIndex = tokenRe.lastIndex; |
| 929 | + const closeMatch = closeRe.exec(html); |
| 930 | + if (closeMatch) { |
| 931 | + const rawText = html.slice(tokenRe.lastIndex, closeMatch.index); |
| 932 | + if (rawText) el.appendChild(doc.createTextNode(rawText)); |
| 933 | + tokenRe.lastIndex = closeMatch.index + closeMatch[0].length; |
| 934 | + stack.pop(); |
| 935 | + } |
| 936 | + } |
909 | 937 | } |
910 | 938 | } |
911 | 939 | } |
| 940 | + function ensureEscapeHTMLPolicy() { |
| 941 | + if (!canPolicySetHTML) return null; |
| 942 | + if (escapeHTMLCreator) return escapeHTMLPolicy; |
| 943 | + const createPolicy = _unsafeWindow && _unsafeWindow.trustedTypes && _unsafeWindow.trustedTypes.createPolicy; |
| 944 | + if (typeof createPolicy !== "function") return (canPolicySetHTML = false, null); |
| 945 | + try { |
| 946 | + escapeHTMLPolicy = createPolicy(MY_POLICY_NAME, { |
| 947 | + createHTML: (string, sink) => string, |
| 948 | + createScriptURL: string => string, |
| 949 | + createScript: string => string |
| 950 | + }); |
| 951 | + } catch (e) {} |
| 952 | + escapeHTMLCreator = escapeHTMLPolicy && escapeHTMLPolicy.createHTML; |
| 953 | + if (!escapeHTMLCreator) canPolicySetHTML = false; |
| 954 | + return escapeHTMLPolicy; |
| 955 | + } |
| 956 | + function tryDirectSetHTML(target, htmlStr) { |
| 957 | + if (!canDirectSetHTML) return false; |
| 958 | + try { |
| 959 | + target.innerHTML = htmlStr; |
| 960 | + return true; |
| 961 | + } catch (e) { |
| 962 | + canDirectSetHTML = false; |
| 963 | + return false; |
| 964 | + } |
| 965 | + } |
| 966 | + function tryPolicySetHTML(target, htmlStr) { |
| 967 | + if (!canPolicySetHTML) return false; |
| 968 | + ensureEscapeHTMLPolicy(); |
| 969 | + if (!escapeHTMLCreator) return false; |
| 970 | + try { |
| 971 | + target.innerHTML = escapeHTMLCreator(htmlStr); |
| 972 | + return true; |
| 973 | + } catch (e) { |
| 974 | + canPolicySetHTML = false; |
| 975 | + return false; |
| 976 | + } |
| 977 | + } |
912 | 978 | function setHTML(target, html, doc) { |
913 | 979 | if (!target) return; |
| 980 | + const htmlStr = html === null || html === undefined ? '' : String(html); |
| 981 | + if (tryDirectSetHTML(target, htmlStr) || tryPolicySetHTML(target, htmlStr)) return; |
914 | 982 | const targetDoc = doc || target.ownerDocument || document; |
915 | | - const fragment = createHTML(html, targetDoc); |
| 983 | + const fragment = createHTML(htmlStr, targetDoc); |
916 | 984 | while (target.firstChild) { |
917 | 985 | target.removeChild(target.firstChild); |
918 | 986 | } |
|
0 commit comments