• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# iExploder - Generates bad HTML files to perform QA for web browsers.
2# Developed for the Mozilla Foundation.
3#####################
4#
5# Copyright (c) 2006 Thomas Stromberg <thomas%stromberg.org>
6#
7# This software is provided 'as-is', without any express or implied warranty.
8# In no event will the authors be held liable for any damages arising from the
9# use of this software.
10#
11# Permission is granted to anyone to use this software for any purpose,
12# including commercial applications, and to alter it and redistribute it
13# freely, subject to the following restrictions:
14#
15# 1. The origin of this software must not be misrepresented; you must not
16# claim that you wrote the original software. If you use this software in a
17# product, an acknowledgment in the product documentation would be appreciated
18# but is not required.
19#
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22#
23# 3. This notice may not be removed or altered from any source distribution.
24
25$VERSION="1.3.2"
26
27class IExploder
28    attr_accessor :test_num, :subtest_num, :lookup_mode, :random_mode, :url
29    attr_accessor :offset, :lines, :stop_num
30
31    def initialize(max_tags, max_attrs, max_props)
32        @htmlMaxTags = max_tags
33        @htmlMaxAttrs = max_attrs
34        @cssMaxProps = max_props
35        @mangledTagTotal = 0
36        @stop_num = 0
37    end
38
39    def setRandomSeed
40        if @test_num > 0
41            srand(@test_num)
42        else
43            srand
44        end
45    end
46
47
48    def readTagFiles
49        # These if statements are so that mod_ruby doesn't have to reload the files
50        # each time
51
52        if (! @cssTags)
53            @cssTags = readTagFile('cssproperties.in');
54        end
55
56        if (! @htmlTags)
57            @htmlTags = readTagFile('htmltags.in');
58        end
59        if (! @htmlAttr)
60            @htmlAttr = readTagFile('htmlattrs.in');
61        end
62
63        if (! @htmlValues)
64            @htmlValues = readTagFile('htmlvalues.in');
65        end
66
67        if (! @cssValues)
68            @cssValues = readTagFile('cssvalues.in');
69        end
70
71    end
72
73
74    def readTagFile(filename)
75        list = Array.new
76        File.new(filename).readlines.each { |line|
77            line.chop!
78
79            # Don't include comments.
80            if (line !~ /^# /) && (line.length > 0)
81                list << line
82            end
83        }
84        return  list
85    end
86
87    # based on make_up_value, essentially.
88    def inventValue
89        value = rand(19);
90        case value
91        when 1..3 then return (@htmlValues[rand(@htmlValues.length)])
92        when 4..5 then return (@htmlValues[rand(@htmlValues.length)] + inventValue())
93        when 6 then return (@htmlValues[rand(@htmlValues.length)] + "//" + inventValue())
94        when 7 then return ''
95            # this may return negative argument?
96        when 8..10 then return rand(255).chr * (rand(256)+8)
97        when 11 then return rand(255).chr * (rand(2048)+8)
98        when 12 then return "#" + rand(999999).to_s
99        when 13 then return rand(999999).to_s + "%"
100        when 14..15 then return "&" + rand(999999).to_s + ";"
101            # filters
102        when 16 then
103            return inventValue() + "=" + inventValue()
104
105            # this my return undefined method + for nil:NilClass
106        when 17 then return inventValue() + "," + inventValue()
107        else
108            if rand(5) > 3
109                return "-" + rand(999999).to_s
110            else
111                return rand(999999).to_s
112            end
113        end
114    end
115
116    # based on make_up_value, essentially.
117    def inventCssValue(tag)
118        value = rand(23);
119        case value
120        when 1..10 then return @cssValues[rand(@cssValues.length)]
121        when 11 then return ''
122        when 12 then return rand(255).chr * (rand(8192)+8)
123        when 13
124            length = rand(1024) + 8
125            return (rand(255).chr * length) + " " + (rand(255).chr * length) + " " + (rand(255).chr * length)
126        when 14 then return (rand(255).chr * (rand(1024)+3)) + "px"
127        when 15 then return (rand(255).chr * (rand(1024)+3)) + "em"
128        when 16 then return "url(" + inventValue() + ")"
129        when 17..18 then return "#" + rand(999999999).to_s
130        when 19 then return "-" + rand(99999999).to_s
131        else return rand(99999999).to_s;
132        end
133    end
134
135
136    def mangleTag(tag)
137        @mangledTagTotal += 1
138        out = ''
139
140        # 20% chance of closing a tag instead of opening it. This
141        # still counts against @mangledTagTotal, however.
142        if rand(10) > 8
143            out = "</" + tag + ">"
144            return out
145        end
146
147        # we're opening it.
148        out = "<" + tag
149
150        # forgot the space between the tag and the attributes
151        if rand(15) > 1
152            out << ' '
153        end
154
155        attrNum = rand(@htmlMaxAttrs) + 1
156
157        1.upto(attrNum) {
158            attr = @htmlAttr[rand(@htmlAttr.length)]
159
160            out << attr
161
162            # 7.5% of the time we skip the = sign. Don't prefix it
163            # if the attribute ends with a ( however.
164
165
166            if rand(15) > 1
167                out << '='
168            end
169
170            # sometimes quote it, sometimes not. I doubt the importance
171            # of this test, but mangleme-1.2 added it, and adding more
172            # random-ness never hurt anything but time. I'll do it less often.
173            quote = rand(2)
174            if (quote > 1)
175                out << "\""
176            end
177
178            out << inventValue()
179
180            # end the quote when you are done
181            if (quote > 1)
182                out << "\" "
183            end
184
185            # 5% chance we skip the space at the end of the name
186            if rand(20) > 1
187                out << ' '
188            end
189
190        }
191
192        # CSS styles!
193        if rand(4) > 1
194            out << " style=\""
195            1.upto(rand(@cssMaxProps)+1) {
196                out << @cssTags[rand(@cssTags.length)]
197
198                # very small chance we let the tag run on.
199                if rand(50) > 1
200                    out << ": "
201                end
202
203                out << inventCssValue(tag)
204                # we almost always put the ; there.
205                if rand(50) > 1
206                    out << '; '
207                end
208            }
209            out << "\""
210        end
211
212        out << ">\n"
213
214        # support our local troops!
215        if (@subtest_num > 0) && filterSubTest()
216            if tag =~ /html|body|head/
217                return '<' + tag + '>'
218            else
219                return "<x-#@mangledTagTotal>\n"
220            end
221        else
222            return out
223        end
224    end
225    #end
226
227    def filterSubTest()
228        result = 1
229        if (@mangledTagTotal >= @offset) && (@mangledTagTotal < (@offset + @lines))
230            result = nil
231        end
232        return result
233    end
234
235    def nextTestNum()
236        if random_mode
237            n = rand(99999999)
238        else
239            if @test_num
240                n = @test_num  + 1
241            else
242                n = 1
243            end
244        end
245        return n
246    end
247
248    # If we are at line 30 with 8 extra lines, there is no point to try line 31
249    # with 8 lines as well.. skip back to 1 and bump up the line count.
250    def nextSubTestNum()
251        if (@offset + @lines) > @htmlMaxTags
252            nextNum = ((@lines * 2 -1)) * @htmlMaxTags
253        else
254            nextNum = @subtest_num + 1
255        end
256        return nextNum
257    end
258
259
260    def buildPage
261        if (! @test_num) || (@test_num < 1)
262            @test_num = 1
263        end
264        next_num=nextTestNum()
265        @lines = @subtest_num.div(@htmlMaxTags) + 1
266        @offset = @subtest_num.modulo(@htmlMaxTags)
267
268        # building the HTML
269        bodyText = mangleTag('html')
270        bodyText << "\n<head>\n"
271
272        # Only do redirects if lookup=1 has not been specified.
273        if (! @lookup_mode) && (@lines <= @htmlMaxTags) && (@stop_num != @test_num)
274            newpage = @url + "?"
275            if @subtest_num > 0
276                newpage << "test=" << @test_num.to_s << "&subtest=" << nextSubTestNum().to_s
277            else
278                newpage << "test=" << next_num.to_s
279            end
280
281            if @random_mode
282                newpage << "&random=1"
283            end
284
285            if @stop_num > 0
286                newpage << "&stop=" << @stop_num.to_s
287            end
288
289            bodyText << "\t<META HTTP-EQUIV=\"Refresh\" content=\"0;URL=#{newpage}\">\n"
290            # use both techniques, because you never know how you might be corrupting yourself.
291            bodyText << "\t<script language=\"javascript\">setTimeout('window.location=\"#{newpage}\"', 1000);</script>\n"
292        end
293
294        bodyText << "\t" << mangleTag('meta')
295        bodyText << "\t" <<  mangleTag('meta')
296        bodyText << "\t" <<  mangleTag('link')
297
298        bodyText << "\t<title>[#@test_num] iExploder #{$VERSION} - #{inventValue()}</title>\n"
299        bodyText << "</head>\n\n"
300
301        # What tags will we be messing with ######################
302        tagList = [ 'body']
303
304        # we already have 5 tags?
305        1.upto(@htmlMaxTags - 5 ) { tagList << @htmlTags[rand(@htmlTags.length)] }
306
307        tagList.each { |tag|
308            bodyText << mangleTag(tag)
309            bodyText << inventValue() + "\n"
310        }
311        bodyText << "</body>\n</html>"
312    end
313end
314
315
316
317if $0 == __FILE__
318    max=ARGV[0].to_i
319    puts "testing #{max} tags"
320    test = IExploder.new(max, 5, 5)
321    test.readTagFiles()
322    test.test_num=1
323    test.subtest_num=1
324    counter=0
325    test.lines=0
326
327    while test.lines < max
328        test.lines = test.subtest_num.div(max) + 1
329        test.offset = test.subtest_num.modulo(max)
330        test.subtest_num=test.nextSubTestNum
331        counter = counter + 1
332	puts "[#{counter}] subtest #{test.subtest_num} is #{test.lines} lines with #{test.offset} offset"
333    end
334
335    puts "for #{max} tests, you will have #{counter} iterations until #{test.subtest_num}"
336end
337
338