1846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<HTML>
2846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<BODY BGCOLOR="white">
3846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<PRE>
4846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">001</FONT>    // Copyright (c) 2011, Mike Samuel<a name="line.1"></a>
5846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">002</FONT>    // All rights reserved.<a name="line.2"></a>
6846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">003</FONT>    //<a name="line.3"></a>
7846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">004</FONT>    // Redistribution and use in source and binary forms, with or without<a name="line.4"></a>
8846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">005</FONT>    // modification, are permitted provided that the following conditions<a name="line.5"></a>
9846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">006</FONT>    // are met:<a name="line.6"></a>
10846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">007</FONT>    //<a name="line.7"></a>
11846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">008</FONT>    // Redistributions of source code must retain the above copyright<a name="line.8"></a>
12846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">009</FONT>    // notice, this list of conditions and the following disclaimer.<a name="line.9"></a>
13846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">010</FONT>    // Redistributions in binary form must reproduce the above copyright<a name="line.10"></a>
14846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">011</FONT>    // notice, this list of conditions and the following disclaimer in the<a name="line.11"></a>
15846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">012</FONT>    // documentation and/or other materials provided with the distribution.<a name="line.12"></a>
16846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">013</FONT>    // Neither the name of the OWASP nor the names of its contributors may<a name="line.13"></a>
17846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">014</FONT>    // be used to endorse or promote products derived from this software<a name="line.14"></a>
18846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">015</FONT>    // without specific prior written permission.<a name="line.15"></a>
19846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">016</FONT>    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS<a name="line.16"></a>
20846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">017</FONT>    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT<a name="line.17"></a>
21846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">018</FONT>    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS<a name="line.18"></a>
22846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">019</FONT>    // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE<a name="line.19"></a>
23846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">020</FONT>    // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,<a name="line.20"></a>
24846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">021</FONT>    // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,<a name="line.21"></a>
25846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">022</FONT>    // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;<a name="line.22"></a>
26846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">023</FONT>    // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER<a name="line.23"></a>
27846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">024</FONT>    // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT<a name="line.24"></a>
28846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">025</FONT>    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN<a name="line.25"></a>
29846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">026</FONT>    // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<a name="line.26"></a>
30846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">027</FONT>    // POSSIBILITY OF SUCH DAMAGE.<a name="line.27"></a>
31846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">028</FONT>    <a name="line.28"></a>
32846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">029</FONT>    package org.owasp.html;<a name="line.29"></a>
33846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">030</FONT>    <a name="line.30"></a>
34846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">031</FONT>    import java.util.List;<a name="line.31"></a>
35846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">032</FONT>    import java.util.Map;<a name="line.32"></a>
36846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">033</FONT>    import java.util.Set;<a name="line.33"></a>
37846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">034</FONT>    import java.util.regex.Pattern;<a name="line.34"></a>
38846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">035</FONT>    <a name="line.35"></a>
39846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">036</FONT>    import javax.annotation.Nullable;<a name="line.36"></a>
40846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">037</FONT>    import javax.annotation.concurrent.NotThreadSafe;<a name="line.37"></a>
41846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">038</FONT>    <a name="line.38"></a>
423cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">039</FONT>    import com.google.common.base.Predicate;<a name="line.39"></a>
433cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">040</FONT>    import com.google.common.collect.ImmutableList;<a name="line.40"></a>
443cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">041</FONT>    import com.google.common.collect.ImmutableMap;<a name="line.41"></a>
453cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">042</FONT>    import com.google.common.collect.ImmutableSet;<a name="line.42"></a>
463cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">043</FONT>    import com.google.common.collect.Maps;<a name="line.43"></a>
473cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">044</FONT>    import com.google.common.collect.Sets;<a name="line.44"></a>
483cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">045</FONT>    <a name="line.45"></a>
49846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel<FONT color="green">046</FONT>    <a name="line.46"></a>
503cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">047</FONT>    /**<a name="line.47"></a>
513cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">048</FONT>     * Conveniences for configuring policies for the {@link HtmlSanitizer}.<a name="line.48"></a>
523cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">049</FONT>     *<a name="line.49"></a>
533cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">050</FONT>     * &lt;h3&gt;Usage&lt;/h3&gt;<a name="line.50"></a>
543cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">051</FONT>     * &lt;p&gt;<a name="line.51"></a>
553cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">052</FONT>     * To create a policy, first construct an instance of this class; then call<a name="line.52"></a>
563cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">053</FONT>     * &lt;code&gt;allow&amp;hellip;&lt;/code&gt; methods to turn on tags, attributes, and other<a name="line.53"></a>
574793dee33bcf30a77b148028d784003438bb2f1dmikesamuel<FONT color="green">054</FONT>     * processing modes; and finally call &lt;code&gt;build(renderer)&lt;/code&gt; or<a name="line.54"></a>
583cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">055</FONT>     * &lt;code&gt;toFactory()&lt;/code&gt;.<a name="line.55"></a>
593cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">056</FONT>     * &lt;/p&gt;<a name="line.56"></a>
603cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">057</FONT>     * &lt;pre class="prettyprint lang-java"&gt;<a name="line.57"></a>
613cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">058</FONT>     * // Define the policy.<a name="line.58"></a>
622446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">059</FONT>     * Function&amp;lt;HtmlStreamEventReceiver, HtmlSanitizer.Policy&amp;gt; policy<a name="line.59"></a>
633cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">060</FONT>     *     = new HtmlPolicyBuilder()<a name="line.60"></a>
643cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">061</FONT>     *         .allowElements("a", "p")<a name="line.61"></a>
653cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">062</FONT>     *         .allowAttributes("href").onElements("a")<a name="line.62"></a>
663cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">063</FONT>     *         .toFactory();<a name="line.63"></a>
673cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">064</FONT>     *<a name="line.64"></a>
683cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">065</FONT>     * // Sanitize your output.<a name="line.65"></a>
6962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">066</FONT>     * HtmlSanitizer.sanitize(myHtml, policy.apply(myHtmlStreamRenderer));<a name="line.66"></a>
703cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">067</FONT>     * &lt;/pre&gt;<a name="line.67"></a>
713cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">068</FONT>     *<a name="line.68"></a>
723cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">069</FONT>     * &lt;h3&gt;Embedded Content&lt;/h3&gt;<a name="line.69"></a>
733cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">070</FONT>     * &lt;p&gt;<a name="line.70"></a>
743cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">071</FONT>     * Embedded URLs are filtered by<a name="line.71"></a>
753cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">072</FONT>     * {@link HtmlPolicyBuilder#allowUrlProtocols protocol}.<a name="line.72"></a>
763cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">073</FONT>     * There is a {@link HtmlPolicyBuilder#allowStandardUrlProtocols canned policy}<a name="line.73"></a>
773cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">074</FONT>     * so you can easily white-list widely used policies that don't violate the<a name="line.74"></a>
783cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">075</FONT>     * current pages origin.  See "Customization" below for ways to do further<a name="line.75"></a>
793cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">076</FONT>     * filtering.  If you allow links it might be worthwhile to<a name="line.76"></a>
803cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">077</FONT>     * {@link HtmlPolicyBuilder#requireRelNofollowOnLinks() require}<a name="line.77"></a>
813cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">078</FONT>     * {@code rel=nofollow}.<a name="line.78"></a>
823cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">079</FONT>     * &lt;/p&gt;<a name="line.79"></a>
833cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">080</FONT>     * &lt;p&gt;<a name="line.80"></a>
843cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">081</FONT>     * This class simply throws out all embedded JS.<a name="line.81"></a>
853cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">082</FONT>     * Use a custom element or attribute policy to allow through<a name="line.82"></a>
863cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">083</FONT>     * signed or otherwise known-safe code.<a name="line.83"></a>
873cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">084</FONT>     * Check out the Caja project if you need a way to contain third-party JS.<a name="line.84"></a>
883cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">085</FONT>     * &lt;/p&gt;<a name="line.85"></a>
893cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">086</FONT>     * &lt;p&gt;<a name="line.86"></a>
903cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">087</FONT>     * This class does not attempt to faithfully parse and sanitize CSS.<a name="line.87"></a>
913cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">088</FONT>     * It does provide {@link HtmlPolicyBuilder#allowStyling() one} styling option<a name="line.88"></a>
923cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">089</FONT>     * that allows through a few CSS properties that allow textual styling, but that<a name="line.89"></a>
933cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">090</FONT>     * disallow image loading, history stealing, layout breaking, code execution,<a name="line.90"></a>
943cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">091</FONT>     * etc.<a name="line.91"></a>
953cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">092</FONT>     * &lt;/p&gt;<a name="line.92"></a>
963cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">093</FONT>     *<a name="line.93"></a>
973cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">094</FONT>     * &lt;h3&gt;Customization&lt;/h3&gt;<a name="line.94"></a>
983cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">095</FONT>     * &lt;p&gt;<a name="line.95"></a>
993cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">096</FONT>     * You can easily do custom processing on tags and attributes by supplying your<a name="line.96"></a>
1003cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">097</FONT>     * own {@link ElementPolicy element policy} or<a name="line.97"></a>
1013cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">098</FONT>     * {@link AttributePolicy attribute policy} when calling<a name="line.98"></a>
1023cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">099</FONT>     * &lt;code&gt;allow&amp;hellip;&lt;/code&gt;.<a name="line.99"></a>
1033cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">100</FONT>     * E.g. to convert headers into {@code &lt;div&gt;}s, you could use an element policy<a name="line.100"></a>
1043cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">101</FONT>     * &lt;/p&gt;<a name="line.101"></a>
1053cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">102</FONT>     * &lt;pre class="prettyprint lang-java"&gt;<a name="line.102"></a>
1062446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">103</FONT>     * new HtmlPolicyBuilder()<a name="line.103"></a>
1072446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">104</FONT>     *   .allowElement(<a name="line.104"></a>
1082446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">105</FONT>     *     new ElementPolicy() {<a name="line.105"></a>
1092446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">106</FONT>     *       public String apply(String elementName, List&amp;lt;String&gt; attributes) {<a name="line.106"></a>
1102446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">107</FONT>     *         attributes.add("class");<a name="line.107"></a>
1112446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">108</FONT>     *         attributes.add("header-" + elementName);<a name="line.108"></a>
1122446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">109</FONT>     *         return "div";<a name="line.109"></a>
1132446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">110</FONT>     *       }<a name="line.110"></a>
1142446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">111</FONT>     *     },<a name="line.111"></a>
1152446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">112</FONT>     *     "h1", "h2", "h3", "h4", "h5", "h6")<a name="line.112"></a>
1162446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">113</FONT>     *   .build(outputChannel)<a name="line.113"></a>
1173cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">114</FONT>     * &lt;/pre&gt;<a name="line.114"></a>
1183cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">115</FONT>     *<a name="line.115"></a>
1193cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">116</FONT>     * &lt;h3&gt;Rules of Thumb&lt;/h3&gt;<a name="line.116"></a>
1203cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">117</FONT>     * &lt;p&gt;<a name="line.117"></a>
1213cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">118</FONT>     * Throughout this class, several rules hold:<a name="line.118"></a>
1223cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">119</FONT>     * &lt;ul&gt;<a name="line.119"></a>
1233cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">120</FONT>     *   &lt;li&gt;Everything is denied by default.  There are<a name="line.120"></a>
1243cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">121</FONT>     *     &lt;code&gt;disallow&amp;hellip;&lt;/code&gt; methods, but those reverse<a name="line.121"></a>
1253cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">122</FONT>     *     allows instead of rolling back overly permissive defaults.<a name="line.122"></a>
1263cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">123</FONT>     *   &lt;li&gt;The order of allows and disallows does not matter.<a name="line.123"></a>
1273cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">124</FONT>     *     Disallows trump allows whether they occur before or after them.<a name="line.124"></a>
1283cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">125</FONT>     *     The only method that needs to be called in a particular place is<a name="line.125"></a>
1293cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">126</FONT>     *     {@link HtmlPolicyBuilder#build}.<a name="line.126"></a>
1303cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">127</FONT>     *     Allows or disallows after {@code build} is called have no<a name="line.127"></a>
1313cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">128</FONT>     *     effect on the already built policy.<a name="line.128"></a>
1323cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">129</FONT>     *   &lt;li&gt;Element and attribute policies are applied in the following order:<a name="line.129"></a>
1333cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">130</FONT>     *     element specific attribute policy, global attribute policy, element<a name="line.130"></a>
1343cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">131</FONT>     *     policy.<a name="line.131"></a>
1353cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">132</FONT>     *     Element policies come last so they can observe all the post-processed<a name="line.132"></a>
1363cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">133</FONT>     *     attributes, and so they can add attributes that are exempt from<a name="line.133"></a>
1373cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">134</FONT>     *     attribute policies.<a name="line.134"></a>
1383cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">135</FONT>     *     Element specific policies go first, so they can normalize content to<a name="line.135"></a>
1393cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">136</FONT>     *     a form that might be acceptable to a more simplistic global policy.<a name="line.136"></a>
1403cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">137</FONT>     * &lt;/ul&gt;<a name="line.137"></a>
1413cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">138</FONT>     *<a name="line.138"></a>
1423cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">139</FONT>     * &lt;h3&gt;Thread safety and efficiency&lt;/h3&gt;<a name="line.139"></a>
1433cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">140</FONT>     * &lt;p&gt;<a name="line.140"></a>
1443cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">141</FONT>     * This class is not thread-safe.  The resulting policy will not violate its<a name="line.141"></a>
1453cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">142</FONT>     * security guarantees as a result of race conditions, but is not thread safe<a name="line.142"></a>
1463cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">143</FONT>     * because it maintains state to track whether text inside disallowed elements<a name="line.143"></a>
1473cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">144</FONT>     * should be suppressed.<a name="line.144"></a>
1483cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">145</FONT>     * &lt;p&gt;<a name="line.145"></a>
1493cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">146</FONT>     * The resulting policy can be reused, but if you use the<a name="line.146"></a>
1503cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">147</FONT>     * {@link HtmlPolicyBuilder#toFactory()} method instead of {@link #build}, then<a name="line.147"></a>
1513cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">148</FONT>     * binding policies to output channels is cheap so there's no need.<a name="line.148"></a>
1523cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">149</FONT>     * &lt;/p&gt;<a name="line.149"></a>
1533cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">150</FONT>     *<a name="line.150"></a>
1543cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">151</FONT>     * @author Mike Samuel &lt;mikesamuel@gmail.com&gt;<a name="line.151"></a>
1553cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">152</FONT>     */<a name="line.152"></a>
1563cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">153</FONT>    @TCB<a name="line.153"></a>
1573cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">154</FONT>    @NotThreadSafe<a name="line.154"></a>
1583cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">155</FONT>    public class HtmlPolicyBuilder {<a name="line.155"></a>
1593cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">156</FONT>      /**<a name="line.156"></a>
1603cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">157</FONT>       * The default set of elements that are removed if they have no attributes.<a name="line.157"></a>
1613cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">158</FONT>       * Since {@code &lt;img&gt;} is in this set, by default, a policy will remove<a name="line.158"></a>
1623cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">159</FONT>       * {@code &lt;img src=javascript:alert(1337)&gt;} because its URL is not allowed<a name="line.159"></a>
1633cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">160</FONT>       * and it has no other attributes that would warrant it appearing in the<a name="line.160"></a>
1643cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">161</FONT>       * output.<a name="line.161"></a>
1653cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">162</FONT>       */<a name="line.162"></a>
1663cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">163</FONT>      public static final ImmutableSet&lt;String&gt; DEFAULT_SKIP_IF_EMPTY<a name="line.163"></a>
1673cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">164</FONT>          = ImmutableSet.of("a", "font", "img", "input", "span");<a name="line.164"></a>
1683cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">165</FONT>    <a name="line.165"></a>
1693cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">166</FONT>      private final Map&lt;String, ElementPolicy&gt; elPolicies = Maps.newLinkedHashMap();<a name="line.166"></a>
1703cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">167</FONT>      private final Map&lt;String, Map&lt;String, AttributePolicy&gt;&gt; attrPolicies<a name="line.167"></a>
1713cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">168</FONT>          = Maps.newLinkedHashMap();<a name="line.168"></a>
1723cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">169</FONT>      private final Map&lt;String, AttributePolicy&gt; globalAttrPolicies<a name="line.169"></a>
1733cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">170</FONT>          = Maps.newLinkedHashMap();<a name="line.170"></a>
1743cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">171</FONT>      private final Set&lt;String&gt; allowedProtocols = Sets.newLinkedHashSet();<a name="line.171"></a>
1753cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">172</FONT>      private final Set&lt;String&gt; skipIfEmpty = Sets.newLinkedHashSet(<a name="line.172"></a>
1763cf345079f2a4ceaf9e5b4e17b8cd89d90cfe1f6mikesamuel<FONT color="green">173</FONT>          DEFAULT_SKIP_IF_EMPTY);<a name="line.173"></a>
1770455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">174</FONT>      private final Map&lt;String, Boolean&gt; textContainers = Maps.newLinkedHashMap();<a name="line.174"></a>
1782446c27bfba79ac823c77f18e55c644d9d78f572mikesamuel<FONT color="green">175</FONT>      private boolean requireRelNofollowOnLinks;<a name="line.175"></a>
1790455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">176</FONT>    <a name="line.176"></a>
1800455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">177</FONT>      /**<a name="line.177"></a>
1810455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">178</FONT>       * Allows the named elements.<a name="line.178"></a>
1820455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">179</FONT>       */<a name="line.179"></a>
1830455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">180</FONT>      public HtmlPolicyBuilder allowElements(String... elementNames) {<a name="line.180"></a>
1840455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">181</FONT>        return allowElements(ElementPolicy.IDENTITY_ELEMENT_POLICY, elementNames);<a name="line.181"></a>
1850455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">182</FONT>      }<a name="line.182"></a>
1860455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">183</FONT>    <a name="line.183"></a>
1870455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">184</FONT>      /**<a name="line.184"></a>
1880455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">185</FONT>       * Disallows the named elements.  Elements are disallowed by default, so<a name="line.185"></a>
1890455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">186</FONT>       * there is no need to disallow elements, unless you are making an exception<a name="line.186"></a>
1900455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">187</FONT>       * based on an earlier allow.<a name="line.187"></a>
1910455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">188</FONT>       */<a name="line.188"></a>
1920455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">189</FONT>      public HtmlPolicyBuilder disallowElements(String... elementNames) {<a name="line.189"></a>
1930455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">190</FONT>        return allowElements(ElementPolicy.REJECT_ALL_ELEMENT_POLICY, elementNames);<a name="line.190"></a>
1940455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">191</FONT>      }<a name="line.191"></a>
1950455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">192</FONT>    <a name="line.192"></a>
1960455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">193</FONT>      /**<a name="line.193"></a>
1970455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">194</FONT>       * Allow the given elements with the given policy.<a name="line.194"></a>
1980455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">195</FONT>       *<a name="line.195"></a>
1990455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">196</FONT>       * @param policy May remove or add attributes, change the element name, or<a name="line.196"></a>
2000455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">197</FONT>       *    deny the element.<a name="line.197"></a>
2010455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">198</FONT>       */<a name="line.198"></a>
2020455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">199</FONT>      public HtmlPolicyBuilder allowElements(<a name="line.199"></a>
2030455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">200</FONT>          ElementPolicy policy, String... elementNames) {<a name="line.200"></a>
2040455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">201</FONT>        invalidateCompiledState();<a name="line.201"></a>
2050455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">202</FONT>        for (String elementName : elementNames) {<a name="line.202"></a>
2060455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">203</FONT>          elementName = HtmlLexer.canonicalName(elementName);<a name="line.203"></a>
2070455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">204</FONT>          ElementPolicy newPolicy = ElementPolicy.Util.join(<a name="line.204"></a>
2080455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">205</FONT>              elPolicies.get(elementName), policy);<a name="line.205"></a>
2090455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">206</FONT>          // Don't remove if newPolicy is the always reject policy since we want<a name="line.206"></a>
2100455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">207</FONT>          // that to infect later allowElement calls for this particular element<a name="line.207"></a>
2110455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">208</FONT>          // name.  rejects should have higher priority than allows.<a name="line.208"></a>
2120455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">209</FONT>          elPolicies.put(elementName, newPolicy);<a name="line.209"></a>
2130455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">210</FONT>          if (!textContainers.containsKey(elementName)<a name="line.210"></a>
2140455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">211</FONT>              &amp;&amp; TagBalancingHtmlStreamEventReceiver<a name="line.211"></a>
2150455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">212</FONT>                  .allowsPlainTextualContent(elementName)) {<a name="line.212"></a>
2160455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">213</FONT>            textContainers.put(elementName, true);<a name="line.213"></a>
2170455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">214</FONT>          }<a name="line.214"></a>
2180455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">215</FONT>        }<a name="line.215"></a>
2190455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">216</FONT>        return this;<a name="line.216"></a>
2200455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">217</FONT>      }<a name="line.217"></a>
2210455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">218</FONT>    <a name="line.218"></a>
2220455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">219</FONT>      /**<a name="line.219"></a>
2230455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">220</FONT>       * A canned policy that allows a number of common formatting elements.<a name="line.220"></a>
2240455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">221</FONT>       */<a name="line.221"></a>
2250455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">222</FONT>      public HtmlPolicyBuilder allowCommonInlineFormattingElements() {<a name="line.222"></a>
2260455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">223</FONT>        return allowElements(<a name="line.223"></a>
2270455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">224</FONT>            "b", "i", "font", "s", "u", "o", "sup", "sub", "ins", "del", "strong",<a name="line.224"></a>
2280455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">225</FONT>            "strike", "tt", "code", "big", "small", "br", "span");<a name="line.225"></a>
2290455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">226</FONT>      }<a name="line.226"></a>
2300455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">227</FONT>    <a name="line.227"></a>
2310455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">228</FONT>      /**<a name="line.228"></a>
2320455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">229</FONT>       * A canned policy that allows a number of common block elements.<a name="line.229"></a>
2330455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">230</FONT>       */<a name="line.230"></a>
2340455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">231</FONT>      public HtmlPolicyBuilder allowCommonBlockElements() {<a name="line.231"></a>
2350455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">232</FONT>        return allowElements(<a name="line.232"></a>
2360455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">233</FONT>            "p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li",<a name="line.233"></a>
2370455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">234</FONT>            "blockquote");<a name="line.234"></a>
2380455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">235</FONT>      }<a name="line.235"></a>
2390455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">236</FONT>    <a name="line.236"></a>
2400455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">237</FONT>      /**<a name="line.237"></a>
2410455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">238</FONT>       * Allows text content in the named elements.<a name="line.238"></a>
2420455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">239</FONT>       * By default, text content is allowed in any<a name="line.239"></a>
2430455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">240</FONT>       * {@link #allowElements allowed elements} that can contain character data per<a name="line.240"></a>
2440455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">241</FONT>       * the HTML5 spec, but text content is not allowed by default in elements that<a name="line.241"></a>
2450455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">242</FONT>       * contain content of other kinds (like JavaScript in {@code &lt;script&gt;}<a name="line.242"></a>
2460455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">243</FONT>       * elements.<a name="line.243"></a>
2470455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">244</FONT>       * &lt;p&gt;<a name="line.244"></a>
2480455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">245</FONT>       * To write a policy that whitelists {@code &lt;script&gt;} or {@code &lt;style&gt;}<a name="line.245"></a>
2490455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">246</FONT>       * elements, first {@code allowTextIn("script")}.<a name="line.246"></a>
2500455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">247</FONT>       */<a name="line.247"></a>
2510455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">248</FONT>      public HtmlPolicyBuilder allowTextIn(String... elementNames) {<a name="line.248"></a>
2520455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">249</FONT>        invalidateCompiledState();<a name="line.249"></a>
2530455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">250</FONT>        for (String elementName : elementNames) {<a name="line.250"></a>
2540455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">251</FONT>          elementName = HtmlLexer.canonicalName(elementName);<a name="line.251"></a>
2550455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">252</FONT>          textContainers.put(elementName, true);<a name="line.252"></a>
2560455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">253</FONT>        }<a name="line.253"></a>
2570455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">254</FONT>        return this;<a name="line.254"></a>
2580455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">255</FONT>      }<a name="line.255"></a>
2590455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">256</FONT>    <a name="line.256"></a>
2600455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">257</FONT>      public HtmlPolicyBuilder disallowTextIn(String... elementNames) {<a name="line.257"></a>
2610455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">258</FONT>        invalidateCompiledState();<a name="line.258"></a>
2620455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">259</FONT>        for (String elementName : elementNames) {<a name="line.259"></a>
2630455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">260</FONT>          elementName = HtmlLexer.canonicalName(elementName);<a name="line.260"></a>
2640455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">261</FONT>          textContainers.put(elementName, false);<a name="line.261"></a>
2650455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">262</FONT>        }<a name="line.262"></a>
2660455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">263</FONT>        return this;<a name="line.263"></a>
2670455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">264</FONT>      }<a name="line.264"></a>
2680455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">265</FONT>    <a name="line.265"></a>
2690455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">266</FONT>      /**<a name="line.266"></a>
2700455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">267</FONT>       * Assuming the given elements are allowed, allows them to appear without<a name="line.267"></a>
2710455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">268</FONT>       * attributes.<a name="line.268"></a>
2720455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">269</FONT>       *<a name="line.269"></a>
2730455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">270</FONT>       * @see #DEFAULT_SKIP_IF_EMPTY<a name="line.270"></a>
2740455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">271</FONT>       * @see #disallowWithoutAttributes<a name="line.271"></a>
2750455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">272</FONT>       */<a name="line.272"></a>
2760455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">273</FONT>      public HtmlPolicyBuilder allowWithoutAttributes(String... elementNames) {<a name="line.273"></a>
2770455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">274</FONT>        invalidateCompiledState();<a name="line.274"></a>
2780455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">275</FONT>        for (String elementName : elementNames) {<a name="line.275"></a>
2790455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">276</FONT>          elementName = HtmlLexer.canonicalName(elementName);<a name="line.276"></a>
2800455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">277</FONT>          skipIfEmpty.remove(elementName);<a name="line.277"></a>
2810455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">278</FONT>        }<a name="line.278"></a>
2820455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">279</FONT>        return this;<a name="line.279"></a>
2830455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">280</FONT>      }<a name="line.280"></a>
2840455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">281</FONT>    <a name="line.281"></a>
2850455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">282</FONT>      /**<a name="line.282"></a>
2860455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">283</FONT>       * Disallows the given elements from appearing without attributes.<a name="line.283"></a>
2870455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">284</FONT>       *<a name="line.284"></a>
2880455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">285</FONT>       * @see #DEFAULT_SKIP_IF_EMPTY<a name="line.285"></a>
2890455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">286</FONT>       * @see #allowWithoutAttributes<a name="line.286"></a>
2900455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">287</FONT>       */<a name="line.287"></a>
2910455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">288</FONT>      public HtmlPolicyBuilder disallowWithoutAttributes(String... elementNames) {<a name="line.288"></a>
2920455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">289</FONT>        invalidateCompiledState();<a name="line.289"></a>
2930455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">290</FONT>        for (String elementName : elementNames) {<a name="line.290"></a>
2940455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">291</FONT>          elementName = HtmlLexer.canonicalName(elementName);<a name="line.291"></a>
2950455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">292</FONT>          skipIfEmpty.add(elementName);<a name="line.292"></a>
2960455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">293</FONT>        }<a name="line.293"></a>
2970455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">294</FONT>        return this;<a name="line.294"></a>
2980455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">295</FONT>      }<a name="line.295"></a>
2990455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">296</FONT>    <a name="line.296"></a>
3000455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">297</FONT>      /**<a name="line.297"></a>
3010455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">298</FONT>       * Returns an object that lets you associate policies with the given<a name="line.298"></a>
3020455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">299</FONT>       * attributes, and allow them globally or on specific elements.<a name="line.299"></a>
3030455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">300</FONT>       */<a name="line.300"></a>
3040455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">301</FONT>      public AttributeBuilder allowAttributes(String... attributeNames) {<a name="line.301"></a>
3050455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">302</FONT>        ImmutableList.Builder&lt;String&gt; b = ImmutableList.builder();<a name="line.302"></a>
3060455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">303</FONT>        for (String attributeName : attributeNames) {<a name="line.303"></a>
3070455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">304</FONT>          b.add(HtmlLexer.canonicalName(attributeName));<a name="line.304"></a>
3080455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">305</FONT>        }<a name="line.305"></a>
3090455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">306</FONT>        return new AttributeBuilder(b.build());<a name="line.306"></a>
3100455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">307</FONT>      }<a name="line.307"></a>
3110455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">308</FONT>    <a name="line.308"></a>
3120455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">309</FONT>      /**<a name="line.309"></a>
3130455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">310</FONT>       * Reverse an earlier attribute {@link #allowAttributes allow}.<a name="line.310"></a>
3140455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">311</FONT>       * &lt;p&gt;<a name="line.311"></a>
3150455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">312</FONT>       * For this to have an effect you must call at least one of<a name="line.312"></a>
3160455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">313</FONT>       * {@link AttributeBuilder#globally} and {@link AttributeBuilder#onElements}.<a name="line.313"></a>
3170455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">314</FONT>       * &lt;p&gt;<a name="line.314"></a>
3180455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">315</FONT>       * Attributes are disallowed by default, so there is no need to call this<a name="line.315"></a>
3190455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">316</FONT>       * with a laundry list of attribute/element pairs.<a name="line.316"></a>
3200455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">317</FONT>       */<a name="line.317"></a>
3210455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">318</FONT>      public AttributeBuilder disallowAttributes(String... attributeNames) {<a name="line.318"></a>
3220455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">319</FONT>        return this.allowAttributes(attributeNames)<a name="line.319"></a>
3230455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">320</FONT>            .matching(AttributePolicy.REJECT_ALL_ATTRIBUTE_POLICY);<a name="line.320"></a>
3240455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">321</FONT>      }<a name="line.321"></a>
3250455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">322</FONT>    <a name="line.322"></a>
3260455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">323</FONT>    <a name="line.323"></a>
3270455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">324</FONT>      private HtmlPolicyBuilder allowAttributesGlobally(<a name="line.324"></a>
3280455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">325</FONT>          AttributePolicy policy, List&lt;String&gt; attributeNames) {<a name="line.325"></a>
3290455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">326</FONT>        invalidateCompiledState();<a name="line.326"></a>
3300455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">327</FONT>        for (String attributeName : attributeNames) {<a name="line.327"></a>
3310455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">328</FONT>          // We reinterpret the identity policy later via policy joining since its<a name="line.328"></a>
3320455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">329</FONT>          // the default passed from the policy-less method, but we don't do<a name="line.329"></a>
3330455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">330</FONT>          // anything here since we don't know until build() is called whether the<a name="line.330"></a>
3340455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">331</FONT>          // policy author wants to allow certain URL protocols or wants to deal<a name="line.331"></a>
3350455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">332</FONT>          // with styles.<a name="line.332"></a>
3360455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">333</FONT>          AttributePolicy oldPolicy = globalAttrPolicies.get(attributeName);<a name="line.333"></a>
3370455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">334</FONT>          globalAttrPolicies.put(<a name="line.334"></a>
3380455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">335</FONT>              attributeName, AttributePolicy.Util.join(oldPolicy, policy));<a name="line.335"></a>
3390455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">336</FONT>        }<a name="line.336"></a>
3400455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">337</FONT>        return this;<a name="line.337"></a>
3410455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">338</FONT>      }<a name="line.338"></a>
3420455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">339</FONT>    <a name="line.339"></a>
3430455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">340</FONT>      private HtmlPolicyBuilder allowAttributesOnElements(<a name="line.340"></a>
3440455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">341</FONT>          AttributePolicy policy, List&lt;String&gt; attributeNames,<a name="line.341"></a>
3450455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">342</FONT>          List&lt;String&gt; elementNames) {<a name="line.342"></a>
3460455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">343</FONT>        invalidateCompiledState();<a name="line.343"></a>
3470455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">344</FONT>        for (String elementName : elementNames) {<a name="line.344"></a>
3480455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">345</FONT>          Map&lt;String, AttributePolicy&gt; policies = attrPolicies.get(elementName);<a name="line.345"></a>
3490455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">346</FONT>          if (policies == null) {<a name="line.346"></a>
3500455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">347</FONT>            policies = Maps.newLinkedHashMap();<a name="line.347"></a>
3510455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">348</FONT>            attrPolicies.put(elementName, policies);<a name="line.348"></a>
3520455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">349</FONT>          }<a name="line.349"></a>
3530455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">350</FONT>          for (String attributeName : attributeNames) {<a name="line.350"></a>
3540455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">351</FONT>            AttributePolicy oldPolicy = policies.get(attributeName);<a name="line.351"></a>
3550455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">352</FONT>            policies.put(<a name="line.352"></a>
3560455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">353</FONT>                attributeName,<a name="line.353"></a>
3570455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">354</FONT>                AttributePolicy.Util.join(oldPolicy, policy));<a name="line.354"></a>
3580455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">355</FONT>          }<a name="line.355"></a>
3590455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">356</FONT>        }<a name="line.356"></a>
3600455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">357</FONT>        return this;<a name="line.357"></a>
3610455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">358</FONT>      }<a name="line.358"></a>
3620455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">359</FONT>    <a name="line.359"></a>
3630455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">360</FONT>      /**<a name="line.360"></a>
3640455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">361</FONT>       * Adds &lt;a href="http://en.wikipedia.org/wiki/Nofollow"&gt;&lt;code&gt;rel=nofollow&lt;/code&gt;&lt;/a&gt;<a name="line.361"></a>
3650455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">362</FONT>       * to links.<a name="line.362"></a>
3660455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">363</FONT>       */<a name="line.363"></a>
3670455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">364</FONT>      public HtmlPolicyBuilder requireRelNofollowOnLinks() {<a name="line.364"></a>
3680455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">365</FONT>        invalidateCompiledState();<a name="line.365"></a>
3690455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">366</FONT>        this.requireRelNofollowOnLinks = true;<a name="line.366"></a>
3700455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">367</FONT>        return this;<a name="line.367"></a>
3710455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">368</FONT>      }<a name="line.368"></a>
3720455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">369</FONT>    <a name="line.369"></a>
3730455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">370</FONT>      /**<a name="line.370"></a>
3740455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">371</FONT>       * Adds to the set of protocols that are allowed in URL attributes.<a name="line.371"></a>
3750455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">372</FONT>       * For each URL attribute that is allowed, we further constrain it by<a name="line.372"></a>
3760455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">373</FONT>       * only allowing the value through if it specifies no protocol, or if it<a name="line.373"></a>
3770455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">374</FONT>       * specifies one in the allowedProtocols white-list.<a name="line.374"></a>
3780455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">375</FONT>       * This is done regardless of whether any protocols have been allowed, so<a name="line.375"></a>
3790455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">376</FONT>       * allowing the attribute "href" globally with the identity policy but<a name="line.376"></a>
3800455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">377</FONT>       * not white-listing any protocols, effectively disallows the "href"<a name="line.377"></a>
3810455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">378</FONT>       * attribute globally.<a name="line.378"></a>
3820455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">379</FONT>       * &lt;p&gt;<a name="line.379"></a>
3830455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">380</FONT>       * Do not allow any &lt;code&gt;*script&lt;/code&gt; such as &lt;code&gt;javascript&lt;/code&gt;<a name="line.380"></a>
3840455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">381</FONT>       * protocols if you might use this policy with untrusted code.<a name="line.381"></a>
3850455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">382</FONT>       */<a name="line.382"></a>
3860455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">383</FONT>      public HtmlPolicyBuilder allowUrlProtocols(String... protocols) {<a name="line.383"></a>
3870455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">384</FONT>        invalidateCompiledState();<a name="line.384"></a>
3880455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">385</FONT>        // If there is at least one allowed protocol, then allow URLs and<a name="line.385"></a>
3890455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">386</FONT>        // add a filter that checks href and src values.<a name="line.386"></a>
3900455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">387</FONT>    <a name="line.387"></a>
3910455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">388</FONT>        // Do not allow href and srcs through otherwise, and only allow on images<a name="line.388"></a>
3920455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">389</FONT>        // and links.<a name="line.389"></a>
3930455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">390</FONT>        for (String protocol : protocols) {<a name="line.390"></a>
3940455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">391</FONT>          protocol = Strings.toLowerCase(protocol);<a name="line.391"></a>
3950455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">392</FONT>          allowedProtocols.add(protocol);<a name="line.392"></a>
3960455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">393</FONT>        }<a name="line.393"></a>
3970455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">394</FONT>        return this;<a name="line.394"></a>
3980455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">395</FONT>      }<a name="line.395"></a>
3990455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">396</FONT>    <a name="line.396"></a>
4000455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">397</FONT>      /**<a name="line.397"></a>
4010455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">398</FONT>       * Reverses a decision made by {@link #allowUrlProtocols}.<a name="line.398"></a>
4020455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">399</FONT>       */<a name="line.399"></a>
4030455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">400</FONT>      public HtmlPolicyBuilder disallowUrlProtocols(String... protocols) {<a name="line.400"></a>
4040455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">401</FONT>        invalidateCompiledState();<a name="line.401"></a>
4050455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">402</FONT>        for (String protocol : protocols) {<a name="line.402"></a>
4060455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">403</FONT>          protocol = Strings.toLowerCase(protocol);<a name="line.403"></a>
4070455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">404</FONT>          allowedProtocols.remove(protocol);<a name="line.404"></a>
4080455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">405</FONT>        }<a name="line.405"></a>
4090455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">406</FONT>        return this;<a name="line.406"></a>
4100455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">407</FONT>      }<a name="line.407"></a>
4110455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">408</FONT>    <a name="line.408"></a>
4120455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">409</FONT>      /**<a name="line.409"></a>
4130455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">410</FONT>       * A canned URL protocol policy that allows &lt;code&gt;http&lt;/code&gt;,<a name="line.410"></a>
4140455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">411</FONT>       * &lt;code&gt;https&lt;/code&gt;, and &lt;code&gt;mailto&lt;/code&gt;.<a name="line.411"></a>
4150455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">412</FONT>       */<a name="line.412"></a>
4160455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">413</FONT>      public HtmlPolicyBuilder allowStandardUrlProtocols() {<a name="line.413"></a>
4170455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">414</FONT>        return allowUrlProtocols("http", "https", "mailto");<a name="line.414"></a>
4180455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">415</FONT>      }<a name="line.415"></a>
4190455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">416</FONT>    <a name="line.416"></a>
4200455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">417</FONT>      /**<a name="line.417"></a>
42162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">418</FONT>       * Convert &lt;code&gt;style="&amp;lt;CSS&amp;gt;"&lt;/code&gt; to sanitized CSS which allows<a name="line.418"></a>
42262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">419</FONT>       * color, font-size, type-face, and other styling using the default schema;<a name="line.419"></a>
42362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">420</FONT>       * but which does not allow content to escape its clipping context.<a name="line.420"></a>
4240455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">421</FONT>       */<a name="line.421"></a>
4250455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">422</FONT>      public HtmlPolicyBuilder allowStyling() {<a name="line.422"></a>
42662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">423</FONT>        allowStyling(CssSchema.DEFAULT);<a name="line.423"></a>
42762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">424</FONT>        return this;<a name="line.424"></a>
42862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">425</FONT>      }<a name="line.425"></a>
42962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">426</FONT>    <a name="line.426"></a>
43062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">427</FONT>      /**<a name="line.427"></a>
43162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">428</FONT>       * Convert &lt;code&gt;style="&amp;lt;CSS&amp;gt;"&lt;/code&gt; to sanitized CSS which allows<a name="line.428"></a>
43262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">429</FONT>       * color, font-size, type-face, and other styling using the given schema.<a name="line.429"></a>
43362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">430</FONT>       */<a name="line.430"></a>
43462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">431</FONT>      public HtmlPolicyBuilder allowStyling(CssSchema whitelist) {<a name="line.431"></a>
43562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">432</FONT>        invalidateCompiledState();<a name="line.432"></a>
43662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">433</FONT>        allowAttributesGlobally(<a name="line.433"></a>
43762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">434</FONT>            new StylingPolicy(whitelist), ImmutableList.of("style"));<a name="line.434"></a>
43862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">435</FONT>        return this;<a name="line.435"></a>
43962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">436</FONT>      }<a name="line.436"></a>
4400455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">437</FONT>    <a name="line.437"></a>
4410455516fc1900c4f4972a4ceea36eff23aeec998mikesamuel<FONT color="green">438</FONT>      /**<a name="line.438"></a>
44262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">439</FONT>       * Names of attributes from HTML 4 whose values are URLs.<a name="line.439"></a>
44362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">440</FONT>       * Other attributes, e.g. &lt;code&gt;style&lt;/code&gt; may contain URLs even though<a name="line.440"></a>
44462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">441</FONT>       * there values are not URLs.<a name="line.441"></a>
44562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">442</FONT>       */<a name="line.442"></a>
44662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">443</FONT>      private static final Set&lt;String&gt; URL_ATTRIBUTE_NAMES = ImmutableSet.of(<a name="line.443"></a>
44762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">444</FONT>          "action", "archive", "background", "cite", "classid", "codebase", "data",<a name="line.444"></a>
44862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">445</FONT>          "dsync", "formaction", "href", "icon", "longdesc", "manifest", "poster",<a name="line.445"></a>
44946c777f3b673c15229003ec585c8817ab411382dmikesamuel<FONT color="green">446</FONT>          "profile", "src", "srcset", "usemap");<a name="line.446"></a>
45062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">447</FONT>    <a name="line.447"></a>
45162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">448</FONT>      /**<a name="line.448"></a>
45262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">449</FONT>       * Produces a policy based on the allow and disallow calls previously made.<a name="line.449"></a>
45362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">450</FONT>       *<a name="line.450"></a>
45462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">451</FONT>       * @param out receives calls to open only tags allowed by<a name="line.451"></a>
45562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">452</FONT>       *      previous calls to this object.<a name="line.452"></a>
45662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">453</FONT>       *      Typically a {@link HtmlStreamRenderer}.<a name="line.453"></a>
45762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">454</FONT>       */<a name="line.454"></a>
45862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">455</FONT>      public HtmlSanitizer.Policy build(HtmlStreamEventReceiver out) {<a name="line.455"></a>
45962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">456</FONT>        return toFactory().apply(out);<a name="line.456"></a>
46062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">457</FONT>      }<a name="line.457"></a>
46162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">458</FONT>    <a name="line.458"></a>
46262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">459</FONT>      /**<a name="line.459"></a>
46362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">460</FONT>       * Produces a policy based on the allow and disallow calls previously made.<a name="line.460"></a>
46462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">461</FONT>       *<a name="line.461"></a>
46562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">462</FONT>       * @param out receives calls to open only tags allowed by<a name="line.462"></a>
46662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">463</FONT>       *      previous calls to this object.<a name="line.463"></a>
46762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">464</FONT>       *      Typically a {@link HtmlStreamRenderer}.<a name="line.464"></a>
46862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">465</FONT>       * @param listener is notified of dropped tags and attributes so that<a name="line.465"></a>
46962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">466</FONT>       *      intrusion detection systems can be alerted to questionable HTML.<a name="line.466"></a>
47062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">467</FONT>       *      If {@code null} then no notifications are sent.<a name="line.467"></a>
47162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">468</FONT>       * @param context if {@code (listener != null)} then the context value passed<a name="line.468"></a>
47262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">469</FONT>       *      with alerts.  This can be used to let the listener know from which<a name="line.469"></a>
47362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">470</FONT>       *      connection or request the questionable HTML was received.<a name="line.470"></a>
47462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">471</FONT>       */<a name="line.471"></a>
47562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">472</FONT>      public &lt;CTX&gt; HtmlSanitizer.Policy build(<a name="line.472"></a>
47662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">473</FONT>          HtmlStreamEventReceiver out,<a name="line.473"></a>
47762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">474</FONT>          @Nullable HtmlChangeListener&lt;? super CTX&gt; listener,<a name="line.474"></a>
47862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">475</FONT>          @Nullable CTX context) {<a name="line.475"></a>
47962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">476</FONT>        return toFactory().apply(out, listener, context);<a name="line.476"></a>
48062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">477</FONT>      }<a name="line.477"></a>
48162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">478</FONT>    <a name="line.478"></a>
48262805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">479</FONT>      /**<a name="line.479"></a>
48362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">480</FONT>       * Like {@link #build} but can be reused to create many different policies<a name="line.480"></a>
48462805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">481</FONT>       * each backed by a different output channel.<a name="line.481"></a>
48562805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">482</FONT>       */<a name="line.482"></a>
48662805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">483</FONT>      public PolicyFactory toFactory() {<a name="line.483"></a>
48762805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">484</FONT>        ImmutableSet.Builder&lt;String&gt; textContainers = ImmutableSet.builder();<a name="line.484"></a>
48862805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">485</FONT>        for (Map.Entry&lt;String, Boolean&gt; textContainer<a name="line.485"></a>
48962805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">486</FONT>             : this.textContainers.entrySet()) {<a name="line.486"></a>
49062805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">487</FONT>          if (Boolean.TRUE.equals(textContainer.getValue())) {<a name="line.487"></a>
49162805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">488</FONT>            textContainers.add(textContainer.getKey());<a name="line.488"></a>
4922cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">489</FONT>          }<a name="line.489"></a>
49362805f77bb2450bc07567ba9cefaa5f79b8e9671mikesamuel<FONT color="green">490</FONT>        }<a name="line.490"></a>
4942cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">491</FONT>        return new PolicyFactory(compilePolicies(), textContainers.build(),<a name="line.491"></a>
4952cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">492</FONT>                                 ImmutableMap.copyOf(globalAttrPolicies));<a name="line.492"></a>
4962cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">493</FONT>      }<a name="line.493"></a>
4972cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">494</FONT>    <a name="line.494"></a>
4982cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">495</FONT>      // Speed up subsequent builds by caching the compiled policies.<a name="line.495"></a>
4992cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">496</FONT>      private transient ImmutableMap&lt;String, ElementAndAttributePolicies&gt;<a name="line.496"></a>
5002cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">497</FONT>          compiledPolicies;<a name="line.497"></a>
5012cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">498</FONT>    <a name="line.498"></a>
5022cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">499</FONT>      /** Called by mutators to signal that any compiled policy is out-of-date. */<a name="line.499"></a>
5032cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">500</FONT>      private void invalidateCompiledState() {<a name="line.500"></a>
5042cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">501</FONT>        compiledPolicies = null;<a name="line.501"></a>
5052cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">502</FONT>      }<a name="line.502"></a>
5062cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">503</FONT>    <a name="line.503"></a>
5072cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">504</FONT>      private ImmutableMap&lt;String, ElementAndAttributePolicies&gt; compilePolicies() {<a name="line.504"></a>
5082cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">505</FONT>        if (compiledPolicies != null) { return compiledPolicies; }<a name="line.505"></a>
5092cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">506</FONT>    <a name="line.506"></a>
5102cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">507</FONT>        // Copy maps before normalizing in case builder is reused.<a name="line.507"></a>
5112cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">508</FONT>        Map&lt;String, ElementPolicy&gt; elPolicies<a name="line.508"></a>
5122cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">509</FONT>            = Maps.newLinkedHashMap(this.elPolicies);<a name="line.509"></a>
5132cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">510</FONT>        Map&lt;String, Map&lt;String, AttributePolicy&gt;&gt; attrPolicies<a name="line.510"></a>
5142cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">511</FONT>            = Maps.newLinkedHashMap(this.attrPolicies);<a name="line.511"></a>
5152cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">512</FONT>        for (Map.Entry&lt;String, Map&lt;String, AttributePolicy&gt;&gt; e :<a name="line.512"></a>
5162cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">513</FONT>             attrPolicies.entrySet()) {<a name="line.513"></a>
5172cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">514</FONT>          e.setValue(Maps.newLinkedHashMap(e.getValue()));<a name="line.514"></a>
5182cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">515</FONT>        }<a name="line.515"></a>
5192cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">516</FONT>        Map&lt;String, AttributePolicy&gt; globalAttrPolicies<a name="line.516"></a>
5202cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">517</FONT>            = Maps.newLinkedHashMap(this.globalAttrPolicies);<a name="line.517"></a>
5212cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">518</FONT>        Set&lt;String&gt; allowedProtocols = ImmutableSet.copyOf(this.allowedProtocols);<a name="line.518"></a>
5222cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">519</FONT>    <a name="line.519"></a>
5232cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">520</FONT>        // Implement requireRelNofollowOnLinks<a name="line.520"></a>
5242cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">521</FONT>        if (requireRelNofollowOnLinks) {<a name="line.521"></a>
5252cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">522</FONT>          ElementPolicy linkPolicy = elPolicies.get("a");<a name="line.522"></a>
5262cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">523</FONT>          if (linkPolicy == null) {<a name="line.523"></a>
5272cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">524</FONT>            linkPolicy = ElementPolicy.REJECT_ALL_ELEMENT_POLICY;<a name="line.524"></a>
5282cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">525</FONT>          }<a name="line.525"></a>
5292cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">526</FONT>          elPolicies.put(<a name="line.526"></a>
5302cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">527</FONT>              "a",<a name="line.527"></a>
5312cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">528</FONT>              ElementPolicy.Util.join(<a name="line.528"></a>
5322cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">529</FONT>                  linkPolicy,<a name="line.529"></a>
5332cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">530</FONT>                  new ElementPolicy() {<a name="line.530"></a>
5342cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">531</FONT>                    public String apply(String elementName, List&lt;String&gt; attrs) {<a name="line.531"></a>
5352cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">532</FONT>                      for (int i = 0, n = attrs.size(); i &lt; n; i += 2) {<a name="line.532"></a>
5362cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">533</FONT>                        if ("href".equals(attrs.get(i))) {<a name="line.533"></a>
5372cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">534</FONT>                          attrs.add("rel");<a name="line.534"></a>
5382cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">535</FONT>                          attrs.add("nofollow");<a name="line.535"></a>
5392cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">536</FONT>                          break;<a name="line.536"></a>
5402cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">537</FONT>                        }<a name="line.537"></a>
5412cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">538</FONT>                      }<a name="line.538"></a>
5422cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">539</FONT>                      return elementName;<a name="line.539"></a>
5432cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">540</FONT>                    }<a name="line.540"></a>
5442cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">541</FONT>                  }));<a name="line.541"></a>
5452cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">542</FONT>        }<a name="line.542"></a>
5462cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">543</FONT>    <a name="line.543"></a>
5472cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">544</FONT>        // Implement protocol policies.<a name="line.544"></a>
5482cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">545</FONT>        // For each URL attribute that is allowed, we further constrain it by<a name="line.545"></a>
5492cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">546</FONT>        // only allowing the value through if it specifies no protocol, or if it<a name="line.546"></a>
5502cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">547</FONT>        // specifies one in the allowedProtocols white-list.<a name="line.547"></a>
5512cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">548</FONT>        // This is done regardless of whether any protocols have been allowed, so<a name="line.548"></a>
5522cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">549</FONT>        // allowing the attribute "href" globally with the identity policy but<a name="line.549"></a>
5532cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">550</FONT>        // not white-listing any protocols, effectively disallows the "href"<a name="line.550"></a>
5542cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">551</FONT>        // attribute globally.<a name="line.551"></a>
5552cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">552</FONT>        {<a name="line.552"></a>
5562cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">553</FONT>          AttributePolicy urlAttributePolicy;<a name="line.553"></a>
5572cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">554</FONT>          if (allowedProtocols.size() == 3<a name="line.554"></a>
5582cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">555</FONT>              &amp;&amp; allowedProtocols.contains("mailto")<a name="line.555"></a>
5592cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">556</FONT>              &amp;&amp; allowedProtocols.contains("http")<a name="line.556"></a>
5602cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">557</FONT>              &amp;&amp; allowedProtocols.contains("https")) {<a name="line.557"></a>
5612cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">558</FONT>            urlAttributePolicy = StandardUrlAttributePolicy.INSTANCE;<a name="line.558"></a>
5622cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">559</FONT>          } else {<a name="line.559"></a>
5632cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">560</FONT>            urlAttributePolicy = new FilterUrlByProtocolAttributePolicy(<a name="line.560"></a>
5642cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">561</FONT>                allowedProtocols);<a name="line.561"></a>
5652cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">562</FONT>          }<a name="line.562"></a>
5662cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">563</FONT>          Set&lt;String&gt; toGuard = Sets.newLinkedHashSet(URL_ATTRIBUTE_NAMES);<a name="line.563"></a>
5672cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">564</FONT>          for (String urlAttributeName : URL_ATTRIBUTE_NAMES) {<a name="line.564"></a>
5682cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">565</FONT>            if (globalAttrPolicies.containsKey(urlAttributeName)) {<a name="line.565"></a>
5692cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">566</FONT>              toGuard.remove(urlAttributeName);<a name="line.566"></a>
5702cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">567</FONT>              globalAttrPolicies.put(urlAttributeName, AttributePolicy.Util.join(<a name="line.567"></a>
5712cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">568</FONT>                  urlAttributePolicy, globalAttrPolicies.get(urlAttributeName)));<a name="line.568"></a>
5722cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">569</FONT>            }<a name="line.569"></a>
5732cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">570</FONT>          }<a name="line.570"></a>
5742cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">571</FONT>          // Implement guards not implemented on global policies in the per-element<a name="line.571"></a>
5752cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">572</FONT>          // policy maps.<a name="line.572"></a>
5762cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">573</FONT>          for (Map.Entry&lt;String, Map&lt;String, AttributePolicy&gt;&gt; e<a name="line.573"></a>
5772cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">574</FONT>               : attrPolicies.entrySet()) {<a name="line.574"></a>
5782cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">575</FONT>            Map&lt;String, AttributePolicy&gt; policies = e.getValue();<a name="line.575"></a>
5792cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">576</FONT>            for (String urlAttributeName : toGuard) {<a name="line.576"></a>
5802cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">577</FONT>              if (policies.containsKey(urlAttributeName)) {<a name="line.577"></a>
5812cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">578</FONT>                policies.put(urlAttributeName, AttributePolicy.Util.join(<a name="line.578"></a>
5822cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">579</FONT>                    urlAttributePolicy, policies.get(urlAttributeName)));<a name="line.579"></a>
5832cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">580</FONT>              }<a name="line.580"></a>
5842cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">581</FONT>            }<a name="line.581"></a>
5852cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">582</FONT>          }<a name="line.582"></a>
5862cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">583</FONT>        }<a name="line.583"></a>
5872cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">584</FONT>    <a name="line.584"></a>
5882cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">585</FONT>        ImmutableMap.Builder&lt;String, ElementAndAttributePolicies&gt; policiesBuilder<a name="line.585"></a>
5892cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">586</FONT>            = ImmutableMap.builder();<a name="line.586"></a>
5902cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">587</FONT>        for (Map.Entry&lt;String, ElementPolicy&gt; e : elPolicies.entrySet()) {<a name="line.587"></a>
5912cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">588</FONT>          String elementName = e.getKey();<a name="line.588"></a>
5922cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">589</FONT>          ElementPolicy elPolicy = e.getValue();<a name="line.589"></a>
5932cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">590</FONT>          if (ElementPolicy.REJECT_ALL_ELEMENT_POLICY.equals(elPolicy)) {<a name="line.590"></a>
5942cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">591</FONT>            continue;<a name="line.591"></a>
5952cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">592</FONT>          }<a name="line.592"></a>
5962cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">593</FONT>    <a name="line.593"></a>
5972cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">594</FONT>          Map&lt;String, AttributePolicy&gt; elAttrPolicies<a name="line.594"></a>
5982cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">595</FONT>              = attrPolicies.get(elementName);<a name="line.595"></a>
5992cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">596</FONT>          if (elAttrPolicies == null) { elAttrPolicies = ImmutableMap.of(); }<a name="line.596"></a>
6002cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">597</FONT>          ImmutableMap.Builder&lt;String, AttributePolicy&gt; attrs<a name="line.597"></a>
6012cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">598</FONT>              = ImmutableMap.builder();<a name="line.598"></a>
6022cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">599</FONT>          for (Map.Entry&lt;String, AttributePolicy&gt; ape : elAttrPolicies.entrySet()) {<a name="line.599"></a>
6032cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">600</FONT>            String attributeName = ape.getKey();<a name="line.600"></a>
6042cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">601</FONT>            // Handle below so we don't end up putting the same key into the map<a name="line.601"></a>
6052cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">602</FONT>            // twice.  ImmutableMap.Builder hates that.<a name="line.602"></a>
6062cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">603</FONT>            if (globalAttrPolicies.containsKey(attributeName)) { continue; }<a name="line.603"></a>
6072cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">604</FONT>            AttributePolicy policy = ape.getValue();<a name="line.604"></a>
6082cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">605</FONT>            if (!AttributePolicy.REJECT_ALL_ATTRIBUTE_POLICY.equals(policy)) {<a name="line.605"></a>
6092cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">606</FONT>              attrs.put(attributeName, policy);<a name="line.606"></a>
6102cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">607</FONT>            }<a name="line.607"></a>
6112cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">608</FONT>          }<a name="line.608"></a>
6122cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">609</FONT>          for (Map.Entry&lt;String, AttributePolicy&gt; ape<a name="line.609"></a>
6132cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">610</FONT>               : globalAttrPolicies.entrySet()) {<a name="line.610"></a>
6142cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">611</FONT>            String attributeName = ape.getKey();<a name="line.611"></a>
6152cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">612</FONT>            AttributePolicy policy = AttributePolicy.Util.join(<a name="line.612"></a>
6162cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">613</FONT>                elAttrPolicies.get(attributeName), ape.getValue());<a name="line.613"></a>
6172cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">614</FONT>            if (!AttributePolicy.REJECT_ALL_ATTRIBUTE_POLICY.equals(policy)) {<a name="line.614"></a>
6182cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">615</FONT>              attrs.put(attributeName, policy);<a name="line.615"></a>
6192cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">616</FONT>            }<a name="line.616"></a>
6202cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">617</FONT>          }<a name="line.617"></a>
6212cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">618</FONT>    <a name="line.618"></a>
6222cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">619</FONT>          policiesBuilder.put(<a name="line.619"></a>
6232cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">620</FONT>              elementName,<a name="line.620"></a>
6242cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">621</FONT>              new ElementAndAttributePolicies(<a name="line.621"></a>
6252cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">622</FONT>                  elementName,<a name="line.622"></a>
6262cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">623</FONT>                  elPolicy, attrs.build(), skipIfEmpty.contains(elementName)));<a name="line.623"></a>
6272cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">624</FONT>        }<a name="line.624"></a>
6282cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">625</FONT>        return compiledPolicies = policiesBuilder.build();<a name="line.625"></a>
6292cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">626</FONT>      }<a name="line.626"></a>
6302cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">627</FONT>    <a name="line.627"></a>
6312cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">628</FONT>      /**<a name="line.628"></a>
6322cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">629</FONT>       * Builds the relationship between attributes, the values that they may have,<a name="line.629"></a>
6332cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">630</FONT>       * and the elements on which they may appear.<a name="line.630"></a>
6342cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">631</FONT>       *<a name="line.631"></a>
6352cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">632</FONT>       * @author Mike Samuel<a name="line.632"></a>
6362cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">633</FONT>       */<a name="line.633"></a>
6372cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">634</FONT>      public final class AttributeBuilder {<a name="line.634"></a>
6382cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">635</FONT>        private final List&lt;String&gt; attributeNames;<a name="line.635"></a>
6392cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">636</FONT>        private AttributePolicy policy = AttributePolicy.IDENTITY_ATTRIBUTE_POLICY;<a name="line.636"></a>
6402cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">637</FONT>    <a name="line.637"></a>
6412cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">638</FONT>        AttributeBuilder(List&lt;? extends String&gt; attributeNames) {<a name="line.638"></a>
6422cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">639</FONT>          this.attributeNames = ImmutableList.copyOf(attributeNames);<a name="line.639"></a>
6432cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">640</FONT>        }<a name="line.640"></a>
6442cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">641</FONT>    <a name="line.641"></a>
6452cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">642</FONT>        /**<a name="line.642"></a>
6462cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">643</FONT>         * Filters and/or transforms the attribute values<a name="line.643"></a>
6472cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">644</FONT>         * allowed by later {@code allow*} calls.<a name="line.644"></a>
6482cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">645</FONT>         * Multiple calls to {@code matching} are combined so that the policies<a name="line.645"></a>
6492cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">646</FONT>         * receive the value in order, each seeing the value after any<a name="line.646"></a>
6502cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">647</FONT>         * transformation by a previous policy.<a name="line.647"></a>
6512cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">648</FONT>         */<a name="line.648"></a>
6522cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">649</FONT>        public AttributeBuilder matching(AttributePolicy policy) {<a name="line.649"></a>
6532cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">650</FONT>          this.policy = AttributePolicy.Util.join(this.policy, policy);<a name="line.650"></a>
6542cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">651</FONT>          return this;<a name="line.651"></a>
6552cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">652</FONT>        }<a name="line.652"></a>
6562cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">653</FONT>    <a name="line.653"></a>
6572cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">654</FONT>        /**<a name="line.654"></a>
6582cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">655</FONT>         * Restrict the values allowed by later {@code allow*} calls to those<a name="line.655"></a>
6592cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">656</FONT>         * matching the pattern.<a name="line.656"></a>
6602cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">657</FONT>         * Multiple calls to {@code matching} are combined to restrict to the<a name="line.657"></a>
6612cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">658</FONT>         * intersection of possible matched values.<a name="line.658"></a>
6622cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">659</FONT>         */<a name="line.659"></a>
6632cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">660</FONT>        public AttributeBuilder matching(final Pattern pattern) {<a name="line.660"></a>
6642cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">661</FONT>          return matching(new AttributePolicy() {<a name="line.661"></a>
6652cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">662</FONT>            public @Nullable String apply(<a name="line.662"></a>
6662cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">663</FONT>                String elementName, String attributeName, String value) {<a name="line.663"></a>
6672cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">664</FONT>              return pattern.matcher(value).matches() ? value : null;<a name="line.664"></a>
6682cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">665</FONT>            }<a name="line.665"></a>
6692cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">666</FONT>          });<a name="line.666"></a>
6702cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">667</FONT>        }<a name="line.667"></a>
6712cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">668</FONT>    <a name="line.668"></a>
6722cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">669</FONT>        /**<a name="line.669"></a>
6732cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">670</FONT>         * Restrict the values allowed by later {@code allow*} calls to those<a name="line.670"></a>
6742cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">671</FONT>         * matching the given predicate.<a name="line.671"></a>
6752cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">672</FONT>         * Multiple calls to {@code matching} are combined to restrict to the<a name="line.672"></a>
6762cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">673</FONT>         * intersection of possible matched values.<a name="line.673"></a>
6772cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">674</FONT>         */<a name="line.674"></a>
6782cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">675</FONT>        public AttributeBuilder matching(<a name="line.675"></a>
6792cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">676</FONT>            final Predicate&lt;? super String&gt; filter) {<a name="line.676"></a>
6802cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">677</FONT>          return matching(new AttributePolicy() {<a name="line.677"></a>
6812cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">678</FONT>            public @Nullable String apply(<a name="line.678"></a>
6822cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">679</FONT>                String elementName, String attributeName, String value) {<a name="line.679"></a>
6832cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">680</FONT>              return filter.apply(value) ? value : null;<a name="line.680"></a>
6842cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">681</FONT>            }<a name="line.681"></a>
6852cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">682</FONT>          });<a name="line.682"></a>
6862cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">683</FONT>        }<a name="line.683"></a>
6872cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">684</FONT>    <a name="line.684"></a>
6882cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">685</FONT>        /**<a name="line.685"></a>
6892cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">686</FONT>         * Restrict the values allowed by later {@code allow*} calls to those<a name="line.686"></a>
6902cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">687</FONT>         * supplied.<a name="line.687"></a>
6912cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">688</FONT>         * Multiple calls to {@code matching} are combined to restrict to the<a name="line.688"></a>
6922cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">689</FONT>         * intersection of possible matched values.<a name="line.689"></a>
6932cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">690</FONT>         */<a name="line.690"></a>
6942cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">691</FONT>        public AttributeBuilder matching(<a name="line.691"></a>
6952cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">692</FONT>            boolean ignoreCase, String... allowedValues) {<a name="line.692"></a>
6962cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">693</FONT>          return matching(ignoreCase, ImmutableSet.copyOf(allowedValues));<a name="line.693"></a>
6972cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">694</FONT>        }<a name="line.694"></a>
6982cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">695</FONT>    <a name="line.695"></a>
6992cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">696</FONT>        /**<a name="line.696"></a>
7002cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">697</FONT>         * Restrict the values allowed by later {@code allow*} calls to those<a name="line.697"></a>
7012cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">698</FONT>         * supplied.<a name="line.698"></a>
7022cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">699</FONT>         * Multiple calls to {@code matching} are combined to restrict to the<a name="line.699"></a>
7032cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">700</FONT>         * intersection of possible matched values.<a name="line.700"></a>
7042cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">701</FONT>         */<a name="line.701"></a>
7052cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">702</FONT>        public AttributeBuilder matching(<a name="line.702"></a>
7062cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">703</FONT>            final boolean ignoreCase, Set&lt;? extends String&gt; allowedValues) {<a name="line.703"></a>
7072cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">704</FONT>          final ImmutableSet&lt;String&gt; allowed = ImmutableSet.copyOf(allowedValues);<a name="line.704"></a>
7082cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">705</FONT>          return matching(new AttributePolicy() {<a name="line.705"></a>
7092cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">706</FONT>            public @Nullable String apply(<a name="line.706"></a>
7102cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">707</FONT>                String elementName, String attributeName, String value) {<a name="line.707"></a>
7112cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">708</FONT>              if (ignoreCase) { value = Strings.toLowerCase(value); }<a name="line.708"></a>
7122cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">709</FONT>              return allowed.contains(value) ? value : null;<a name="line.709"></a>
7132cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">710</FONT>            }<a name="line.710"></a>
7142cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">711</FONT>          });<a name="line.711"></a>
7152cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">712</FONT>        }<a name="line.712"></a>
7162cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">713</FONT>    <a name="line.713"></a>
7172cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">714</FONT>        /**<a name="line.714"></a>
7182cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">715</FONT>         * Allows the given attributes on any elements but filters the<a name="line.715"></a>
7192cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">716</FONT>         * attributes' values based on previous calls to {@code matching(...)}.<a name="line.716"></a>
7202cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">717</FONT>         * Global attribute policies are applied after element specific policies.<a name="line.717"></a>
7212cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">718</FONT>         * Be careful of using this with attributes like &lt;code&gt;type&lt;/code&gt; which<a name="line.718"></a>
7222cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">719</FONT>         * have different meanings on different attributes.<a name="line.719"></a>
7232cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">720</FONT>         * Also be careful of allowing globally attributes like &lt;code&gt;href&lt;/code&gt;<a name="line.720"></a>
7242cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">721</FONT>         * which can have more far-reaching effects on tags like<a name="line.721"></a>
7252cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">722</FONT>         * &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; than on<a name="line.722"></a>
7262cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">723</FONT>         * &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; because in the former, they have an effect without<a name="line.723"></a>
7272cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">724</FONT>         * user interaction and can change the behavior of the current page.<a name="line.724"></a>
7282cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">725</FONT>         */<a name="line.725"></a>
7292cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">726</FONT>        public HtmlPolicyBuilder globally() {<a name="line.726"></a>
7302cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">727</FONT>          return HtmlPolicyBuilder.this.allowAttributesGlobally(<a name="line.727"></a>
7312cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">728</FONT>              policy, attributeNames);<a name="line.728"></a>
7322cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">729</FONT>        }<a name="line.729"></a>
7332cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">730</FONT>    <a name="line.730"></a>
7342cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">731</FONT>        /**<a name="line.731"></a>
7352cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">732</FONT>         * Allows the named attributes on the given elements but filters the<a name="line.732"></a>
7362cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">733</FONT>         * attributes' values based on previous calls to {@code matching(...)}.<a name="line.733"></a>
7372cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">734</FONT>         */<a name="line.734"></a>
7382cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">735</FONT>        public HtmlPolicyBuilder onElements(String... elementNames) {<a name="line.735"></a>
7392cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">736</FONT>          ImmutableList.Builder&lt;String&gt; b = ImmutableList.builder();<a name="line.736"></a>
7402cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">737</FONT>          for (String elementName : elementNames) {<a name="line.737"></a>
7412cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">738</FONT>            b.add(HtmlLexer.canonicalName(elementName));<a name="line.738"></a>
7422cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">739</FONT>          }<a name="line.739"></a>
7432cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">740</FONT>          return HtmlPolicyBuilder.this.allowAttributesOnElements(<a name="line.740"></a>
7442cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">741</FONT>              policy, attributeNames, b.build());<a name="line.741"></a>
7452cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">742</FONT>        }<a name="line.742"></a>
7462cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">743</FONT>      }<a name="line.743"></a>
7472cf15c456fb69a6f1d12f704dcdf4a0164963fd9mikesamuel<FONT color="green">744</FONT>    }<a name="line.744"></a>
748846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
749846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
750846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
751846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
752846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
753846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
754846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
755846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
756846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
757846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
758846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
759846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
760846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
761846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
762846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
763846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
764846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
765846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
766846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
767846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
768846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
769846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
770846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
771846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
772846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
773846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
774846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
775846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
776846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
777846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
778846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
779846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
780846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
781846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
782846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
783846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
784846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
785846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
786846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
787846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
788846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
789846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
790846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
791846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
792846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
793846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
794846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
795846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
796846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
797846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
798846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
799846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
800846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
801846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
802846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
803846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
804846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
805846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
806846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
807846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel
808846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel</PRE>
809846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel</BODY>
810846d5d0377617bd20ac271a486f07bfe757cc7a2mikesamuel</HTML>
811