1f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# encoding: ASCII-8BIT
2f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
3f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# iExploder - Generates bad HTML files to perform QA for web browsers.
4f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
5f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Copyright 2010 Thomas Stromberg - All Rights Reserved.
6f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
7f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Licensed under the Apache License, Version 2.0 (the "License");
8f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# you may not use this file except in compliance with the License.
9f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# You may obtain a copy of the License at
10f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
11f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#      http://www.apache.org/licenses/LICENSE-2.0
12f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
13f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Unless required by applicable law or agreed to in writing, software
14f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# distributed under the License is distributed on an "AS IS" BASIS,
15f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# See the License for the specific language governing permissions and
17f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# limitations under the License.
18f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
19f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochrequire 'cgi'
20f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochrequire 'yaml'
21f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
22f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochrequire './scanner.rb'
23f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochrequire './version.rb'
24f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
25f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Used to speed up subtest generation
26f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch$TEST_CACHE = {}
27f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
28f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Media extensions to proper mime type map (not that we always listen'
29f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch$MIME_MAP = {
30f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'bmp' => 'image/bmp',
31f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'gif' => 'image/gif',
32f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'jpg' => 'image/jpeg',
33f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'png' => 'image/png',
34f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'svg' => 'image/svg+xml',
35f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'tiff' => 'image/tiff',
36f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'xbm' => 'image/xbm',
37f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'ico' => 'image/x-icon',
38f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'jng' => 'image/x-jng',
39f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'xpm' => 'image/x-portable-pixmap',
40f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'ogg' => 'audio/ogg',
41f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'snd' => 'audio/basic',
42f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  'wav' => 'audio/wav'
43f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch}
44f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
45f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# These tags get src properties more often than others
46f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch$SRC_TAGS = ['img', 'audio', 'video', 'embed']
47f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
48f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochclass IExploder
49f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  attr_accessor :test_num, :subtest_data, :lookup_mode, :random_mode, :cgi_url, :browser, :claimed_browser
50f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  attr_accessor :offset, :lines, :stop_num, :config
51f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
52f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def initialize(config_path)
53f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @config = YAML::load(File.open(config_path))
54f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @stop_num = nil
55f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @subtest_data = nil
56f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @test_num = 0
57f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @cgi_url = '/iexploder.cgi'
58f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @browser = 'UNKNOWN'
59f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @claimed_browser = nil
60f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    readTagFiles()
61f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return nil
62f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
63f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
64f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def setRandomSeed
65f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @test_num > 0
66f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      srand(@test_num)
67f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
68f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      srand
69f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
70f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
71f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
72f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
73f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def readTagFiles
74f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # These if statements are so that mod_ruby doesn't have to reload the files
75f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # each time
76f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    data_path = @config['mangle_data_path']
77f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @cssTags = readTagsDir("#{data_path}/css-properties")
78f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @cssPseudoTags = readTagsDir("#{data_path}/css-pseudo")
79f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @cssAtRules = readTagsDir("#{data_path}/css-atrules")
80f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @htmlTags = readTagsDir("#{data_path}/html-tags")
81f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @htmlAttr = readTagsDir("#{data_path}/html-attrs")
82f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @htmlValues = readTagsDir("#{data_path}/html-values")
83f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @cssValues = readTagsDir("#{data_path}/css-values")
84f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @headerValues = readTagsDir("#{data_path}/headers")
85f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @protocolValues = readTagsDir("#{data_path}/protocols")
86f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @mimeTypes = readTagsDir("#{data_path}/mime-types")
87f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    @media = readMediaDir("#{data_path}/media")
88f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
89f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
90f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def readTagsDir(directory)
91f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    values = []
92f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    Dir.foreach(directory) { |filename|
93f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if File.file?(directory + "/" + filename)
94f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        values = values + readTagFile(directory + "/" + filename)
95f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
96f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
97f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return values.uniq
98f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
99f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
100f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def readMediaDir(directory)
101f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    data = {}
102f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    Dir.foreach(directory) { |filename|
103f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if File.file?(directory + "/" + filename)
104f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch       (base, extension) = filename.split('.')
105f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        mime_type = $MIME_MAP[extension]
106f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        data[mime_type] = File.read(directory + "/" + filename)
107f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
108f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
109f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return data
110f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
111f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
112f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def readTagFile(filename)
113f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    list = Array.new
114f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    File.new(filename).readlines.each { |line|
115f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      line.chop!
116f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
117f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # Don't include comments.
118f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if (line !~ /^# /) && (line.length > 0)
119f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        list << line
120f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
121f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
122f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return list
123f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
124f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
125f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
126f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateHtmlValue(tag)
127f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
128f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    tag = tag.sub('EXCLUDED_', '')
129f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if tag =~ /^on/ and choice < 90
130f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateHtmlValue('') + "()"
131f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    elsif tag == 'src' or tag == 'data' or tag == 'profile' and choice < 90
132f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateGarbageUrl(tag)
133f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
134f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
135f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case choice
136f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..50 then
137f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return @htmlValues[rand(@htmlValues.length)]
138f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 51..75
139f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return generateGarbageNumber()
140f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 76..85
141f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return generateGarbageValue()
142f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 86..90
143f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return generateGarbageNumber() + ',' + generateGarbageNumber()
144f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 91..98
145f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return generateGarbageUrl(tag)
146f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
147f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
148f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
149f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
150f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
151f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateMediaUrl(tag)
152f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    mime_type = @media.keys[rand(@media.keys.length)]
153f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return generateTestUrl(@test_num, nil, nil, mime_type)
154f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
155f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
156f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateGarbageUrl(tag)
157f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
158f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case choice
159f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..30
160f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateMediaUrl(tag)
161f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 31..50
162f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return @protocolValues[rand(@protocolValues.length)] + '%' + generateGarbageValue()
163f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 51..60
164f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return @protocolValues[rand(@protocolValues.length)] + '//../' + generateGarbageValue()
165f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 60..75
166f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return @protocolValues[rand(@protocolValues.length)] + '//' + generateGarbageValue()
167f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 75..85
168f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow() + ":" + generateGarbageValue()
169f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 86..97
170f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateGarbageValue() + ":" + generateOverflow()
171f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
172f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
173f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
174f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
175f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
176f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateCssValue(property)
177f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    size_types = ['', 'em', 'px', '%', 'pt', 'pc', 'ex', 'in', 'cm', 'mm']
178f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
179f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
180f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case choice
181f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..50 then
182f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # return the most likely scenario
183f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      case property.sub('EXCLUDED_', '')
184f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when /-image|content/
185f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          return 'url(' + generateGarbageUrl(property) + ')'
186f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when /-width|-radius|-spacing|margin|padding|height/
187f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          return generateGarbageValue() + size_types[rand(size_types.length)]
188f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when /-color/
189f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          return generateGarbageColor()
190f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when /-delay|-duration/
191f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          return generateGarbageValue() + 'ms'
192f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
193f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        return @cssValues[rand(@cssValues.length)]
194f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
195f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 51..75 then return generateGarbageNumber()
196f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 76..85 then return 'url(' + generateGarbageUrl(property) + ')'
197f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 85..98 then return generateGarbageValue()
198f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
199f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
200f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
201f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
202f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
203f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateGarbageColor()
204f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case rand(100)
205f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..50 then return '#' + generateGarbageValue()
206f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 51..70 then return 'rgb(' + generateGarbageNumber() + ',' + generateGarbageNumber() + ',' + generateGarbageNumber() + ')'
207f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 71..98 then return 'rgb(' + generateGarbageNumber() + '%,' + generateGarbageNumber() + '%,' + generateGarbageNumber() + '%)'
208f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
209f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
210f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
211f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
212f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
213f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateGarbageNumber()
214f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
215f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case choice
216f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0 then return '0'
217f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 1..40 then return '9' * rand(100)
218f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 41..60 then return '999999.' + rand(999999999999999999999).to_s
219f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 61..80 then return '-' + ('9' * rand(100))
220f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 81..90 then return '-999999.' + rand(999999999999999999999).to_s
221f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 91..98 then return generateGarbageText()
222f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
223f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
224f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
225f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
226f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
227f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateGarbageValue()
228f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case rand(100)
229f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..30 then return rand(255).chr * rand(@config['buffer_overflow_length'])
230f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 31..50 then return "%n" * 50
231f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 51..65 then return ("&#" + rand(999999).to_s + ";") * rand(@config['max_garbage_text_size'])
232f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 66..70 then
233f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      junk = []
234f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0.upto(rand(20)+1) do
235f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        junk << "\\x" + rand(65535).to_s(16)
236f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
237f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return junk.join('') * rand(@config['max_garbage_text_size'])
238f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 71..99 then
239f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      junk = []
240f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      chars = '%?!$#^0123456789ABCDEF%#./\&|;'
241f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0.upto(rand(20)+1) do
242f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        junk << chars[rand(chars.length)].chr
243f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
244f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return junk.join('') * rand(@config['max_garbage_text_size'])
245f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
246f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
247f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
248f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateOverflow()
249f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return rand(255).chr * (@config['buffer_overflow_length'] + rand(500))
250f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
251f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
252f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateGarbageText
253f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case rand(100)
254f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..70 then return 'X' * 129
255f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 71..75 then return "%n" * 15
256f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 76..85 then return ("&#" + rand(9999999999999).to_s + ";") * rand(@config['max_garbage_text_size'])
257f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 86..90 then return generateGarbageValue()
258f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 91..98 then return rand(255).chr * rand(@config['max_garbage_text_size'])
259f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
260f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return generateOverflow()
261f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
262f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
263f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
264f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def isPropertyInBlacklist(properties)
265f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Format: [img, src] or [img, style, property]
266f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    blacklist_entries = []
267f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @config.has_key?('exclude') and @config['exclude']
268f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      blacklist_entries << properties.join('.')
269f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      wildcard_property = properties.dup
270f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      wildcard_property[0] = '*'
271f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      blacklist_entries << wildcard_property.join('.')
272f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      blacklist_entries.each do |entry|
273f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if @config['exclude'].has_key?(entry) and @browser =~ /#{@config['exclude'][entry]}/
274f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          return true
275f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
276f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
277f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
278f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return false
279f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
280f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
281f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateCssStyling(tag)
282f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    out = ' style="'
283f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    0.upto(rand(@config['properties_per_style_max'])) {
284f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      property = @cssTags[rand(@cssTags.length)]
285f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if isPropertyInBlacklist([tag, 'style', property])
286f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        property = "EXCLUDED_#{property}"
287f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
288f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << property
289f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
290f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # very small chance we let the tag run on.
291f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(65) > 1
292f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << ": "
293f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
294f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
295f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      values = []
296f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0.upto(rand(@config['attributes_per_style_property_max'])) {
297f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        values << generateCssValue(property)
298f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      }
299f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << values.join(' ')
300f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # we almost always put the ; there.
301f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(65) > 1
302f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << ";\n    "
303f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
304f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
305f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    out << "\""
306f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return out
307f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
308f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
309f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def mangleTag(tag, no_close_chance=false)
310f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if not no_close_chance and rand(100) < 15
311f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return "</" + tag + ">"
312f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
313f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    out = "<" + tag
314f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) > 1
315f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << ' '
316f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
317f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << generateOverflow()
318f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
319f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
320f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    attrNum = rand(@config['attributes_per_html_tag_max']) + 1
321f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    attrs = []
322f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # The HTML head tag does not have many useful attributes, but is always included in tests.
323f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if tag == 'head' and rand(100) < 75
324f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      case rand(3)
325f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 0 then attrs << 'lang'
326f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 1 then attrs << 'dir'
327f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 2 then attrs << 'profile'
328f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
329f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
330f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # 75% of the time, these tags get a src attribute
331f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if $SRC_TAGS.include?(tag) and rand(100) < 75
332f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if @config.has_key?('exclude') and @config['exclude'] and @config['exclude'].has_key?("#{tag}.src")
333f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        attrs << 'EXCLUDED_src'
334f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
335f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        attrs << 'src'
336f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
337f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
338f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
339f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    while attrs.length < attrNum
340f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      attribute = @htmlAttr[rand(@htmlAttr.length)]
341f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if isPropertyInBlacklist([tag, attribute])
342f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        attribute = "EXCLUDED_#{attribute}"
343f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
344f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      attrs << attribute
345f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
346f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
347f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Add a few HTML attributes
348f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    for attr in attrs
349f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << attr
350f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) > 1
351f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << '='
352f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
353f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if (rand(100) >= 50)
354f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        quoted = 1
355f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << "\""
356f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
357f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        quoted = nil
358f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
359f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << generateHtmlValue(attr)
360f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if quoted
361f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) >= 10
362f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          out << "\""
363f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
364f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
365f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) >= 1
366f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << "\n  "
367f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
368f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
369f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
370f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) >= 25
371f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << generateCssStyling(tag)
372f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
373f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    out << ">\n"
374f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return out
375f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
376f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
377f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def nextTestNum()
378f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @subtest_data
379f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return @test_num
380f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    elsif @random_mode
381f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return rand(99999999999)
382f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
383f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return @test_num  + 1
384f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
385f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
386f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
387f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateCssPattern()
388f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Generate a CSS selector pattern.
389f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
390f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    pattern = ''
391f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    case choice
392f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 0..84 then pattern = @htmlTags[rand(@htmlTags.length)].dup
393f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 85..89 then pattern = "*"
394f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 90..94 then pattern = @cssAtRules[rand(@cssAtRules.length)].dup
395f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      when 95..100 then pattern = ''
396f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
397f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
398f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 25
399f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << " " + @htmlTags[rand(@htmlTags.length)]
400f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
401f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
402f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 25
403f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << " > " + @htmlTags[rand(@htmlTags.length)]
404f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
405f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
406f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 25
407f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << " + " + @htmlTags[rand(@htmlTags.length)]
408f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
409f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
410f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 10
411f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << "*"
412f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
413f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
414f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
415f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 25
416f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pseudo = @cssPseudoTags[rand(@cssPseudoTags.length)].dup
417f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # These tags typically have a parenthesis
418f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if (pseudo =~ /^lang|^nth|^not/ and rand(100) < 75 and pseudo !~ /\(/) or rand(100) < 20
419f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        pseudo << '('
420f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
421f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
422f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if pseudo =~ /\(/
423f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) < 75
424f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          pseudo << generateGarbageValue()
425f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
426f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) < 75
427f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          pseudo << ')'
428f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
429f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
430f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << ":" + pseudo
431f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
432f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
433f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 20
434f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      html_attr = @htmlAttr[rand(@htmlAttr.length)]
435f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      match = '[' + html_attr
436f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      choice = rand(100)
437f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      garbage = generateGarbageValue()
438f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      case choice
439f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 0..25 then match << ']'
440f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 26..50 then match << "=\"#{garbage}\"]"
441f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 51..75 then match << "=~\"#{garbage}\"]"
442f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        when 76..99 then match << "|=\"#{garbage}\"]"
443f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
444f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << match
445f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
446f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
447f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 20
448f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) < 50
449f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        pattern << '.' + generateGarbageValue()
450f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
451f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        pattern << '.*'
452f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
453f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
454f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
455f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 20
456f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << '#' + generateGarbageValue()
457f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
458f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
459f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 5
460f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      pattern << ' #' + generateGarbageValue()
461f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
462f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
463f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return pattern
464f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
465f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
466f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildStyleTag()
467f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    out = "\n"
468f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    0.upto(rand(@config['properties_per_style_max'])) {
469f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      out << generateCssPattern()
470f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) < 90
471f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << " {\n"
472f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
473f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
474f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0.upto(rand(@config['properties_per_style_max'])) {
475f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        property = @cssTags[rand(@cssTags.length)].dup
476f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if isPropertyInBlacklist(['style', 'style', property])
477f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          property = "  EXCLUDED_#{property}"
478f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
479f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << "  #{property}: "
480f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
481f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        values = []
482f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        0.upto(rand(@config['attributes_per_style_property_max'])) {
483f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          values << generateCssValue(property)
484f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        }
485f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << values.join(' ')
486f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) < 95
487f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          out << ";\n"
488f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
489f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      }
490f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) < 90
491f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        out << "\n}\n"
492f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
493f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
494f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
495f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return out
496f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
497f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
498f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
499f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  # Build any malicious javascript here. Fairly naive at the moment.
500f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildJavaScript
501f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    target = @htmlTags[rand(@htmlTags.length)]
502f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    css_property = @cssTags[rand(@cssTags.length)]
503f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    css_property2 = @cssTags[rand(@cssTags.length)]
504f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    html_attr = @htmlAttr[rand(@htmlAttr.length)]
505f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    css_value = generateCssValue(css_property)
506f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    html_value = generateHtmlValue(html_attr)
507f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    html_value2 = generateGarbageNumber()
508f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    mangled = mangleTag(@htmlTags[rand(@htmlTags.length)]);
509f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    mangled2 = mangleTag(@htmlTags[rand(@htmlTags.length)]);
510f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
511f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js = []
512f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "window.onload=function(){"
513f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  var ietarget = document.createElement('#{target}');"
514f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  ietarget.style.#{css_property} = '#{css_value}';"
515f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  ietarget.#{html_attr} = '#{html_value}';"
516f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  document.body.appendChild(ietarget);"
517f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  ietarget.style.#{css_property2} = #{html_value2};"
518f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
519f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  document.write('#{mangled}');"
520f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "  document.write('#{mangled2}');"
521f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    js << "}"
522f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return js.join("\n")
523f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
524f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
525f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildMediaFile(mime_type)
526f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @media.has_key?(mime_type)
527f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      data = @media[mime_type].dup
528f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
529f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      puts "No media found for #{mime_type}"
530f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      data = generateGarbageText()
531f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
532f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
533f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # corrupt it in a subtle way
534f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    choice = rand(100)
535f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if choice > 50
536f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      garbage = generateGarbageValue()
537f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
538f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      garbage = rand(255).chr * rand(8)
539f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
540f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
541f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if "1.9".respond_to?(:encoding)
542f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      garbage.force_encoding('ASCII-8BIT')
543f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      data.force_encoding('ASCII-8BIT')
544f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
545f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
546f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    garbage_start = rand(data.length)
547f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    garbage_end = garbage_start + garbage.length
548f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    data[garbage_start..garbage_end] = garbage
549f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if rand(100) < 15
550f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      data << generateGarbageValue()
551f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
552f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return data
553f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
554f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
555f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  # Parse the subtest data passed in as part of the URL
556f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def parseSubTestData(subtest_data)
557f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Initialize with one line at 0
558f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if not subtest_data or subtest_data.to_i == 0
559f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return [@config['initial_subtest_width'], [0]]
560f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
561f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch     (lines_at_time, offsets_string) = subtest_data.split('_')
562f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    offsets = offsets_string.split(',').map! {|x| x.to_i }
563f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return [lines_at_time.to_i, offsets]
564f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
565f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
566f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def generateTestUrl(test_num, subtest_width=nil, subtest_offsets=nil, mime_type=nil)
567f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    url = @cgi_url + '?'
568f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if subtest_width
569f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if subtest_offsets.length > @config['subtest_combinations_max']
570f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        url << "t=" << test_num.to_s << "&l=test_redirect&z=THE_END"
571f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
572f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        url << "t=" << test_num.to_s << "&s=" << subtest_width.to_s << "_" << subtest_offsets.join(',')
573f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
574f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
575f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      url << "t=" << test_num.to_s
576f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
577f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
578f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @random_mode
579f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      url << "&r=1"
580f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    elsif @stop_num
581f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      url << "&x=" << @stop_num.to_s
582f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
583f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
584f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if mime_type
585f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      url << '&m=' + CGI::escape(mime_type)
586f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
587f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
588f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    url << "&b=" << CGI::escape(@browser)
589f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return url
590f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
591f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
592f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildBodyTags(tag_count)
593f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    tagList = ['body']
594f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # subtract the <body> tag from tag_count.
595f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    1.upto(tag_count-1) { tagList << @htmlTags[rand(@htmlTags.length)] }
596f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
597f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Lean ourselves toward lots of img and src tests
598f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    for tag, percent in @config['favor_html_tags']
599f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) < percent.to_f
600f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        # Don't overwrite the body tag.
601f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tagList[rand(tagList.length-1)+1] = tag
602f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
603f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
604f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
605f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # Now we have our hitlist of tags,lets mangle them.
606f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    mangled_tags = []
607f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    tagList.each do |tag|
608f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      tag_data = mangleTag(tag)
609f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if tag == 'script'
610f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) < 40
611f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          tag_data = "<script>"
612f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
613f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data << buildJavaScript() + "\n" + "</script>\n"
614f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      elsif tag == 'style'
615f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if rand(100) < 40
616f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          tag_data = "<style>"
617f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
618f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data << buildStyleTag() + "\n" + "</style>\n"
619f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      elsif rand(100) <= 90
620f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data << generateGarbageText() << "\n"
621f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
622f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data << "\n"
623f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
624f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
625f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) <= 33
626f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data << "</#{tag}>\n"
627f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
628f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      mangled_tags << "\n<!-- START #{tag} -->\n" + tag_data + "\n<!-- END #{tag} -->\n"
629f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
630f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return mangled_tags
631f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
632f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
633f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildHeaderTags(tag_count)
634f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    valid_head_tags = ['title', 'base', 'link', 'meta']
635f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    header_tags = ['html', 'head']
636f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    1.upto(tag_count-1) { header_tags << valid_head_tags[rand(valid_head_tags.length)] }
637f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    header_tags << @htmlTags[rand(@htmlTags.length)]
638f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    mangled_tags = []
639f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    header_tags.each do |tag|
640f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      mangled_tags << mangleTag(tag, no_close_chance=true)
641f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
642f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return mangled_tags
643f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
644f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
645f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildSurvivedPage(page_type)
646f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    page = "<html><head>"
647f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    page << "<body>Bummer. You survived both redirects. Let me go sulk in the corner.</body>"
648f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    page << "</html>"
649f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return page
650f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
651f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
652f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildRedirect(test_num, subtest_data, lookup_mode, stop_num=nil)
653f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # no more redirects.
654f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if lookup_mode == '1' or stop_num == test_num
655f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return ''
656f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
657f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
658f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if subtest_data
659f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      width, offsets = parseSubTestData(@subtest_data)
660f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
661f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      width, offsets = nil
662f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
663f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
664f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # We still need a redirect, but don't bother generating new data.
665f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if lookup_mode
666f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      redirect_url = generateTestUrl(test_num, width, offsets)
667f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if lookup_mode == 'test_redirect'
668f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        redirect_url << "&l=test_another_redirect"
669f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      elsif lookup_mode == 'test_another_redirect'
670f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        redirect_url << "&l=survived_redirect"
671f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
672f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        redirect_url << "&l=#{lookup_mode}"
673f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
674f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
675f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # This is a normal redirect going on to the next page. If we have subtest, get the next one.
676f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if subtest_data
677f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        width, offsets = combine_combo_creator(@config['html_tags_per_page'], width, offsets)[0..1]
678f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
679f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      redirect_url = generateTestUrl(nextTestNum(), width, offsets)
680f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
681f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
682f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    redirect_code = "\t<META HTTP-EQUIV=\"Refresh\" content=\"0;URL=#{redirect_url}\">\n"
683f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    # use both techniques, because you never know how you might be corrupting yourself.
684f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    redirect_code << "\t<script language=\"javascript\">setTimeout('window.location=\"#{redirect_url}\"', 1000);</script>\n"
685f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return redirect_code
686f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
687f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
688f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildPage()
689f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @lookup_mode == 'survived_redirect'
690f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      return self.buildSurvivedPage(@lookup_mode)
691f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
692f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    tag_count = @config['html_tags_per_page']
693f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
694f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if $TEST_CACHE.has_key?(@test_num)
695f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch     (header_tags, body_tags) = $TEST_CACHE[@test_num]
696f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
697f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      header_tags = buildHeaderTags(3)
698f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      body_tags = buildBodyTags(tag_count - header_tags.length)
699f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
700f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    required_tags = {
701f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0 => 'html',
702f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      1 => 'head',
703f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      header_tags.length => 'body'
704f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    }
705f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
706f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @subtest_data and @subtest_data.length > 0
707f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if not $TEST_CACHE.has_key?(@test_num)
708f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        $TEST_CACHE[@test_num] = [header_tags, body_tags]
709f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
710f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      (width, offsets) = parseSubTestData(@subtest_data)
711f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      lines = combine_combo_creator(tag_count, width, offsets)[2]
712f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      all_tags = header_tags + body_tags
713f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      body_start = header_tags.length
714f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      header_tags = []
715f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      body_tags = []
716f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # <html> and <body> are required, regardless of their existence in the subtest data.
717f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      0.upto(tag_count) do |line_number|
718f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        tag_data = nil
719f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if lines.include?(line_number)
720f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          tag_data = all_tags[line_number]
721f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        elsif required_tags.key?(line_number)
722f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          tag_data = "<" + required_tags[line_number] + ">"
723f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
724f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        if tag_data
725f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          if line_number < body_start
726f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch            header_tags << tag_data
727f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          else
728f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch            body_tags << tag_data
729f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          end
730f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
731f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
732f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      header_tags << "<!-- subtest mode: #{offsets.length} combinations, width: #{width} -->"
733f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
734f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
735f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText = header_tags[0..1].join("\n\t")
736f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << buildRedirect(@test_num, @subtest_data, @lookup_mode, @stop_num)
737f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << "<title>[#{@test_num}:#{@subtest_data}] iExploder #{$VERSION} - #{generateGarbageText()}</title>\n"
738f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if @claimed_browser and @claimed_browser.length > 1
739f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      show_browser = @claimed_browser
740f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
741f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      show_browser = @browser
742f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
743f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << "\n<!-- iExploder #{$VERSION} | test #{@test_num}:#{@subtest_data} at #{Time.now} -->\n"
744f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << "<!-- browser: #{show_browser} -->\n"
745f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << header_tags[2..-1].join("\n\t")
746f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << "\n</head>\n\n"
747f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << body_tags.join("\n")
748f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    htmlText << "</body>\n</html>"
749f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return htmlText
750f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
751f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
752f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  def buildHeaders(mime_type)
753f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    use_headers = []
754f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    banned_headers = []
755f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    response = {'Content-Type' => mime_type}
756f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    0.upto(rand(@config['headers_per_page_max'])) do
757f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      try_header = @headerValues[rand(@headerValues.length)]
758f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if ! banned_headers.include?(try_header.downcase)
759f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        use_headers << try_header
760f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
761f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
762f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    for header in use_headers.uniq
763f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if rand(100) > 75
764f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        response[header] = generateGarbageNumber()
765f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
766f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        response[header] = generateGarbageUrl(header)
767f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
768f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
769f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    return response
770f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
771f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
772f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
773f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
774f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# for testing
775f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochif $0 == __FILE__
776f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  ie = IExploder.new('config.yaml')
777f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  ie.test_num = ARGV[0].to_i || 1
778f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  ie.subtest_data = ARGV[1] || nil
779f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  mime_type = ARGV[2] || nil
780f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  ie.setRandomSeed()
781f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  if not mime_type
782f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    html_output = ie.buildPage()
783f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts html_output
784f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  else
785f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    headers = ie.buildHeaders(mime_type)
786f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    for (key, value) in headers
787f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      puts "#{key}: #{value}"
788f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
789f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "Mime-Type: #{mime_type}"
790f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts ie.buildMediaFile(mime_type)
791f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
792f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
793