summaryrefslogtreecommitdiffstats
path: root/chrome/tools
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/tools
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/tools')
-rw-r--r--chrome/tools/README.google4
-rw-r--r--chrome/tools/automated_ui_test_tools/README.txt18
-rw-r--r--chrome/tools/automated_ui_test_tools/auto_ui_test_input_generator.py468
-rw-r--r--chrome/tools/automated_ui_test_tools/possible_actions.txt17
-rw-r--r--chrome/tools/build/win/FILES51
-rw-r--r--chrome/tools/build/win/create_installer_archive.py273
-rw-r--r--chrome/tools/build/win/data_dll.vsprops18
-rw-r--r--chrome/tools/build/win/debugger_disabled.vsprops15
-rw-r--r--chrome/tools/build/win/dependencies.py197
-rw-r--r--chrome/tools/build/win/flattened_html_file.bat14
-rw-r--r--chrome/tools/build/win/flattened_html_file.rules20
-rw-r--r--chrome/tools/build/win/font_file_copy.rules19
-rw-r--r--chrome/tools/build/win/hardlink_failsafe.bat12
-rw-r--r--chrome/tools/build/win/html_inline.py128
-rw-r--r--chrome/tools/build/win/inspector_copy.rules28
-rw-r--r--chrome/tools/build/win/js_engine.vsprops8
-rw-r--r--chrome/tools/build/win/js_engine_impl.vsprops11
-rw-r--r--chrome/tools/build/win/js_engine_impl_kjs.vsprops11
-rw-r--r--chrome/tools/build/win/language_dll.vsprops12
-rwxr-xr-xchrome/tools/build/win/make_zip.sh37
-rw-r--r--chrome/tools/build/win/map_drive.bat30
-rw-r--r--chrome/tools/build/win/precompiled.cc2
-rw-r--r--chrome/tools/build/win/precompiled.h66
-rw-r--r--chrome/tools/build/win/precompiled.vsprops15
-rw-r--r--chrome/tools/build/win/precompiled_wtl.cc2
-rw-r--r--chrome/tools/build/win/precompiled_wtl.h44
-rw-r--r--chrome/tools/build/win/precompiled_wtl.vsprops15
-rw-r--r--chrome/tools/build/win/release.rules38
-rw-r--r--chrome/tools/build/win/reliability_test.vsprops11
-rw-r--r--chrome/tools/build/win/resource_text_file_copy.rules18
-rw-r--r--chrome/tools/build/win/sort_sln.py80
-rw-r--r--chrome/tools/build/win/test_memory_usage.vsprops11
-rw-r--r--chrome/tools/build/win/test_shell_tests.vsprops16
-rw-r--r--chrome/tools/build/win/ui_test.vsprops11
-rw-r--r--chrome/tools/build/win/unit_test.vsprops17
-rw-r--r--chrome/tools/build/win/using_generated_strings.vsprops11
-rw-r--r--chrome/tools/build/win/using_javascriptcore.vsprops11
-rw-r--r--chrome/tools/build/win/version.bat44
-rw-r--r--chrome/tools/build/win/version.rules20
-rw-r--r--chrome/tools/convert_dict/aff_reader.cc301
-rw-r--r--chrome/tools/convert_dict/aff_reader.h131
-rw-r--r--chrome/tools/convert_dict/convert_dict.cc149
-rw-r--r--chrome/tools/convert_dict/convert_dict.vcproj199
-rw-r--r--chrome/tools/convert_dict/dic_reader.cc165
-rw-r--r--chrome/tools/convert_dict/dic_reader.h70
-rw-r--r--chrome/tools/convert_dict/hunspell_reader.cc72
-rw-r--r--chrome/tools/convert_dict/hunspell_reader.h51
-rw-r--r--chrome/tools/crash_service/SConscript99
-rw-r--r--chrome/tools/crash_service/crash_service.cc454
-rw-r--r--chrome/tools/crash_service/crash_service.exe.manifest11
-rw-r--r--chrome/tools/crash_service/crash_service.h138
-rw-r--r--chrome/tools/crash_service/crash_service.vcproj157
-rw-r--r--chrome/tools/crash_service/main.cc84
-rw-r--r--chrome/tools/extract_actions.py124
-rwxr-xr-xchrome/tools/extract_actions.sh9
-rw-r--r--chrome/tools/extract_histograms.py56
-rw-r--r--chrome/tools/history-viz.py240
-rw-r--r--chrome/tools/icudt38.dllbin0 -> 8912896 bytes
-rw-r--r--chrome/tools/inconsistent-eol.py130
-rw-r--r--chrome/tools/optipng.exebin0 -> 81920 bytes
-rw-r--r--chrome/tools/profiles/generate_profile.cc238
-rw-r--r--chrome/tools/profiles/generate_profile.vcproj161
-rw-r--r--chrome/tools/profiles/thumbnail-inl.h807
-rw-r--r--chrome/tools/sqlite.exebin0 -> 1000448 bytes
-rw-r--r--chrome/tools/sqlite3_analyzer.exebin0 -> 1233920 bytes
-rw-r--r--chrome/tools/test/generate_mime_tests.pl270
-rw-r--r--chrome/tools/test/image_diff/SConscript106
-rw-r--r--chrome/tools/test/image_diff/image_diff.cc291
-rw-r--r--chrome/tools/test/image_diff/image_diff.vcproj49
-rw-r--r--chrome/tools/test/smoketests.py263
70 files changed, 6648 insertions, 0 deletions
diff --git a/chrome/tools/README.google b/chrome/tools/README.google
new file mode 100644
index 0000000..1c8057a
--- /dev/null
+++ b/chrome/tools/README.google
@@ -0,0 +1,4 @@
+optipng.exe: v.0.5.5, under zlib/libpng license. Sources available at http://optipng.sourceforge.net/
+
+Sample use: (while in trunk/)
+ for /r %a in (*.png) do @chrome\tools\optipng -o7 %a
diff --git a/chrome/tools/automated_ui_test_tools/README.txt b/chrome/tools/automated_ui_test_tools/README.txt
new file mode 100644
index 0000000..22ff797
--- /dev/null
+++ b/chrome/tools/automated_ui_test_tools/README.txt
@@ -0,0 +1,18 @@
+auto_ui_test_input_generator.py takes in a list of possible actions separated by new lines,
+the number of commands per file, and the number of actions per command, and generate either
+a single random file or a number of files containing all possible commands. This file is then
+used as input to automated_ui_tests.exe (see chrome/test/automated_ui_tests/automated_ui_tests.cc/h)
+which will run the commands, reporting on the success or failure of each.
+
+An example of typical use:
+
+$ python auto_ui_test_input_generator.py --action-list-file="possible_actions.txt" --output="automated_ui_tests.txt" --commands-per-file 100 --actions-per-commands 5
+
+$ automated_ui_tests.exe --input="automated_ui_tests.txt" --output="automated_ui_test_report.txt"
+
+
+This will generate a random sequence of 100 commands containing 5 actions each and write it, formatted in XML,
+to automated_ui_tests.txt. Then automated_ui_tests.exe reads that file, runs the commands, and outputs an XML
+file to automated_ui_test_report.txt.
+
+In the future we can write a script to parse automated_ui_test_report for failures and warnings. \ No newline at end of file
diff --git a/chrome/tools/automated_ui_test_tools/auto_ui_test_input_generator.py b/chrome/tools/automated_ui_test_tools/auto_ui_test_input_generator.py
new file mode 100644
index 0000000..7756028
--- /dev/null
+++ b/chrome/tools/automated_ui_test_tools/auto_ui_test_input_generator.py
@@ -0,0 +1,468 @@
+#!/usr/bin/python
+#
+# Copyright 2008 Google Inc. All Rights Reserved.
+
+"""This generates the input file for automated_ui_tests.exe.
+
+We take in a list of possible actions separated by new lines, the number of
+commands per file, and the number of actions per command, and generate
+a single random file, or one or more files containing all possible commands,
+or one file with a partial set of possible commands starting at a certain
+command number.
+
+Example usage:
+ python auto_ui_test_input_generator.py --commands-per-file 50
+ --actions-per-command 5 --action-list-file possible_actions.txt
+ --output random_file.txt
+
+ Generates a file called random_file.txt with 50 commands containing 5 actions
+ each randomly chosen from the list of new line separated actions in
+ possible_actions.txt
+
+ python auto_ui_test_input_generator.py --commands-per-file 200
+ --actions-per-command 6 --partial-coverage-from 700
+ --action-list-file possible_actions.txt
+ --output partial_coverage.txt
+
+ Generates a file called partial_coverage.txt with 200 commands containing 6
+ actions each starting at command #700 and ending at command #899 and chosen
+ from the list of new line separated actions possible_actions.txt
+
+
+Options:
+ --action-list-file input_file_name
+ Name of file containing possible actions separated by new lines. You can
+ find supported actions in the automated_ui_tests.h documentation.
+
+ --output output_file_name
+ Name of XML file that will be outputted for use as input for
+ automated_ui_tests.exe.
+
+ --commands-per-file commands_per_file
+ Number of commands per file.
+
+ --actions-per-command actions_per_command
+ Number of actions per command.
+
+ --full-coverage
+ If full_coverage flag is set will output as many files as neccesary to cover
+ every combination of possible actions. Files will be named
+ output_file_name_1.txt, output_file_name_2.txt, etc...
+ If --full-coverage is true, --full-coverage-one-file and
+ --partial-coverage-from are ignored.
+
+ --full-coverage-one-file
+ Just like --full_coverage, but outputs to just one file.
+ Ignores commands_per_file. This is very likely to cause memory errors on
+ large action sets. If --full coverage-one-file is true,
+ --partial-coverage-from is ignored.
+
+ --partial-coverage-from command_to_start_at
+ Outputs a part of the full coverage, starting at command number
+ |command_to_start_at| and ending at command number |command_to_start_at| +
+ |commands_per_file|. Command numbering starts at 0, and the maximum
+ command number is number_of_actions_we_choose_from ^ actions_per_command - 1.
+ If |command_to_start_at| + |commands_per_file| is greater than the maximum
+ command number, then only the commands up to the maximum command number
+ are printed.
+
+ --quiet
+ Silence progress messages.
+"""
+
+import optparse
+import os.path
+import random
+import xml.dom.minidom
+
+class ComprehensiveListGenerator:
+ """Generates a comprehensive list of all the ways to choose x combinations
+ from an input list, with replacement.
+
+ Init takes |list_length|, which is the length of the of the combination and
+ |source_list|, which is the list we want to choose from.
+ GetNextPortionOfSourceList() returns a list of length |list_length| with a
+ portion of the complete list of all combinations or None once all possible
+ combinations have been returned.
+
+ Example:
+ >>> list_gen = ComprehensiveListGenerator(2, ['a','b','c'])
+ >>> print list_gen.GetNextPortionOfSourceList()
+ ['a','a']
+ >>> print list_gen.GetNextPortionOfSourceList()
+ ['a','b']
+ >>> print list_gen.GetNextPortionOfSourceList()
+ ['a','c']
+ >>> ...print list_gen.GetNextPortionOfSourceList() 6 more times...
+ >>> print list_gen.GetNextPortionOfSourceList()
+ None
+ >>> list_gen.SetIntegerListToCombinationNumber(2)
+ >>> print list_gen.GetCurrentPortionOfSourceList()
+ ['a','c']
+ >>> print list_gen.GetNextPortionOfSourceList()
+ ['b','a']
+ >>> list_gen.SetIntegerListToCombinationNumber(8)
+ >>> print list_gen.GetCurrentPortionOfSourceList()
+ ['c','c']
+ >>> list_gen.SetIntegerListToCombinationNumber(9)
+ >>> print list_gen.GetCurrentPortionOfSourceList()
+ None
+
+ Attributes:
+ __list_length: Length of the resulting combinations.
+ __source_list: The list we are pulling combinations from.
+ __integer_list: A list of integers representing which indices of
+ |source_list| to return.
+ """
+
+ def __init__(self, list_length, source_list):
+ self.__list_length = list_length
+ self.__integer_list = None
+ self.__source_list = source_list
+
+ def SetIntegerListToCombinationNumber(self, combo_number):
+ """ Sets the integer list to represent the |combo_number|th number in the
+ sequence, counting from 0.
+
+ Args:
+ combo_number: Number to set the integer list to represent.
+
+ Returns: Sets integer_list to None and returns False if the combo_number is
+ out of range (bigger than the maximum number of combinations possible or
+ less than 0)
+ """
+ if (combo_number < 0 or
+ combo_number >= len(self.__source_list) ** self.__list_length):
+ self.__integer_list = None
+ return False
+ if self.__integer_list == None:
+ self.__integer_list = []
+ for i in range(self.__list_length):
+ self.__integer_list.append(0)
+ reversed_range = range(self.__list_length)
+ reversed_range.reverse()
+ index_max_value = len(self.__source_list)
+ quotient = 0
+ for index in reversed_range:
+ quotient, remainder = divmod(combo_number, index_max_value)
+ combo_number = quotient
+ self.__integer_list[index] = remainder
+
+ return True
+
+ def __IncrementIntegerListIndex(self, index):
+ """ Increments the given index of integer_list, rolling over to 0 and
+ incrementing the a lower index if the index is incremented above the last
+ index of source_list
+
+ Args:
+ index: The index integer_list to attempt to increment.
+
+ Returns: False if it is impossible to incremement any index in the list
+ which is less than or equal to the given index.
+ """
+ self.__integer_list[index] += 1
+ if self.__integer_list[index] >= len(self.__source_list):
+ # We've incremented beyond the length of source_list, so reset to zero...
+ self.__integer_list[index] = 0
+ # And if our index is high enough, increment the next index. Otherwise
+ # we can't increment any further and should return false.
+ if index > 0:
+ return self.__IncrementIntegerListIndex(index-1)
+ else:
+ # Restart the integer list at the beginning
+ self.__integer_list = None
+ return False
+ # Successfuly incremented the index, return true.
+ return True
+
+ def __IncrementIntegerList(self):
+ """ Gets the next integer list in the series by attempting to increment the
+ final index of integer_list.
+
+ Returns: False if we can't increment any index in the integer_list.
+ """
+
+ # If the list is empty we just started, so populate it with zeroes.
+ if self.__integer_list == None:
+ self.__integer_list = []
+ for i in range(self.__list_length):
+ self.__integer_list.append(0)
+ return True
+ else:
+ return self.__IncrementIntegerListIndex(self.__list_length-1)
+
+ def GetCurrentPortionOfSourceList(self):
+ """ Returns the current portion of source_list corresponding to the
+ integer_list
+
+ For example, if our current state is:
+ integer_list = [0,1,0,2]
+ source_list = ['a','b','c','d']
+ Then calling GetCurrentPortionOfSourceList() returns:
+ ['a','b','a','c']
+
+ Returns: None if the integer_list is empty, otherwise a list of length
+ list_length with a combination of elements from source_list
+ """
+ portion_list = []
+ if self.__integer_list == None:
+ return None
+
+ for index in range(self.__list_length):
+ portion_list.append(self.__source_list[self.__integer_list[index]])
+
+ return portion_list
+
+ def GetNextPortionOfSourceList(self):
+ """ Increments the integer_list and then returns the current portion of
+ source_list corresponding to the integer_list.
+
+ This is the only function outside users should be calling. It will advance
+ to the next combination of elements from source_list, and return it. See
+ the class documentation for proper use.
+
+ Returns: None if all possible combinations of source_list have previously
+ been returned. Otherwise a new list of length list_length with a combination
+ of elements from source_list.
+ """
+ if self.__IncrementIntegerList():
+ return self.GetCurrentPortionOfSourceList()
+ else:
+ return None
+
+class AutomatedTestInputGenerator:
+ """Creates a random file with with the name |file_name| with
+ the number of commands and actions specified in the command line.
+
+ Attributes:
+ __commands_per_file: Number of commands per file.
+ __actions_per_command: Number of actions per command.
+ __actions_list: A list of strings representing the possible actions.
+ __is_verbose: If true, print progress messages
+ """
+ def __init__(self):
+ (options,args) = ParseCommandLine()
+ input_file = open(options.input_file_name)
+ actions_list = input_file.readlines()
+ input_file.close()
+
+ self.__commands_per_file = options.commands_per_file
+ self.__actions_per_command = options.actions_per_command
+ self.__actions_list = [action.strip() for action in actions_list]
+ self.__is_verbose = options.is_verbose
+
+ def __CreateDocument(self):
+ """ Create the starter XML document.
+
+ Returns: A tuple containing the XML document and its root element named
+ "Command List".
+ """
+ doc = xml.dom.minidom.Document()
+ root_element = doc.createElement("CommandList")
+ doc.appendChild(root_element)
+ return doc, root_element
+
+ def __WriteToOutputFile(self, file_name, output):
+ """Writes |output| to file with name |filename|. Overwriting any pre-existing
+ file.
+
+ Args:
+ file_name: Name of the file to create.
+ output: The string to write to file.
+ """
+ output_file = open(file_name, 'w')
+ output_file.write(output)
+ output_file.close()
+
+ def CreateRandomFile(self, file_name):
+ """Creates a random file with with the name |file_name| with
+ the number of commands and actions specified in the command line.
+
+ Args:
+ file_name - Name of the file to create.
+
+ Return:
+ Nothing.
+ """
+ output_doc, root_element = self.__CreateDocument()
+ for command_num in range(0, self.__commands_per_file):
+ command_element = output_doc.createElement("command")
+ for action_num in range(0,self.__actions_per_command):
+ action_element = output_doc.createElement(
+ random.choice(self.__actions_list))
+ command_element.appendChild(action_element)
+ root_element.appendChild(command_element)
+ self.__WriteToOutputFile(file_name, output_doc.toprettyxml())
+
+ def __AddCommandToDoc(self, output_doc, root_element, command, command_num):
+ """Adds a given command to the output XML document
+
+ Args:
+ output_doc - The output XML document. Used to create elements.
+ root_element - The root element of the XML document. (What we add the
+ command to)
+ command - The name of the command element we create and add to the doc.
+ command_num - The number of the command.
+
+ Return:
+ Nothing.
+ """
+ command_element = output_doc.createElement("command")
+ command_element.setAttribute("number", str(command_num))
+ for action in command:
+ action_element = output_doc.createElement(action)
+ command_element.appendChild(action_element)
+ root_element.appendChild(command_element)
+
+ def CreateComprehensiveFile(self, file_name, start_command, write_to_end):
+ """Creates one file containing all or part of the comprehensive list of
+ commands starting at a set command.
+
+ Args:
+ file_name - Name of the file to create.
+ start_command - Command number to start at.
+ write_to_end - If true, writes all remaining commands, starting at
+ start_command. (If start_command is 0, this would write all
+ possible commands to one file.) If false, write only
+ commands_per_file commands starting at start_command
+
+ Return:
+ Nothing.
+ """
+ list_generator = ComprehensiveListGenerator(self.__actions_per_command,
+ self.__actions_list)
+ output_doc, root_element = self.__CreateDocument()
+ command_counter = start_command
+ end_command = start_command + self.__commands_per_file
+ is_complete = False
+ # Set the underlying integer representation of the command to the
+ # the starting command number.
+ list_generator.SetIntegerListToCombinationNumber(start_command)
+ command = list_generator.GetCurrentPortionOfSourceList()
+ while (command != None and
+ (write_to_end == True or command_counter < end_command)):
+ self.__AddCommandToDoc(output_doc, root_element, command, command_counter)
+ command_counter += 1;
+ command = list_generator.GetNextPortionOfSourceList()
+
+ self.__WriteToOutputFile(file_name, output_doc.toprettyxml())
+
+ def CreateComprehensiveFiles(self, file_name):
+ """Creates a comprehensive coverage of all possible combinations from
+ action_list of length commands_per_file. Names them |file_name|_1,
+ |file_name|_2, and so on.
+
+ Args:
+ file_name - Name of the file to create.
+
+ Return:
+ Nothing.
+ """
+ list_generator = ComprehensiveListGenerator(self.__actions_per_command,
+ self.__actions_list)
+
+ is_complete = False
+ file_counter = 0
+ # Split the file name so we can include the file number before the extension.
+ base_file_name, extension = os.path.splitext(file_name)
+ command_num = 0
+
+ while is_complete == False:
+ output_doc, root_element = self.__CreateDocument()
+ file_counter += 1
+ if self.__is_verbose and file_counter % 200 == 0:
+ print "Created " + str(file_counter) + " files... "
+
+ for i in range(self.__commands_per_file):
+ # Get the next sequence of actions as a list.
+ command = list_generator.GetNextPortionOfSourceList()
+ if command == None:
+ is_complete = True
+ break
+
+ # Parse through the list and add them to the output document as children
+ # of a command element
+ self.__AddCommandToDoc(output_doc, root_element, command, command_num)
+ command_num += 1
+
+ # Finished the commands for this file, so write it and start on next file.
+ self.__WriteToOutputFile(base_file_name + "_" + str(file_counter) +
+ extension, output_doc.toprettyxml())
+
+def ParseCommandLine():
+ """Parses the command line.
+
+ Return: List of options and their values and a list of arguments which were
+ unparsed.
+ """
+ parser = optparse.OptionParser()
+ parser.set_defaults(full_coverage=False)
+ parser.add_option("-i", "--action-list-file", dest="input_file_name",
+ type="string", action="store", default="possible_actions.txt",
+ help="input file with a test of newline separated actions"
+ "which are possible. Default is 'possible_actions.txt'")
+ parser.add_option("-o", "--output", dest="output_file_name", type="string",
+ action="store", default="automated_ui_tests.txt",
+ help="the file to output the command list to")
+ parser.add_option("-c", "--commands-per-file", dest="commands_per_file",
+ type="int", action="store", default=500,
+ help="number of commands per file")
+ parser.add_option("-a", "--actions-per-command", dest="actions_per_command",
+ type="int", action="store", default=5,
+ help="number of actions per command")
+ parser.add_option("-f", "--full-coverage", dest="full_coverage",
+ action="store_true", help="Output files for every possible"
+ "combination. Default is to output just one random file.")
+ parser.add_option("-q", "--quiet",
+ action="store_false", dest="is_verbose", default=True,
+ help="don't print progress message while creating files")
+ parser.add_option("-1", "--full-coverage-one-file",
+ action="store_true", dest="full_coverage_one_file",
+ default=False,
+ help="complete coverage all outputted to one file")
+ parser.add_option("-p", "--partial-coverage-from", dest="start_at_command",
+ type="int", action="store", default=-1,
+ help="partial list from the complete coverage, starting at"
+ "command #start_at_command")
+
+ return parser.parse_args()
+
+def main():
+ (options,args) = ParseCommandLine()
+ test_generator = AutomatedTestInputGenerator()
+ if options.full_coverage == True:
+ if options.full_coverage_one_file and options.is_verbose == True:
+ print ("Error: Both --full-coverage and --full-coverage-one-file present,"
+ " ignoring --full-coverage-one-file")
+ if options.start_at_command >= 0 and options.is_verbose == True:
+ print ("Error: Both --full-coverage and --partial-coverage-from present,"
+ " ignoring --partial-coverage-from")
+ if options.is_verbose == True:
+ print "Starting to write comprehensive files:"
+ test_generator.CreateComprehensiveFiles(options.output_file_name)
+ if options.is_verbose == True:
+ print "Finished writing comprehensive files."
+ elif options.full_coverage_one_file:
+ if options.start_at_command >= 0 and options.is_verbose == True:
+ print ("Error: Both --full-coverage-one-file present and"
+ "--partial-coverage-from present, ignoring --partial-coverage-from")
+ if options.is_verbose == True:
+ print "Starting to write comprehensive file:"
+ test_generator.CreateComprehensiveFile(options.output_file_name, 0, True)
+ if options.is_verbose == True:
+ print "Finished writing comprehensive file."
+ elif options.start_at_command >= 0:
+ if options.is_verbose == True:
+ print "Starting to write partial file:"
+ test_generator.CreateComprehensiveFile(options.output_file_name,
+ options.start_at_command , False)
+ if options.is_verbose == True:
+ print "Finished writing partial file."
+ else:
+ test_generator.CreateRandomFile(options.output_file_name)
+ if options.is_verbose == True:
+ print "Output written to file: " + options.output_file_name
+
+if __name__ == '__main__':
+ main()
diff --git a/chrome/tools/automated_ui_test_tools/possible_actions.txt b/chrome/tools/automated_ui_test_tools/possible_actions.txt
new file mode 100644
index 0000000..4e0345b
--- /dev/null
+++ b/chrome/tools/automated_ui_test_tools/possible_actions.txt
@@ -0,0 +1,17 @@
+Navigate
+NewTab
+Back
+Forward
+CloseTab
+OpenWindow
+Reload
+FindInPage
+SelectNextTab
+SelectPrevTab
+ZoomPlus
+ZoomMinus
+Sessions
+Bookmarks
+History
+Downloads
+Applications \ No newline at end of file
diff --git a/chrome/tools/build/win/FILES b/chrome/tools/build/win/FILES
new file mode 100644
index 0000000..fa6dc0b
--- /dev/null
+++ b/chrome/tools/build/win/FILES
@@ -0,0 +1,51 @@
+chrome.exe
+chrome.dll
+crash_service.exe
+First Run
+icudt38.dll
+themes/default.dll
+resources
+rlz.dll
+locales/ar.dll
+locales/bg.dll
+locales/ca.dll
+locales/cs.dll
+locales/da.dll
+locales/de.dll
+locales/el.dll
+locales/en-GB.dll
+locales/en-US.dll
+locales/es.dll
+locales/es-419.dll
+locales/et.dll
+locales/fi.dll
+locales/fil.dll
+locales/fr.dll
+locales/he.dll
+locales/hi.dll
+locales/hr.dll
+locales/hu.dll
+locales/id.dll
+locales/it.dll
+locales/ja.dll
+locales/ko.dll
+locales/lt.dll
+locales/lv.dll
+locales/nl.dll
+locales/nb.dll
+locales/pl.dll
+locales/pt-BR.dll
+locales/pt-PT.dll
+locales/ro.dll
+locales/ru.dll
+locales/sk.dll
+locales/sl.dll
+locales/sr.dll
+locales/sv.dll
+locales/th.dll
+locales/tr.dll
+locales/uk.dll
+locales/vi.dll
+locales/zh-CN.dll
+locales/zh-TW.dll
+plugins/gears/gears.dll
diff --git a/chrome/tools/build/win/create_installer_archive.py b/chrome/tools/build/win/create_installer_archive.py
new file mode 100644
index 0000000..5f32472
--- /dev/null
+++ b/chrome/tools/build/win/create_installer_archive.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Script to create Chrome Installer archive.
+
+ This script is used to create an archive of all the files required for a
+ Chrome install in appropriate directory structure. It reads chrome.release
+ file as input, creates chrome.7z archive, compresses setup.exe and
+ generates packed_files.txt for mini_installer project.
+
+"""
+
+import ConfigParser
+import glob
+import md5
+import optparse
+import os
+import shutil
+import sys
+
+
+ARCHIVE_DIR = "installer_archive"
+FULL_ARCHIVE_FILE = "chrome.7z" # uncompresed full archive file
+C_FULL_ARCHIVE_FILE = "chrome.packed.7z" # compressed full archive file
+PATCH_FILE_NAME = "patch" # patch archive file name
+PATCH_FILE_EXT = ".packed.7z" # extension of patch archive file
+CHROME_DIR = "Chrome-bin"
+MINI_INSTALLER_INPUT_FILE = "packed_files.txt"
+SETUP_EXEC = "setup.exe"
+BSDIFF_EXEC = "bsdiff.exe"
+VERSION_FILE = "VERSION"
+PACKED_FILE_COMMENTS = """
+// This file is automatically generated by create_installer_archive.py.
+// It contains the resource entries that are going to be linked inside
+// mini_installer.exe. For each file to be linked there should be two
+// lines:
+// - The first line contains the output filename (without path) and the
+// type of the resource ('BN' means the file is not compressed and
+// 'BL' means the file is compressed.
+// - The second line contains the path to the input file. Uses '/' to
+// separate path components.
+"""
+
+def BuildVersion(output_dir):
+ """Returns the full build version string constructed from information in
+ VERSION_FILE. Any segment not found in that file will default to '0'.
+ """
+ major = 0
+ minor = 0
+ build = 0
+ patch = 0
+ # TODO(rahulk): find a better way to locate VERSION file
+ for line in open(os.path.join(output_dir, "..", VERSION_FILE), 'r'):
+ line = line.rstrip()
+ if line.startswith('MAJOR='):
+ major = line[6:]
+ elif line.startswith('MINOR='):
+ minor = line[6:]
+ elif line.startswith('BUILD='):
+ build = line[6:]
+ elif line.startswith('PATCH='):
+ patch = line[6:]
+ return '%s.%s.%s.%s' % (major, minor, build, patch)
+
+
+def Readconfig(output_dir, input_file, current_version):
+ """Reads config information from input file after setting default value of
+ global variabes.
+ """
+ variables = {}
+ variables['ChromeDir'] = CHROME_DIR
+ variables['VersionDir'] = os.path.join(variables['ChromeDir'],
+ current_version)
+ config = ConfigParser.SafeConfigParser(variables)
+ config.read(input_file)
+ return config
+
+
+def MakeStagingDirectory(output_dir):
+ """Creates a staging path for installer archive. If directory exists already,
+ deletes the existing directory.
+ """
+ file_path = os.path.join(output_dir, ARCHIVE_DIR)
+ if os.path.exists(file_path):
+ shutil.rmtree(file_path)
+ os.makedirs(file_path)
+ return file_path
+
+
+def CopyFilesToStagingDir(config, staging_dir, output_dir):
+ """Copies files required for installer archive to staging dir.
+ """
+ for option in config.options('FILES'):
+ if option.endswith('dir'):
+ continue
+
+ dst = os.path.join(staging_dir, config.get('FILES', option))
+ if not os.path.exists(dst):
+ os.makedirs(dst)
+ for file in glob.glob(os.path.join(output_dir, option)):
+ shutil.copy(file, dst)
+
+
+def RunSystemCommand(cmd):
+ if (os.system(cmd) != 0):
+ raise "Error while running cmd: %s" % cmd
+
+
+def CreateArchiveFile(output_dir, staging_dir, current_version,
+ prev_version_dir, prev_version, rebuild_archive):
+ """Creates a new installer archive file after deleting any existing old file.
+ """
+ # First create an uncompressed archive file for the current build
+ # TODO(rahulk): find a better way to locate 7za.exe
+ lzma_exec = os.path.join(output_dir, "..", "..", "third_party",
+ "lzma_sdk", "Executable", "7za.exe")
+ archive_file = os.path.join(output_dir, FULL_ARCHIVE_FILE)
+ cmd = '%s a -t7z "%s" "%s" -mx0' % (lzma_exec, archive_file,
+ os.path.join(staging_dir, CHROME_DIR))
+ # There doesnt seem to be any way in 7za.exe to override existing file so
+ # we always delete before creating a new one.
+ if not os.path.exists(archive_file):
+ RunSystemCommand(cmd)
+ elif rebuild_archive:
+ os.remove(archive_file)
+ RunSystemCommand(cmd)
+
+ # If we are generating a patch, run bsdiff against previous build and
+ # compress the resulting patch file. If this is not a patch just compress the
+ # uncompressed archive file.
+ if (prev_version_dir):
+ prev_archive_file = os.path.join(prev_version_dir, FULL_ARCHIVE_FILE)
+ patch_file = os.path.join(output_dir, "patch.7z")
+ cmd = '%s "%s" "%s" "%s"' % (os.path.join(output_dir, BSDIFF_EXEC),
+ prev_archive_file, archive_file, patch_file)
+ RunSystemCommand(cmd)
+
+ archive_file_name = PATCH_FILE_NAME + "-"
+ if prev_version:
+ archive_file_name += prev_version + "-"
+ archive_file_name += current_version + PATCH_FILE_EXT
+ orig_file = patch_file
+ else:
+ archive_file_name = C_FULL_ARCHIVE_FILE
+ orig_file = archive_file
+
+ compressed_archive_file_path = os.path.join(output_dir, archive_file_name)
+ cmd = '%s a -t7z "%s" "%s" -mx9' % (lzma_exec, compressed_archive_file_path,
+ orig_file)
+ if os.path.exists(compressed_archive_file_path):
+ os.remove(compressed_archive_file_path)
+ RunSystemCommand(cmd)
+
+ return archive_file_name
+
+
+def CompressSetupExec(output_dir):
+ """Compresses setup.exe to reduce size."""
+ cmd = 'makecab.exe /V1 /L "%s" "%s"' % (output_dir,
+ os.path.join(output_dir, SETUP_EXEC))
+ RunSystemCommand(cmd)
+
+
+def GetFileMD5Hash(file):
+ f = open(file, 'rb')
+ hash = md5.new(f.read()).hexdigest()
+ f.close()
+ return hash
+
+
+def CreateResourceInputFile(output_dir,
+ prev_version_dir, archive_file_name):
+ """Creates resource input file (packed_files.txt) for mini_installer project.
+
+ This method checks if we are generating a patch instead of full installer. In
+ case of patch it also checks if setup.exe has changed by comparing its
+ MD5 hash with the MD5 hash of previous setup.exe. If hash values are same
+ setup.exe is not included in packed_files.txt.
+
+ In case of patch we include patch.7z and in case of full
+ installer we include chrome.7z in packed_files.txt.
+ """
+ setup_exe_needed = 1
+ if (prev_version_dir):
+ current_hash = GetFileMD5Hash(os.path.join(output_dir, SETUP_EXEC))
+ prev_hash = GetFileMD5Hash(os.path.join(prev_version_dir, SETUP_EXEC))
+ if (current_hash == prev_hash):
+ setup_exe_needed = 0
+
+ if (setup_exe_needed):
+ CompressSetupExec(output_dir)
+ c_setup_file = SETUP_EXEC[:len(SETUP_EXEC) - 1] + "_"
+ setup_file_entry = "%s\t\tBL\n\"%s\"" % (c_setup_file,
+ os.path.join(output_dir, c_setup_file).replace("\\","/"))
+
+ archive_file_entry = "\n%s\t\tB7\n\"%s\"" % (archive_file_name,
+ os.path.join(output_dir, archive_file_name).replace("\\","/"))
+ output_file = os.path.join(output_dir, MINI_INSTALLER_INPUT_FILE)
+ f = open(output_file, 'w')
+ try:
+ f.write(PACKED_FILE_COMMENTS)
+ if (setup_exe_needed):
+ f.write(setup_file_entry)
+ f.write(archive_file_entry)
+ finally:
+ f.close()
+
+
+def main(options):
+ """Main method that reads input file, creates archive file and write
+ resource input file.
+ """
+ current_version = BuildVersion(options.output_dir)
+
+ config = Readconfig(options.output_dir, options.input_file, current_version)
+
+ staging_dir = MakeStagingDirectory(options.output_dir)
+
+ CopyFilesToStagingDir(config, staging_dir, options.output_dir)
+
+ # Name of the archive file built (for example - chrome.lz or
+ # patch-<old_version>-<new_version>.lz or patch-<new_version>.lz
+ archive_file_name = CreateArchiveFile(options.output_dir, staging_dir,
+ current_version, options.last_chrome_installer,
+ options.last_chrome_version, options.rebuild_archive)
+
+ CreateResourceInputFile(options.output_dir, options.last_chrome_installer,
+ archive_file_name)
+
+
+if '__main__' == __name__:
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('-o', '--output_dir', help='Output directory')
+ option_parser.add_option('-i', '--input_file', help='Input file')
+ option_parser.add_option('-r', '--rebuild_archive', action='store_true',
+ default=False, help='Rebuild Chrome.7z archive, even if it exists.')
+ option_parser.add_option('-l', '--last_chrome_installer',
+ help='Generate differential installer. The value of this parameter ' +
+ 'specifies the directory that contains base versions of ' +
+ 'setup.exe & chrome.7z.')
+ option_parser.add_option('-v', '--last_chrome_version',
+ help='Version of the previous installer. ' +
+ 'Used only for the purpose of naming archive file. Optional.')
+
+ options, args = option_parser.parse_args()
+ sys.exit(main(options))
diff --git a/chrome/tools/build/win/data_dll.vsprops b/chrome/tools/build/win/data_dll.vsprops
new file mode 100644
index 0000000..9e6f840
--- /dev/null
+++ b/chrome/tools/build/win/data_dll.vsprops
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="data_dll"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_USRDLL;GENERATED_RESOURCES_DLL_EXPORTS"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ LinkIncremental="1"
+ LinkTimeCodeGeneration="0"
+ ResourceOnlyDLL="true"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/debugger_disabled.vsprops b/chrome/tools/build/win/debugger_disabled.vsprops
new file mode 100644
index 0000000..b068039
--- /dev/null
+++ b/chrome/tools/build/win/debugger_disabled.vsprops
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="debugger"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="CHROME_DEBUGGER_DISABLED"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="CHROME_DEBUGGER_DISABLED"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/dependencies.py b/chrome/tools/build/win/dependencies.py
new file mode 100644
index 0000000..0ae3b8e
--- /dev/null
+++ b/chrome/tools/build/win/dependencies.py
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Script to verify a Portable Executable's dependencies.
+
+Analyzes the input portable executable (a DLL or an EXE for example), extracts
+its imports and confirms that its dependencies haven't changed. This is for
+regression testing.
+
+Returns 0 if the list matches.
+ 1 if one or more dependencies has been removed.
+ 2 if one or more dependencies has been added. This preempts removal
+ result code.
+"""
+
+import optparse
+import os
+import subprocess
+import sys
+
+DUMPBIN = "dumpbin.exe"
+
+
+class Error(Exception):
+ def __init__(self, message):
+ self.message = message
+
+
+def RunSystemCommand(cmd):
+ return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
+
+
+def RunDumpbin(binary_file):
+ """Runs dumpbin and parses its output.
+
+ Args: binary_file: the binary to analyze
+ Returns: a tuple of the dependencies and the delay-load dependencies
+
+ The output of dumpbin that we will be parsing looks like this:
+ --
+ <blah blah>
+
+ Image has the following dependencies:
+
+ foo.dll
+ bar.dll
+
+ Image has the following delay load dependencies:
+
+ foobar.dll
+ other.dll
+
+ Summary
+
+ <blah blah>
+ --
+ The following parser extracts the dll names from the above format.
+ """
+ cmd = DUMPBIN + " /dependents " + binary_file
+ output = RunSystemCommand(cmd)
+ dependents = []
+ delay_loaded = []
+ (START, DEPENDENCIES_HEADER, DEPENDENCIES, DELAY_LOAD_HEADER, DELAY_LOAD,
+ SUMMARY_HEADER, SUMMARY) = (0, 1, 2, 3, 4, 5, 6)
+ current_section = START
+ # Very basic scanning.
+ for line in output.splitlines():
+ line = line.strip()
+ if len(line) > 1:
+ if line == "Image has the following dependencies:":
+ if current_section != START:
+ raise Error("Internal parsing error.")
+ current_section = DEPENDENCIES_HEADER;
+ elif line == "Image has the following delay load dependencies:":
+ if current_section != DEPENDENCIES:
+ raise Error("Internal parsing error.")
+ current_section = DELAY_LOAD_HEADER;
+ elif line == "Summary":
+ current_section = SUMMARY_HEADER;
+ elif current_section == DEPENDENCIES:
+ # Got a dependent
+ dependents.append(line)
+ elif current_section == DELAY_LOAD:
+ # Got a delay-loaded
+ delay_loaded.append(line)
+ else:
+ if current_section == DEPENDENCIES_HEADER:
+ current_section = DEPENDENCIES
+ elif current_section == DELAY_LOAD_HEADER:
+ current_section = DELAY_LOAD
+ elif current_section == SUMMARY_HEADER:
+ current_section = SUMMARY
+ return dependents, delay_loaded
+
+
+def Diff(name, type, current, expected, deps_file):
+ """
+ Args: name: Portable executable name being analysed.
+ type: Type of dependency.
+ current: List of current dependencies.
+ expected: List of dependencies that are expected.
+ deps_file: File name of the .deps file.
+ Returns 0 if the lists are equal
+ 1 if one entry in list1 is missing
+ 2 if one entry in list2 is missing.
+ """
+ # Create sets of lower-case names.
+ set_expected = set([x.lower() for x in expected])
+ set_current = set([x.lower() for x in current])
+ only_in_expected = set_expected - set_current
+ only_in_current = set_current - set_expected
+
+ # Find difference between the sets.
+ found_extra = 0
+ name = os.path.basename(name).lower()
+ if len(only_in_expected) or len(only_in_current):
+ print name.upper() + " DEPENDENCIES MISMATCH\n"
+
+ if len(only_in_current):
+ found_extra = 1
+ print "%s is no longer dependent on these %s: %s." % (name,
+ type,
+ ' '.join(only_in_current))
+ print "Please update \"%s\"." % deps_file
+
+ if len(only_in_expected):
+ found_extra = 2
+ string = "%s is now dependent on these %s, but shouldn't: %s." % (name,
+ type,
+ ' '.join(only_in_expected))
+ stars = '*' * len(string)
+ print "**" + stars + "**"
+ print "* " + string + " *"
+ print "**" + stars + "**\n"
+ print "Please update \"%s\"." % deps_file
+ return found_extra
+
+
+def VerifyDependents(pe_name, dependents, delay_loaded, list_file):
+ """Compare the actual dependents to the expected ones."""
+ scope = {}
+ execfile(list_file, scope)
+ deps_result = Diff(pe_name,
+ "dll",
+ dependents,
+ scope["dependents"],
+ list_file)
+ delayed_result = Diff(pe_name,
+ "delay loaded dll",
+ delay_loaded,
+ scope["delay_loaded"],
+ list_file)
+ return max(deps_result, delayed_result)
+
+
+def main(options, args):
+ # PE means portable executable. It's any .DLL, .EXE, .SYS, .AX, etc.
+ pe_name = args[0]
+ deps_file = args[1]
+ dependents, delay_loaded = RunDumpbin(pe_name)
+ return VerifyDependents(pe_name, dependents, delay_loaded, deps_file)
+
+
+if '__main__' == __name__:
+ usage = "usage: %prog [options] input output"
+ option_parser = optparse.OptionParser(usage = usage)
+ options, args = option_parser.parse_args()
+ if len(args) != 2:
+ parser.error("Incorrect number of arguments")
+ sys.exit(main(options, args))
diff --git a/chrome/tools/build/win/flattened_html_file.bat b/chrome/tools/build/win/flattened_html_file.bat
new file mode 100644
index 0000000..310d88d
--- /dev/null
+++ b/chrome/tools/build/win/flattened_html_file.bat
@@ -0,0 +1,14 @@
+:: Batch file run as build command for flattening files
+:: The custom build rule is set to expect (inputfile).html
+@echo off
+
+setlocal
+
+set InFile=%~1
+set SolutionDir=%~2
+set OutFile=%~3
+
+:: Use GNU tools
+call %SolutionDir%\..\third_party\gnu\setup_env.bat
+
+%SolutionDir%\..\third_party\python_24\python.exe %SolutionDir%\tools\build\win\html_inline.py %InFile% %OutFile%
diff --git a/chrome/tools/build/win/flattened_html_file.rules b/chrome/tools/build/win/flattened_html_file.rules
new file mode 100644
index 0000000..8cc33ba
--- /dev/null
+++ b/chrome/tools/build/win/flattened_html_file.rules
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="Flattened HTML Resource"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="Flattened HTML Resource"
+ DisplayName="Flattened HTML Resource"
+ CommandLine="$(SolutionDir)\tools\build\win\flattened_html_file.bat $(InputPath) &quot;$(SolutionDir)&quot; &quot;$(IntDir)\$(InputName)_flat.html&quot;"
+ Outputs="$(IntDir)\$(InputName)_flat.html"
+ AdditionalDependencies="$(SolutionDir)\tools\build\win\flattened_html_file.bat"
+ FileExtensions="*.html"
+ ExecutionDescription="Generating resources..."
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/build/win/font_file_copy.rules b/chrome/tools/build/win/font_file_copy.rules
new file mode 100644
index 0000000..912a79b
--- /dev/null
+++ b/chrome/tools/build/win/font_file_copy.rules
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="Font file copy"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="Font file copy"
+ DisplayName="Font file copy"
+ CommandLine="xcopy /Y /D /Q $(InputPath) $(OutDir)\fonts"
+ Outputs="$(OutDir)\fonts\$(InputFileName)"
+ FileExtensions="*.ttf;*.afm"
+ ExecutionDescription="Copying font file..."
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/build/win/hardlink_failsafe.bat b/chrome/tools/build/win/hardlink_failsafe.bat
new file mode 100644
index 0000000..4113922
--- /dev/null
+++ b/chrome/tools/build/win/hardlink_failsafe.bat
@@ -0,0 +1,12 @@
+@echo off
+:: %1 is source
+:: %2 is output
+
+:: Delete it if it existed
+if exist "%2" del "%2"
+
+:: Try to create a hardlink (argument are in reverse order). Hide errors if they occur, we have a fallback.
+fsutil hardlink create "%2" "%1" > nul
+
+:: If it failed, copy it instead. Don't hide errors if it fails.
+if errorlevel 1 copy "%1" "%2"
diff --git a/chrome/tools/build/win/html_inline.py b/chrome/tools/build/win/html_inline.py
new file mode 100644
index 0000000..17d24ca
--- /dev/null
+++ b/chrome/tools/build/win/html_inline.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Flattens a HTML file by inlining its external resources.
+
+This is a small script that takes a HTML file, looks for src attributes
+and inlines the specified file, producing one HTML file with no external
+dependencies.
+
+This does not inline CSS styles, nor does it inline anything referenced
+from an inlined file.
+"""
+
+import re
+import sys
+import base64
+import mimetypes
+from os import path
+
+def ReadFile(input_filename):
+ """Helper function that returns input_filename as a string.
+
+ Args:
+ input_filename: name of file to be read
+
+ Returns:
+ string
+ """
+ f = open(input_filename, 'rb')
+ file_contents = f.read()
+ f.close()
+ return file_contents
+
+def SrcInline(src_match, base_path):
+ """regex replace function.
+
+ Takes a regex match for src="filename", attempts to read the file
+ at 'filename' and returns the src attribute with the file inlined
+ as a data URI
+
+ Args:
+ src_match: regex match object with 'filename' named capturing group
+ base_path: path that to look for files in
+
+ Returns:
+ string
+ """
+ filename = src_match.group('filename')
+
+ if filename.find(':') != -1:
+ # filename is probably a URL, which we don't want to bother inlining
+ return src_match.group(0)
+
+ filepath = path.join(base_path, filename)
+ mimetype = mimetypes.guess_type(filename)[0] or 'text/plain'
+ inline_data = base64.standard_b64encode(ReadFile(filepath))
+
+ prefix = src_match.string[src_match.start():src_match.start('filename')-1]
+ return "%s\"data:%s;base64,%s\"" % (prefix, mimetype, inline_data)
+
+def InlineFile(input_filename, output_filename):
+ """Inlines the resources in a specified file.
+
+ Reads input_filename, finds all the src attributes and attempts to
+ inline the files they are referring to, then writes the result
+ to output_filename.
+
+ Args:
+ input_filename: name of file to read in
+ output_filename: name of file to be written to
+ """
+ print "inlining %s to %s" % (input_filename, output_filename)
+ input_filepath = path.dirname(input_filename)
+
+ def SrcReplace(src_match):
+ """Helper function to provide SrcInline with the base file path"""
+ return SrcInline(src_match, input_filepath)
+
+ # TODO(glen): Make this regex not match src="" text that is not inside a tag
+ flat_text = re.sub('src="(?P<filename>[^"\']*)"',
+ SrcReplace,
+ ReadFile(input_filename))
+
+ # TODO(glen): Make this regex not match url('') that is not inside a style
+ flat_text = re.sub('background:[ ]*url\(\'(?P<filename>[^"\']*)\'',
+ SrcReplace,
+ flat_text)
+
+ out_file = open(output_filename, 'wb')
+ out_file.writelines(flat_text)
+ out_file.close()
+
+def main():
+ if len(sys.argv) <= 2:
+ print "Flattens a HTML file by inlining its external resources.\n"
+ print "html_inline.py inputfile outputfile"
+ else:
+ InlineFile(sys.argv[1], sys.argv[2])
+
+if __name__ == '__main__':
+ main()
diff --git a/chrome/tools/build/win/inspector_copy.rules b/chrome/tools/build/win/inspector_copy.rules
new file mode 100644
index 0000000..475b5b1
--- /dev/null
+++ b/chrome/tools/build/win/inspector_copy.rules
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="Inspector file copy"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="Inspector file copy"
+ CommandLine="xcopy /Y /D /Q $(InputPath) $(OutDir)\Resources\Inspector\"
+ Outputs="$(OutDir)\Resources\Inspector\$(InputFileName)"
+ FileExtensions="*.html;*.js;*.gif;*.css"
+ ExecutionDescription="Copying resource file..."
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ <CustomBuildRule
+ Name="Inspector image file copy"
+ CommandLine="xcopy /Y /D /Q $(InputPath) $(OutDir)\Resources\Inspector\Images\"
+ Outputs="$(OutDir)\Resources\Inspector\Images\$(InputFileName)"
+ FileExtensions="*.png"
+ ExecutionDescription="Copying resource file..."
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/build/win/js_engine.vsprops b/chrome/tools/build/win/js_engine.vsprops
new file mode 100644
index 0000000..c329d6e
--- /dev/null
+++ b/chrome/tools/build/win/js_engine.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="js_engine"
+ InheritedPropertySheets="js_engine_impl$(JS_ENGINE_TYPE).vsprops"
+>
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/js_engine_impl.vsprops b/chrome/tools/build/win/js_engine_impl.vsprops
new file mode 100644
index 0000000..a4f1996
--- /dev/null
+++ b/chrome/tools/build/win/js_engine_impl.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="js_engine_impl"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="CHROME_V8"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/js_engine_impl_kjs.vsprops b/chrome/tools/build/win/js_engine_impl_kjs.vsprops
new file mode 100644
index 0000000..20b24da
--- /dev/null
+++ b/chrome/tools/build/win/js_engine_impl_kjs.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="js_engine_impl_kjs"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="CHROME_KJS"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/language_dll.vsprops b/chrome/tools/build/win/language_dll.vsprops
new file mode 100644
index 0000000..6da7f8e
--- /dev/null
+++ b/chrome/tools/build/win/language_dll.vsprops
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="language_dll"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ BaseAddress="0x3CF00000"
+ OutputFile="$(OutDir)\locales\$(ProjectName).dll"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/make_zip.sh b/chrome/tools/build/win/make_zip.sh
new file mode 100755
index 0000000..2fe8af12
--- /dev/null
+++ b/chrome/tools/build/win/make_zip.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# A simple shell script for creating a chrome zip from an output directory.
+# Pass the path to the output directory you wish to package.
+
+if [ $# = 0 ]; then
+ echo "usage: make_zip.sh path/to/release/dir [output-name]"
+ exit 1
+fi
+
+tools_dir=$(dirname "$0")
+release_dir="$1"
+
+files=$(cat "$tools_dir/FILES")
+
+output=${2:-chrome-win32}
+rm -fr $output $output.zip
+mkdir $output
+
+# Get the absolute path of the output directory. We need it when copying
+# files.
+output_abs=`cygpath -a $output`
+
+# Use cp --parents to copy full relative directory. Since we need the
+# relative directory for the zip, change into the release dir.
+pushd "$release_dir"
+# The file names in FILES may contain whitespace, e.g. 'First Run'.
+# Change IFS setting so we only split words with '\n'
+IFS_Default=$IFS
+IFS=$'\n'
+for f in ${files[@]}; do
+ cp -r --parents "$f" "$output_abs"
+done
+IFS=$IFS_Default
+popd
+
+zip -r $output.zip $output
diff --git a/chrome/tools/build/win/map_drive.bat b/chrome/tools/build/win/map_drive.bat
new file mode 100644
index 0000000..c415d1e
--- /dev/null
+++ b/chrome/tools/build/win/map_drive.bat
@@ -0,0 +1,30 @@
+@echo off
+
+set SHARE_NAME=\\chrome-dev\chrome
+set DRIVE_LETTER=%1
+set PATH=%SystemRoot%;%SystemRoot%\system32
+
+net use %DRIVE_LETTER%
+if errorlevel 1 goto DRIVE_NOT_MAPPED
+
+net use %DRIVE_LETTER% | find "%SHARE_NAME%" > nul
+if not errorlevel 1 goto DRIVE_ALREADY_MAPPED
+
+:WRONG_DRIVE_MAPPED
+echo %DRIVE_LETTER% Drive mapped to wrong share, disconnecting..
+net use %DRIVE_LETTER% /DELETE
+goto MAPDRIVE
+
+:DRIVE_ALREADY_MAPPED
+echo %DRIVE_LETTER% Drive already mapped..
+goto END
+
+:DRIVE_NOT_MAPPED
+echo %DRIVE_LETTER% Drive not mapped..
+goto MAPDRIVE
+
+:MAPDRIVE
+echo Mapping %DRIVE_LETTER% to %SHARE_NAME%
+net use %DRIVE_LETTER% %SHARE_NAME%
+
+:END \ No newline at end of file
diff --git a/chrome/tools/build/win/precompiled.cc b/chrome/tools/build/win/precompiled.cc
new file mode 100644
index 0000000..b09ed13
--- /dev/null
+++ b/chrome/tools/build/win/precompiled.cc
@@ -0,0 +1,2 @@
+// This file exists purely as a means to generate precompiled.pch
+#include "precompiled.h"
diff --git a/chrome/tools/build/win/precompiled.h b/chrome/tools/build/win/precompiled.h
new file mode 100644
index 0000000..50ff863
--- /dev/null
+++ b/chrome/tools/build/win/precompiled.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// This header file is pre-compiled and added to the cl.exe command line for
+// each source file. You should not include it explicitly. Instead, you
+// should ensure that your source files specify all of their include files
+// explicitly so they may be used without this pre-compiled header.
+//
+// To make use of this header file in a vcproj, just add precompiled.cc as a
+// build target, and modify its build properties to enable /Yc for that file
+// only. Then add the precompiled.vsprops property sheet to your vcproj.
+//
+
+// windows headers
+// TODO: when used, winsock2.h must be included before windows.h or
+// the build blows up. The best fix is to define WIN32_LEAN_AND_MEAN.
+// The best workaround is to define _WINSOCKAPI_. That's what winsock2.h does.
+#include <winsock2.h>
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+
+// runtime headers
+#include <cassert>
+#include <climits>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <memory.h>
+
+// Usual STL
+#include <algorithm>
+#include <list>
+#include <map>
+#include <string>
+#include <strstream>
+#include <vector>
+
diff --git a/chrome/tools/build/win/precompiled.vsprops b/chrome/tools/build/win/precompiled.vsprops
new file mode 100644
index 0000000..464ea32
--- /dev/null
+++ b/chrome/tools/build/win/precompiled.vsprops
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="precompiled"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)tools\build\win&quot;"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ PrecompiledHeaderFile="$(IntDir)\precompiled.pch"
+ ForcedIncludeFiles="precompiled.h"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/precompiled_wtl.cc b/chrome/tools/build/win/precompiled_wtl.cc
new file mode 100644
index 0000000..0f6272d
--- /dev/null
+++ b/chrome/tools/build/win/precompiled_wtl.cc
@@ -0,0 +1,2 @@
+// This file exists purely as a means to generate precompiled.pch
+#include "precompiled_wtl.h"
diff --git a/chrome/tools/build/win/precompiled_wtl.h b/chrome/tools/build/win/precompiled_wtl.h
new file mode 100644
index 0000000..a44408a
--- /dev/null
+++ b/chrome/tools/build/win/precompiled_wtl.h
@@ -0,0 +1,44 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See precompiled.h for info on using this file, substituting references to
+// "precompiled" with "precompiled_wtl"
+
+#include "chrome/tools/build/win/precompiled.h"
+
+// ATL/WTL headers
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlwin.h>
+#include <atldlgs.h>
+#include <atlframe.h>
+#include <atlmisc.h>
+#include <atlctrls.h>
+#include <atlcrack.h>
+#include <atltheme.h>
diff --git a/chrome/tools/build/win/precompiled_wtl.vsprops b/chrome/tools/build/win/precompiled_wtl.vsprops
new file mode 100644
index 0000000..1e9a915
--- /dev/null
+++ b/chrome/tools/build/win/precompiled_wtl.vsprops
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="precompiled_wtl"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)tools\build\win&quot;"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled_wtl.h"
+ PrecompiledHeaderFile="$(IntDir)\precompiled_wtl.pch"
+ ForcedIncludeFiles="precompiled_wtl.h"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/release.rules b/chrome/tools/build/win/release.rules
new file mode 100644
index 0000000..2b2f683
--- /dev/null
+++ b/chrome/tools/build/win/release.rules
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="installer archive"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="create installer archive"
+ DisplayName="create installer archive"
+ CommandLine="$(SolutionDir)..\third_party\python_24\python.exe $(SolutionDir)tools\build\win\create_installer_archive.py --output_dir=&quot;$(OutDir)&quot; --input_file=&quot;$(InputPath)&quot; [LastChromeInstaller] [LastChromeVersion] [RebuildArchive]"
+ Outputs="$(OutDir)/$(InputName).7z;$(OutDir)/packed_files.txt;"
+ AdditionalDependencies="$(SolutionDir)\tools\build\win\create_installer_archive.py;$(OutDir)\chrome.exe;$(OutDir)\crash_reporter.exe;$(OutDir)\chrome.dll;$(OutDir)\locales\en-US.dll;$(OutDir)\icudt38.dll"
+ FileExtensions="*.release"
+ ExecutionDescription="create installer archive"
+ >
+ <Properties>
+ <StringProperty
+ Name="LastChromeInstaller"
+ DisplayName="Last Chrome Installer Directory"
+ Description="Directory where old version of installer is present (setup.exe and chrome.7z)"
+ Switch="--last_chrome_installer=&quot;[value]&quot;"
+ />
+ <StringProperty
+ Name="LastChromeVersion"
+ DisplayName="Last Chrome Version"
+ Description="Last released version of Chrome (used to name the patch file)"
+ Switch="--last_chrome_version=&quot;[value]&quot;"
+ />
+ <BooleanProperty
+ Name="RebuildArchive"
+ DisplayName="Rebuild Archive"
+ Description="Rebuilds chrome.7z even if already exists"
+ Switch="--rebuild_archive"
+ />
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/build/win/reliability_test.vsprops b/chrome/tools/build/win/reliability_test.vsprops
new file mode 100644
index 0000000..e3a76bf
--- /dev/null
+++ b/chrome/tools/build/win/reliability_test.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="reliability_test"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="RELIABILITY_TEST"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/resource_text_file_copy.rules b/chrome/tools/build/win/resource_text_file_copy.rules
new file mode 100644
index 0000000..eb468f8
--- /dev/null
+++ b/chrome/tools/build/win/resource_text_file_copy.rules
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="Resource text file copy"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="Resource text file copy"
+ CommandLine="xcopy /Y /D /Q $(InputPath) $(OutDir)\resources"
+ Outputs="$(OutDir)\Resources\$(InputFileName)"
+ FileExtensions="*.html;*.js;*.gif"
+ ExecutionDescription="Copying resource file..."
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/build/win/sort_sln.py b/chrome/tools/build/win/sort_sln.py
new file mode 100644
index 0000000..f679c1b
--- /dev/null
+++ b/chrome/tools/build/win/sort_sln.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+
+if len(sys.argv) != 2:
+ print """Usage: sort_sln.py <SOLUTIONNAME>.sln
+to sort the solution file to a normalized scheme. Do this before checking in
+changes to a solution file to avoid having a lot of unnecessary diffs."""
+ sys.exit(1)
+
+filename = sys.argv[1]
+print "Sorting " + filename;
+
+try:
+ sln = open(filename, "r");
+except IOError:
+ print "Unable to open " + filename + " for reading."
+ sys.exit(1)
+
+output = ""
+seclines = None
+while 1:
+ line = sln.readline()
+ if not line:
+ break
+
+ if seclines is not None:
+ # Process the end of a section, dump the sorted lines
+ if line.lstrip().startswith('End'):
+ output = output + ''.join(sorted(seclines))
+ seclines = None
+ # Process within a section
+ else:
+ seclines.append(line)
+ continue
+
+ # Process the start of a section
+ if (line.lstrip().startswith('GlobalSection') or
+ line.lstrip().startswith('ProjectSection')):
+ if seclines: raise Exception('Already in a section')
+ seclines = []
+
+ output = output + line
+
+sln.close()
+try:
+ sln = open(filename, "w")
+ sln.write(output)
+except IOError:
+ print "Unable to write to " + filename
+ sys.exit(1);
+print "Done."
diff --git a/chrome/tools/build/win/test_memory_usage.vsprops b/chrome/tools/build/win/test_memory_usage.vsprops
new file mode 100644
index 0000000..93c34cc
--- /dev/null
+++ b/chrome/tools/build/win/test_memory_usage.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_memory_usage"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="psapi.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/test_shell_tests.vsprops b/chrome/tools/build/win/test_shell_tests.vsprops
new file mode 100644
index 0000000..d008862
--- /dev/null
+++ b/chrome/tools/build/win/test_shell_tests.vsprops
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell_tests"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\v8\public;"
+ PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/ui_test.vsprops b/chrome/tools/build/win/ui_test.vsprops
new file mode 100644
index 0000000..4a41137
--- /dev/null
+++ b/chrome/tools/build/win/ui_test.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="ui_test"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="UI_TEST"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/unit_test.vsprops b/chrome/tools/build/win/unit_test.vsprops
new file mode 100644
index 0000000..893ef30
--- /dev/null
+++ b/chrome/tools/build/win/unit_test.vsprops
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="unit_test"
+ InheritedPropertySheets="$(SolutionDir)third_party\wtl\using_wtl.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(SolutionDir).."
+ PreprocessorDefinitions="UNIT_TEST"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="rpcrt4.lib oleacc.lib comsupp.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/using_generated_strings.vsprops b/chrome/tools/build/win/using_generated_strings.vsprops
new file mode 100644
index 0000000..da11cb0
--- /dev/null
+++ b/chrome/tools/build/win/using_generated_strings.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="using_generated_strings"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(OutDir)\obj\generated_resources&quot;;&quot;$(OutDir)\obj\localized_strings&quot;"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/using_javascriptcore.vsprops b/chrome/tools/build/win/using_javascriptcore.vsprops
new file mode 100644
index 0000000..139d6f7
--- /dev/null
+++ b/chrome/tools/build/win/using_javascriptcore.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="using_javascriptcore"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)\third_party\webkit\src\JavaScriptCore\bindings&quot;;&quot;$(SolutionDir)\third_party\webkit\src\JavaScriptCore&quot;;&quot;$(SolutionDir)\third_party\webkit\src\JavaScriptCore\os-win32&quot;;&quot;$(SolutionDir)webkit\pending&quot;;&quot;$(SolutionDir)webkit\pending\wtf&quot;"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/tools/build/win/version.bat b/chrome/tools/build/win/version.bat
new file mode 100644
index 0000000..0cda045
--- /dev/null
+++ b/chrome/tools/build/win/version.bat
@@ -0,0 +1,44 @@
+:: Batch file run as build command for vers.vcproj
+@echo off
+
+setlocal
+
+set InFile=%~1
+set SolutionDir=%~2
+set IntDir=%~3
+set OutFile=%~4
+set VarsBat=%IntDir%/vers-vars.bat
+
+:: Use GNU tools
+call %SolutionDir%\..\third_party\gnu\setup_env.bat
+
+:: Load version digits as environment variables
+cat %SolutionDir%\VERSION | sed "s/\(.*\)/set \1/" > %VarsBat%
+
+:: Load branding strings as environment variables
+cat %SolutionDir%\BRANDING | sed "s/\(.*\)/set \1/" >> %VarsBat%
+
+if not "%OFFICIAL_BUILD%" == "1" set OFFICIAL_BUILD=0
+
+:: Determine the current repository revision number
+set PATH=%~dp0..\..\..\..\third_party\svn;%PATH%
+svn.exe info | grep.exe "Revision:" | cut -d" " -f2- | sed "s/\(.*\)/set LASTCHANGE=\1/" >> %VarsBat%
+call %VarsBat%
+
+::echo LastChange: %LASTCHANGE%
+
+:: output file
+cat %InFile% | sed "s/@MAJOR@/%MAJOR%/" ^
+ | sed "s/@MINOR@/%MINOR%/" ^
+ | sed "s/@BUILD@/%BUILD%/" ^
+ | sed "s/@PATCH@/%PATCH%/" ^
+ | sed "s/@COMPANY_FULLNAME@/%COMPANY_FULLNAME%/" ^
+ | sed "s/@COMPANY_SHORTNAME@/%COMPANY_SHORTNAME%/" ^
+ | sed "s/@PRODUCT_FULLNAME@/%PRODUCT_FULLNAME%/" ^
+ | sed "s/@PRODUCT_SHORTNAME@/%PRODUCT_SHORTNAME%/" ^
+ | sed "s/@PRODUCT_EXE@/%PRODUCT_EXE%/" ^
+ | sed "s/@COPYRIGHT@/%COPYRIGHT%/" ^
+ | sed "s/@OFFICIAL_BUILD@/%OFFICIAL_BUILD%/" ^
+ | sed "s/@LASTCHANGE@/%LASTCHANGE%/" > %OutFile%
+
+endlocal
diff --git a/chrome/tools/build/win/version.rules b/chrome/tools/build/win/version.rules
new file mode 100644
index 0000000..244de21
--- /dev/null
+++ b/chrome/tools/build/win/version.rules
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioToolFile
+ Name="Version Template"
+ Version="8.00"
+ >
+ <Rules>
+ <CustomBuildRule
+ Name="Version"
+ DisplayName="Version"
+ CommandLine="$(SolutionDir)/tools/build/win/version.bat [inputs] &quot;$(SolutionDir)&quot; &quot;$(IntDir)&quot; &quot;$(IntDir)/$(InputName)&quot;"
+ Outputs="$(IntDir)/$(InputName)"
+ AdditionalDependencies="$(SolutionDir)/VERSION;$(SolutionDir)/BRANDING;$(SolutionDir)/tools/build/win/version.bat"
+ FileExtensions="*.version"
+ ExecutionDescription="Generating version template file"
+ >
+ <Properties>
+ </Properties>
+ </CustomBuildRule>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/chrome/tools/convert_dict/aff_reader.cc b/chrome/tools/convert_dict/aff_reader.cc
new file mode 100644
index 0000000..2279e02
--- /dev/null
+++ b/chrome/tools/convert_dict/aff_reader.cc
@@ -0,0 +1,301 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/tools/convert_dict/aff_reader.h"
+
+#include <algorithm>
+
+#include "base/string_util.h"
+#include "chrome/tools/convert_dict/hunspell_reader.h"
+
+namespace convert_dict {
+
+namespace {
+
+// Returns true if the given line begins with the given case-sensitive
+// NULL-terminated ASCII string.
+bool StringBeginsWith(const std::string& str, const char* with) {
+ size_t cur = 0;
+ while (cur < str.size() && with[cur] != 0) {
+ if (str[cur] != with[cur])
+ return false;
+ cur++;
+ }
+ return with[cur] == 0;
+}
+
+// Collapses runs of spaces to only one space.
+void CollapseDuplicateSpaces(std::string* str) {
+ int prev_space = false;
+ for (size_t i = 0; i < str->length(); i++) {
+ if ((*str)[i] == ' ') {
+ if (prev_space) {
+ str->erase(str->begin() + i);
+ i--;
+ }
+ prev_space = true;
+ } else {
+ prev_space = false;
+ }
+ }
+}
+
+} // namespace
+
+AffReader::AffReader(const std::string& filename) {
+ fopen_s(&file_, filename.c_str(), "r");
+
+ // Default to Latin1 in case the file doesn't specify it.
+ encoding_ = "ISO8859-1";
+}
+
+AffReader::~AffReader() {
+ if (file_)
+ fclose(file_);
+}
+
+bool AffReader::Read() {
+ if (!file_)
+ return false;
+
+ // TODO(brettw) handle byte order mark.
+
+ bool got_command = false;
+ bool got_first_af = false;
+ bool got_first_rep = false;
+
+ has_indexed_affixes_ = false;
+
+ while (!feof(file_)) {
+ std::string line = ReadLine(file_);
+
+ // Save comment lines before any commands.
+ if (!got_command && !line.empty() && line[0] == '#') {
+ intro_comment_.append(line);
+ intro_comment_.push_back('\n');
+ continue;
+ }
+
+ StripComment(&line);
+ if (line.empty())
+ continue;
+ got_command = true;
+
+ if (StringBeginsWith(line, "SET ")) {
+ // Character set encoding.
+ encoding_ = line.substr(4);
+ TrimLine(&encoding_);
+ } else if (StringBeginsWith(line, "AF ")) {
+ // Affix. The first one is the number of ones following which we don't
+ // bother with.
+ has_indexed_affixes_ = true;
+ if (got_first_af)
+ AddAffixGroup(&line.substr(3));
+ else
+ got_first_af = true;
+ } else if (StringBeginsWith(line, "SFX ") ||
+ StringBeginsWith(line, "PFX ")) {
+ AddAffix(&line);
+ } else if (StringBeginsWith(line, "REP ")) {
+ // The first rep line is the number of ones following which we don't
+ // bother with.
+ if (got_first_rep)
+ AddReplacement(&line.substr(4));
+ else
+ got_first_rep = true;
+ } else if (StringBeginsWith(line, "TRY ") ||
+ StringBeginsWith(line, "MAP ")) {
+ HandleEncodedCommand(line);
+ } else if (StringBeginsWith(line, "IGNORE ")) {
+ printf("We don't support the IGNORE command yet. This would change how "
+ "we would insert things in our lookup table.\n");
+ exit(1);
+ } else if (StringBeginsWith(line, "COMPLEXPREFIXES ")) {
+ printf("We don't support the COMPLEXPREFIXES command yet. This would "
+ "mean we have to insert words backwords as well (I think)\n");
+ exit(1);
+ } else {
+ // All other commands get stored in the other commands list.
+ HandleRawCommand(line);
+ }
+ }
+
+ return true;
+}
+
+bool AffReader::EncodingToUTF8(const std::string& encoded,
+ std::string* utf8) const {
+ std::wstring wide_word;
+ if (!CodepageToWide(encoded, encoding(),
+ OnStringUtilConversionError::FAIL, &wide_word))
+ return false;
+ *utf8 = WideToUTF8(wide_word);
+ return true;
+}
+
+int AffReader::GetAFIndexForAFString(const std::string& af_string) {
+ std::map<std::string, int>::iterator found = affix_groups_.find(af_string);
+ if (found != affix_groups_.end())
+ return found->second;
+ std::string my_string(af_string);
+ return AddAffixGroup(&my_string);
+}
+
+// We convert the data from our map to an indexed list, and also prefix each
+// line with "AF" for the parser to read later.
+std::vector<std::string> AffReader::GetAffixGroups() const {
+ int max_id = 0;
+ for (std::map<std::string, int>::const_iterator i = affix_groups_.begin();
+ i != affix_groups_.end(); ++i) {
+ if (i->second > max_id)
+ max_id = i->second;
+ }
+
+ std::vector<std::string> ret;
+
+ ret.resize(max_id);
+ for (std::map<std::string, int>::const_iterator i = affix_groups_.begin();
+ i != affix_groups_.end(); ++i) {
+ // Convert the indices into 1-based.
+ ret[i->second - 1] = std::string("AF ") + i->first;
+ }
+
+ return ret;
+}
+
+int AffReader::AddAffixGroup(std::string* rule) {
+ TrimLine(rule);
+
+ // We use the 1-based index of the rule. This matches the way Hunspell
+ // refers to the numbers.
+ int affix_id = static_cast<int>(affix_groups_.size()) + 1;
+ affix_groups_.insert(std::make_pair(*rule, affix_id));
+ return affix_id;
+}
+
+void AffReader::AddAffix(std::string* rule) {
+ TrimLine(rule);
+ CollapseDuplicateSpaces(rule);
+
+ // These lines have two forms:
+ // AFX D Y 4 <- First line, lists how many affixes for "D" there are.
+ // AFX D 0 d e <- Following lines.
+ // We want to ensure the two last groups on the last line are encoded in
+ // UTF-8, and we want to make sure that the affix identifier "D" is *not*
+ // encoded, since that's basically an 8-bit identifier.
+
+ // Count to the third space. Everything after that will be re-encoded. This
+ // will re-encode the number on the first line, but that will be a NOP. If
+ // there are not that many groups, we won't reencode it, but pass it through.
+ int found_spaces = 0;
+ for (size_t i = 0; i < rule->length(); i++) {
+ if ((*rule)[i] == ' ') {
+ found_spaces++;
+ if (found_spaces == 3) {
+ std::string part = rule->substr(i); // From here to end.
+
+ size_t slash_index = part.find('/');
+ if (slash_index != std::string::npos && !has_indexed_affixes()) {
+ // This can also have a rule string associated with it following a
+ // slash. For example:
+ // PFX P 0 foo/Y .
+ // The "Y" is a flag. For example, the aff file might have a line:
+ // COMPOUNDFLAG Y
+ // so that means that this prefix would be a compound one.
+ //
+ // It expects these rules to use the same alias rules as the .dic
+ // file. We've forced it to use aliases, which is a numberical index
+ // instead of these character flags, and this needs to be consistent.
+
+ std::string before_flags = part.substr(0, slash_index + 1);
+
+ // After the slash are both the flags, then whitespace, then the part
+ // that tells us what to strip.
+ std::vector<std::string> after_slash;
+ SplitString(part.substr(slash_index + 1), ' ', &after_slash);
+ if (after_slash.size() < 2) {
+ // Note that we may get a third term here which is the
+ // morphological description of this rule. This happens in the tests
+ // only, so we can just ignore it.
+ printf("ERROR: Didn't get enough after the slash\n");
+ return;
+ }
+
+ part = StringPrintf("%s%d %s", before_flags.c_str(),
+ GetAFIndexForAFString(after_slash[0]),
+ after_slash[1].c_str());
+ }
+
+ // Reencode from here
+ std::string reencoded;
+ if (!EncodingToUTF8(part, &reencoded))
+ break;
+
+ *rule = rule->substr(0, i) + reencoded;
+ break;
+ }
+ }
+ }
+
+ affix_rules_.push_back(*rule);
+}
+
+void AffReader::AddReplacement(std::string* rule) {
+ TrimLine(rule);
+
+ std::string utf8rule;
+ if (!EncodingToUTF8(*rule, &utf8rule))
+ return;
+
+ std::vector<std::string> split;
+ SplitString(utf8rule, ' ', &split);
+
+ // There should be two parts.
+ if (split.size() != 2)
+ return;
+
+ // Underscores are used to represent spaces
+ // (since the line is parsed on spaces).
+ std::replace(split[0].begin(), split[0].end(), '_', ' ');
+ std::replace(split[1].begin(), split[1].end(), '_', ' ');
+
+ replacements_.push_back(std::make_pair(split[0], split[1]));
+}
+
+void AffReader::HandleRawCommand(const std::string& line) {
+ other_commands_.push_back(line);
+}
+
+void AffReader::HandleEncodedCommand(const std::string& line) {
+ std::string utf8;
+ if (EncodingToUTF8(line, &utf8))
+ other_commands_.push_back(utf8);
+}
+
+} // namespace convert_dict
diff --git a/chrome/tools/convert_dict/aff_reader.h b/chrome/tools/convert_dict/aff_reader.h
new file mode 100644
index 0000000..0cfd40a
--- /dev/null
+++ b/chrome/tools/convert_dict/aff_reader.h
@@ -0,0 +1,131 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_TOOLS_CONVERT_DICT_AFF_READER_H__
+#define CHROME_TOOLS_CONVERT_DICT_AFF_READER_H__
+
+#include <map>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+namespace convert_dict {
+
+class AffReader {
+ public:
+ AffReader(const std::string& filename);
+ ~AffReader();
+
+ bool Read();
+
+ // Returns whether this file uses indexed affixes, or, on false, whether the
+ // rule string will be specified literally in the .dic file. This must be
+ // called after Read().
+ bool has_indexed_affixes() const { return has_indexed_affixes_; }
+
+ // Returns a string representing the encoding of the dictionary. This will
+ // default to ISO-8859-1 if the .aff file does not specify it.
+ const char* encoding() const { return encoding_.c_str(); }
+
+ // Converts the given string from the file encoding to UTF-8, returning true
+ // on success.
+ bool EncodingToUTF8(const std::string& encoded, std::string* utf8) const;
+
+ // Adds a new affix string, returning the index. If it already exists, returns
+ // the index of the existing one. This is used to convert .dic files which
+ // list the
+ // You must not call this until after Read();
+ int GetAFIndexForAFString(const std::string& af_string);
+
+ // Getters for the computed data.
+ const std::string& comments() const { return intro_comment_; }
+ const std::vector<std::string>& affix_rules() const { return affix_rules_; }
+ const std::vector< std::pair<std::string, std::string> >&
+ replacements() const {
+ return replacements_;
+ }
+ const std::vector<std::string>& other_commands() const {
+ return other_commands_;
+ }
+
+ // Returns the affix groups ("AF" lines) for this file. The indices into this
+ // are 1-based, but we don't use the 0th item, so lookups will have to
+ // subtract one to get the index. This is how hunspell stores this data.
+ std::vector<std::string> GetAffixGroups() const;
+
+ private:
+ // Command-specific handlers. These are given the string folling the
+ // command. The input rule may be modified arbitrarily by the function.
+ int AddAffixGroup(std::string* rule); // Returns the new affix group ID.
+ void AddAffix(std::string* rule); // SFX/PFX
+ void AddReplacement(std::string* rule);
+ //void HandleFlag(std::string* rule);
+
+ // Used to handle "other" commands. The "raw" just saves the line as-is.
+ // The "encoded" version converts the line to UTF-8 and saves it.
+ void HandleRawCommand(const std::string& line);
+ void HandleEncodedCommand(const std::string& line);
+
+ FILE* file_;
+
+ // Comments from the beginning of the file. This is everything before the
+ // first command. We want to store this since it often contains the copyright
+ // information.
+ std::string intro_comment_;
+
+ // Encoding of the source words.
+ std::string encoding_;
+
+ // Affix rules. These are populated by "AF" commands. The .dic file can refer
+ // to these by index. They are indexed by their string value (the list of
+ // characters representing rules), and map to the numeric affix IDs.
+ //
+ // These can also be added using GetAFIndexForAFString.
+ std::map<std::string, int> affix_groups_;
+
+ // True when the affixes were specified in the .aff file using indices. The
+ // dictionary reader uses this to see how it should treat the stuff after the
+ // word on each line.
+ bool has_indexed_affixes_;
+
+ // SFX and PFX commands. This is a list of each of those lines in the order
+ // they appear in the file. They have been re-encoded.
+ std::vector<std::string> affix_rules_;
+
+ // Replacement commands. The first string is a possible input, and the second
+ // is the replacment.
+ std::vector< std::pair<std::string, std::string> > replacements_;
+
+ // All other commands.
+ std::vector<std::string> other_commands_;
+};
+
+} // namespace convert_dict
+
+#endif // CHROME_TOOLS_CONVERT_DICT_AFF_READER_H__
diff --git a/chrome/tools/convert_dict/convert_dict.cc b/chrome/tools/convert_dict/convert_dict.cc
new file mode 100644
index 0000000..c6918ce
--- /dev/null
+++ b/chrome/tools/convert_dict/convert_dict.cc
@@ -0,0 +1,149 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This tool converts Hunspell .aff/.dic pairs to a combined binary dictionary
+// format (.bdic). This format is more compact, and can be more efficiently
+// read by the client application.
+//
+// We do this conversion manually before publishing dictionary files. It is not
+// part of any build process.
+//
+// See PrintHelp() below for usage.
+
+#include <stdio.h>
+
+#include "base/icu_util.h"
+#include "base/string_util.h"
+#include "chrome/third_party/hunspell/google/bdict_reader.h"
+#include "chrome/third_party/hunspell/google/bdict_writer.h"
+#include "chrome/tools/convert_dict/aff_reader.h"
+#include "chrome/tools/convert_dict/dic_reader.h"
+
+namespace {
+
+// Compares the given word list with the serialized trie to make sure they
+// are the same.
+bool VerifyWords(const convert_dict::DicReader::WordList& org_words,
+ const std::string& serialized) {
+ hunspell::BDictReader reader;
+ if (!reader.Init(reinterpret_cast<const unsigned char*>(serialized.data()),
+ serialized.size())) {
+ printf("BDict is invalid\n");
+ return false;
+ }
+ hunspell::WordIterator iter = reader.GetAllWordIterator();
+
+ int affix_ids[hunspell::BDict::MAX_AFFIXES_PER_WORD];
+
+ static const int buf_size = 128;
+ char buf[buf_size];
+ for (size_t i = 0; i < org_words.size(); i++) {
+ int affix_matches = iter.Advance(buf, buf_size, affix_ids);
+ if (affix_matches == 0)
+ return false; // Found the end before we expectd.
+ if (org_words[i].first != buf)
+ return false; // Word doesn't match.
+
+ if (affix_matches != static_cast<int>(org_words[i].second.size()))
+ return false; // Different number of affix indices.
+
+ // Check the individual affix indices.
+ for (size_t affix_index = 0; affix_index < org_words[i].second.size();
+ affix_index++) {
+ if (affix_ids[affix_index] != org_words[i].second[affix_index])
+ return false; // Index doesn't match.
+ }
+ }
+
+ return true;
+}
+
+int PrintHelp() {
+ printf("Usage: convert_dict <dicfile base name>\n\n");
+ printf("Example:\n convert_dict en-US\nwill read en-US.dic / en-US.aff and\n");
+ printf("generate en-US.bdic\n\n");
+ return 1;
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ if (argc != 2)
+ return PrintHelp();
+
+ icu_util::Initialize();
+
+ std::string file_base = argv[1];
+
+ std::string aff_name = file_base + ".aff";
+ printf("Reading %s ...\n", aff_name.c_str());
+ convert_dict::AffReader aff_reader(aff_name.c_str());
+ if (!aff_reader.Read()) {
+ printf("Unable to read the aff file.\n");
+ return 1;
+ }
+
+ std::string dic_name = file_base + ".dic";
+ printf("Reading %s ...\n", dic_name.c_str());
+ convert_dict::DicReader dic_reader(dic_name.c_str());
+ if (!dic_reader.Read(&aff_reader)) {
+ printf("Unable to read the dic file.\n");
+ return 1;
+ }
+
+ hunspell::BDictWriter writer;
+ writer.SetComment(aff_reader.comments());
+ writer.SetAffixRules(aff_reader.affix_rules());
+ writer.SetAffixGroups(aff_reader.GetAffixGroups());
+ writer.SetReplacements(aff_reader.replacements());
+ writer.SetOtherCommands(aff_reader.other_commands());
+ writer.SetWords(dic_reader.words());
+
+ printf("Serializing...\n");
+ std::string serialized = writer.GetBDict();
+
+ printf("Verifying...\n");
+ if (!VerifyWords(dic_reader.words(), serialized)) {
+ printf("ERROR converting, the dictionary does not check out OK.");
+ return 1;
+ }
+
+ std::string out_name = file_base + ".bdic";
+ printf("Writing %s ...\n", out_name.c_str());
+ FILE* out_file;
+ fopen_s(&out_file, out_name.c_str(), "wb");
+ if (!out_file) {
+ printf("ERROR writing file\n");
+ return 1;
+ }
+ fwrite(&serialized[0], 1, serialized.size(), out_file);
+ fclose(out_file);
+
+ return 0;
+}
diff --git a/chrome/tools/convert_dict/convert_dict.vcproj b/chrome/tools/convert_dict/convert_dict.vcproj
new file mode 100644
index 0000000..72e19be
--- /dev/null
+++ b/chrome/tools/convert_dict/convert_dict.vcproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="convert_dict"
+ ProjectGUID="{42ECD5EC-722F-41DE-B6B8-83764C8016DF}"
+ RootNamespace="convert_dict"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\..\build\debug.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="..\..\..\build\release.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\aff_reader.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\aff_reader.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\third_party\hunspell\google\bdict.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\third_party\hunspell\google\bdict_reader.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\third_party\hunspell\google\bdict_reader.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\third_party\hunspell\google\bdict_writer.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\third_party\hunspell\google\bdict_writer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\convert_dict.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dic_reader.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dic_reader.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hunspell_reader.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\hunspell_reader.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/tools/convert_dict/dic_reader.cc b/chrome/tools/convert_dict/dic_reader.cc
new file mode 100644
index 0000000..8358e13
--- /dev/null
+++ b/chrome/tools/convert_dict/dic_reader.cc
@@ -0,0 +1,165 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/tools/convert_dict/dic_reader.h"
+
+#include <algorithm>
+#include <set>
+
+#include "base/string_util.h"
+#include "chrome/tools/convert_dict/aff_reader.h"
+#include "chrome/tools/convert_dict/hunspell_reader.h"
+
+namespace convert_dict {
+
+namespace {
+
+// Maps each unique word to the unique affix group IDs associated with it.
+typedef std::map<std::string, std::set<int> > WordSet;
+
+void SplitDicLine(const std::string& line, std::vector<std::string>* output) {
+ // We split the line on a slash not preceeded by a backslash. A slash at the
+ // beginning of the line is not a separator either.
+ size_t slash_index = line.size();
+ for (size_t i = 0; i < line.size(); i++) {
+ if (line[i] == '/' && i > 0 && line[i - 1] != '\\') {
+ slash_index = i;
+ break;
+ }
+ }
+
+ output->clear();
+
+ // Everything before the slash index is the first term. We also need to
+ // convert all escaped slashes ("\/" sequences) to regular slashes.
+ std::string word = line.substr(0, slash_index);
+ ReplaceSubstringsAfterOffset(&word, 0, "\\/", "/");
+ output->push_back(word);
+
+ // Everything (if anything) after the slash is the second.
+ if (slash_index < line.size() - 1)
+ output->push_back(line.substr(slash_index + 1));
+}
+
+} // namespace
+
+DicReader::DicReader(const std::string& filename) {
+ fopen_s(&file_, filename.c_str(), "r");
+}
+
+DicReader::~DicReader() {
+ if (file_)
+ fclose(file_);
+}
+
+bool DicReader::Read(AffReader* aff_reader) {
+ if (!file_)
+ return false;
+
+ bool got_count = false;
+ int line_number = 0;
+
+ WordSet word_set;
+ while (!feof(file_)) {
+ std::string line = ReadLine(file_);
+ line_number++;
+ StripComment(&line);
+ if (line.empty())
+ continue;
+
+ if (!got_count) {
+ // Skip the first nonempty line, this is the line count. We don't bother
+ // with it and just read all the lines.
+ got_count = true;
+ continue;
+ }
+
+ std::vector<std::string> split;
+ SplitDicLine(line, &split);
+ if (split.size() == 0 || split.size() > 2) {
+ printf("Line %d has extra slashes in the dic file\n", line_number);
+ return false;
+ }
+
+ // The first part is the word, the second (optional) part is the affix. We
+ // always use UTF-8 as the encoding to simplify life.
+ std::string utf8word;
+ if (!aff_reader->EncodingToUTF8(split[0], &utf8word)) {
+ printf("Unable to convert line %d from %s to UTF-8 in the dic file\n",
+ line_number, aff_reader->encoding());
+ return false;
+ }
+
+ // We always convert the affix to an index. 0 means no affix.
+ int affix_index = 0;
+ if (split.size() == 2) {
+ // Got a rule, which is the stuff after the slash. The line may also have
+ // an optional term separated by a tab. This is the morphological
+ // description. We don't care about this (it is used in the tests to
+ // generate a nice dump), so we remove it.
+ size_t split1_tab_offset = split[1].find('\t');
+ if (split1_tab_offset != std::string::npos)
+ split[1] = split[1].substr(0, split1_tab_offset);
+
+ if (aff_reader->has_indexed_affixes())
+ affix_index = atoi(split[1].c_str());
+ else
+ affix_index = aff_reader->GetAFIndexForAFString(split[1]);
+ }
+
+ WordSet::iterator found = word_set.find(utf8word);
+ if (found == word_set.end()) {
+ std::set<int> affix_vector;
+ affix_vector.insert(affix_index);
+ word_set.insert(std::make_pair(utf8word, affix_vector));
+ } else {
+ found->second.insert(affix_index);
+ }
+ }
+
+ // Make sure the words are sorted, they may be unsorted in the input.
+ for (WordSet::iterator word = word_set.begin(); word != word_set.end();
+ ++word) {
+ std::vector<int> affixes;
+ for (std::set<int>::iterator aff = word->second.begin();
+ aff != word->second.end(); ++aff)
+ affixes.push_back(*aff);
+
+ // Double check that the affixes are sorted. This isn't strictly necessary
+ // but it's nice for the file to have a fixed layout.
+ std::sort(affixes.begin(), affixes.end());
+ words_.push_back(std::make_pair(word->first, affixes));
+ }
+
+ // Double-check that the words are sorted.
+ std::sort(words_.begin(), words_.end());
+ return true;
+}
+
+} // namespace convert_dict
diff --git a/chrome/tools/convert_dict/dic_reader.h b/chrome/tools/convert_dict/dic_reader.h
new file mode 100644
index 0000000..4a9a059
--- /dev/null
+++ b/chrome/tools/convert_dict/dic_reader.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_TOOLS_CONVERT_DICT_DIC_READER_H__
+#define CHROME_TOOLS_CONVERT_DICT_DIC_READER_H__
+
+#include <stdio.h>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace convert_dict {
+
+class AffReader;
+
+// Reads Hunspell .dic files.
+class DicReader {
+ public:
+ // Associated with each word is a list of affix group IDs. This will typically
+ // be only one long, but may be more if there are multiple groups of
+ // independent affix rules for a base word.
+ typedef std::pair<std::string, std::vector<int> > WordEntry;
+ typedef std::vector<WordEntry> WordList;
+
+ DicReader(const std::string& filename);
+ ~DicReader();
+
+ // Non-numeric affixes will be added to the given AffReader and converted into
+ // indices.
+ bool Read(AffReader* aff_reader);
+
+ // Returns the words read by Read(). These will be in order.
+ const WordList& words() const { return words_; }
+
+ private:
+ FILE* file_;
+
+ // Contains all words and their corresponding affix index.
+ WordList words_;
+};
+
+} // namespace convert_dict
+
+#endif // CHROME_TOOLS_CONVERT_DICT_DIC_READER_H__
diff --git a/chrome/tools/convert_dict/hunspell_reader.cc b/chrome/tools/convert_dict/hunspell_reader.cc
new file mode 100644
index 0000000..7b44f76
--- /dev/null
+++ b/chrome/tools/convert_dict/hunspell_reader.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/tools/convert_dict/hunspell_reader.h"
+
+#include "base/string_util.h"
+
+namespace convert_dict {
+
+// This silly 64K buffer is just copied from Hunspell's way of parsing.
+const int kLineBufferLen = 65535;
+char line_buffer[kLineBufferLen];
+
+// Shortcut for trimming whitespace from both ends of the line.
+void TrimLine(std::string* line) {
+ if (line->size() > 3 &&
+ static_cast<unsigned char>((*line)[0]) == 0xef &&
+ static_cast<unsigned char>((*line)[1]) == 0xbb &&
+ static_cast<unsigned char>((*line)[2]) == 0xbf)
+ *line = line->substr(3);
+
+ TrimWhitespace(*line, TRIM_ALL, line);
+}
+
+std::string ReadLine(FILE* file) {
+ const char* line = fgets(line_buffer, kLineBufferLen - 1, file);
+ if (!line)
+ return std::string();
+
+ std::string str = line;
+ TrimLine(&str);
+ return str;
+}
+
+void StripComment(std::string* line) {
+ for (size_t i = 0; i < line->size(); i++) {
+ if ((*line)[i] == '#') {
+ line->resize(i);
+ TrimLine(line);
+ return;
+ }
+ }
+}
+
+} // namespace convert_dict
+
diff --git a/chrome/tools/convert_dict/hunspell_reader.h b/chrome/tools/convert_dict/hunspell_reader.h
new file mode 100644
index 0000000..2c0a1db
--- /dev/null
+++ b/chrome/tools/convert_dict/hunspell_reader.h
@@ -0,0 +1,51 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_TOOLS_DIC_CONVERTER_HUNSPELL_READER_H__
+#define CHROME_TOOLS_DIC_CONVERTER_HUNSPELL_READER_H__
+
+#include <string>
+
+// Common routines for reading hunspell files.
+namespace convert_dict {
+
+// Reads one line and returns it. Whitespace will be trimmed.
+std::string ReadLine(FILE* file);
+
+// Trims whitespace from the beginning and end of the given string. Also trims
+// UTF-8 byte order markers from the beginning.
+void TrimLine(std::string* line);
+
+// Strips any comments for the given line.
+void StripComment(std::string* line);
+
+} // namespace convert_dict
+
+#endif // CHROME_TOOLS_DIC_CONVERTER_HUNSPELL_READER_H__
+
diff --git a/chrome/tools/crash_service/SConscript b/chrome/tools/crash_service/SConscript
new file mode 100644
index 0000000..b682986
--- /dev/null
+++ b/chrome/tools/crash_service/SConscript
@@ -0,0 +1,99 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ '$BREAKPAD_DIR/src',
+ '#/..',
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+
+ '/DEBUG',
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+
+ ],
+ LIBS = [
+ 'advapi32.lib',
+ 'comdlg32.lib',
+ 'gdi32.lib',
+ 'kernel32.lib',
+ 'msimg32.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'psapi.lib',
+ 'shell32.lib',
+ 'user32.lib',
+ 'usp10.lib',
+ 'uuid.lib',
+ 'version.lib',
+ 'wininet.lib',
+ 'winspool.lib',
+ 'ws2_32.lib',
+
+ 'DelayImp.lib',
+ ],
+)
+
+libs = [
+ '$BASE_DIR/base.lib',
+ '$BASE_DIR/gfx/base_gfx.lib',
+ '$BREAKPAD_DIR/breakpad_handler.lib',
+ '$BREAKPAD_DIR/breakpad_sender.lib',
+ '$CHROME_DIR/common/common.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$SKIA_DIR/skia.lib',
+ '$ZLIB_DIR/zlib.lib',
+]
+
+
+input_files = [
+ 'main.cc',
+ 'crash_service.cc',
+]
+
+exe = env.Program('crash_service.exe', input_files + libs)
+
+i = env.Install('$TARGET_ROOT', exe)
+Alias('chrome', i)
diff --git a/chrome/tools/crash_service/crash_service.cc b/chrome/tools/crash_service/crash_service.cc
new file mode 100644
index 0000000..172b66a
--- /dev/null
+++ b/chrome/tools/crash_service/crash_service.cc
@@ -0,0 +1,454 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/tools/crash_service/crash_service.h"
+
+#include <windows.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
+#include "breakpad/src/client/windows/sender/crash_report_sender.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/win_util.h"
+
+// TODO(cpu): Bug 1169078. There is a laundry list of things to do for this
+// application. They will be addressed as they are required.
+
+namespace {
+
+const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
+
+const wchar_t kCrashReportURL[] = L"https://www.google.com/cr/report";
+const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";
+
+typedef std::map<std::wstring, std::wstring> CrashMap;
+
+bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
+ const std::wstring& reporter_tag, CrashMap* map) {
+ google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();
+
+ for (int i = 0; i < info.count; ++i) {
+ (*map)[info.entries[i].name] = info.entries[i].value;
+ }
+
+ (*map)[L"rept"] = reporter_tag;
+
+ return (map->size() > 0);
+}
+
+bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
+ std::wstring file_path(dump_path);
+ size_t last_dot = file_path.rfind(L'.');
+ if (last_dot == std::wstring::npos)
+ return false;
+ file_path.resize(last_dot);
+ file_path += L".txt";
+
+ std::wofstream file(file_path.c_str(),
+ std::ios_base::out | std::ios_base::app | std::ios::binary);
+ if (!file.is_open())
+ return false;
+
+ CrashMap::const_iterator pos;
+ for (pos = map.begin(); pos != map.end(); ++pos) {
+ std::wstring line = pos->first;
+ line += L':';
+ line += pos->second;
+ line += L'\n';
+ file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
+ }
+ return true;
+}
+
+// The window procedure task is to handle when a) the user logs off.
+// b) the system shuts down or c) when the user closes the window.
+LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case WM_CLOSE:
+ case WM_ENDSESSION:
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ return 0;
+}
+
+// This is the main and only application window.
+HWND g_top_window = NULL;
+
+bool CreateTopWindow(HINSTANCE instance, bool visible) {
+ WNDCLASSEXW wcx = {0};
+ wcx.cbSize = sizeof(wcx);
+ wcx.style = CS_HREDRAW | CS_VREDRAW;
+ wcx.lpfnWndProc = CrashSvcWndProc;
+ wcx.hInstance = instance;
+ wcx.lpszClassName = L"crash_svc_class";
+ ATOM atom = ::RegisterClassExW(&wcx);
+ DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;
+
+ // The window size is zero but being a popup window still shows in the
+ // task bar and can be closed using the system menu or using task manager.
+ HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
+ CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
+ NULL, NULL, instance, NULL);
+ if (!window)
+ return false;
+
+ ::UpdateWindow(window);
+ LOG(INFO) << "window handle is " << window;
+ g_top_window = window;
+ return true;
+}
+
+// Simple helper class to keep the process alive until the current request
+// finishes.
+class ProcessingLock {
+ public:
+ ProcessingLock() {
+ ::InterlockedIncrement(&op_count_);
+ }
+ ~ProcessingLock() {
+ ::InterlockedDecrement(&op_count_);
+ }
+ static bool IsWorking() {
+ return (op_count_ != 0);
+ }
+ private:
+ static volatile LONG op_count_;
+};
+
+volatile LONG ProcessingLock::op_count_ = 0;
+
+// This structure contains the information that the worker thread needs to
+// send a crash dump to the server.
+struct DumpJobInfo {
+ DWORD pid;
+ CrashService* self;
+ CrashMap map;
+ std::wstring dump_path;
+
+ DumpJobInfo(DWORD process_id, CrashService* service,
+ const CrashMap& crash_map, const std::wstring& path)
+ : pid(process_id), self(service), map(crash_map), dump_path(path) {
+ }
+};
+
+} // namespace
+
+// Command line switches:
+const wchar_t CrashService::kMaxReports[] = L"max-reports";
+const wchar_t CrashService::kNoWindow[] = L"no-window";
+const wchar_t CrashService::kReporterTag[]= L"reporter";
+
+CrashService::CrashService(const std::wstring& report_dir)
+ : report_path_(report_dir),
+ sender_(NULL),
+ dumper_(NULL),
+ requests_handled_(0),
+ requests_sent_(0),
+ clients_connected_(0),
+ clients_terminated_(0) {
+ chrome::RegisterPathProvider();
+}
+
+CrashService::~CrashService() {
+ AutoLock lock(sending_);
+ delete dumper_;
+ delete sender_;
+}
+
+bool CrashService::Initialize(const std::wstring& command_line) {
+ using google_breakpad::CrashReportSender;
+ using google_breakpad::CrashGenerationServer;
+
+ const wchar_t* pipe_name = kTestPipeName;
+ std::wstring dumps_path;
+ int max_reports = -1;
+
+ // The checkpoint file allows CrashReportSender to enforce the the maximum
+ // reports per day quota. Does not seem to serve any other purpose.
+ std::wstring checkpoint_path = report_path_;
+ file_util::AppendToPath(&checkpoint_path, kCheckPointFile);
+
+ // The dumps path is typically : '<user profile>\Local settings\
+ // Application data\Goggle\Chrome\Crash Reports' and the report path is
+ // Application data\Google\Chrome\Reported Crashes.txt
+ if (!PathService::Get(chrome::DIR_USER_DATA, &report_path_)) {
+ LOG(ERROR) << "could not get DIR_USER_DATA";
+ return false;
+ }
+ file_util::AppendToPath(&report_path_, chrome::kCrashReportLog);
+ if (!PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path)) {
+ LOG(ERROR) << "could not get DIR_CRASH_DUMPS";
+ return false;
+ }
+
+ CommandLine cmd_line(command_line);
+
+ // We can override the send reports quota with a command line switch.
+ if (cmd_line.HasSwitch(kMaxReports))
+ max_reports = _wtoi(cmd_line.GetSwitchValue(kMaxReports).c_str());
+
+ if (max_reports > 0) {
+ // Create the http sender object.
+ sender_ = new CrashReportSender(checkpoint_path);
+ if (!sender_) {
+ LOG(ERROR) << "could not create sender";
+ return false;
+ }
+ sender_->set_max_reports_per_day(max_reports);
+ }
+ // Create the OOP crash generator object.
+ dumper_ = new CrashGenerationServer(pipe_name, NULL,
+ &CrashService::OnClientConnected, this,
+ &CrashService::OnClientDumpRequest, this,
+ &CrashService::OnClientExited, this,
+ true, &dumps_path);
+ if (!dumper_) {
+ LOG(ERROR) << "could not create dumper";
+ return false;
+ }
+
+ if (!CreateTopWindow(::GetModuleHandleW(NULL),
+ !cmd_line.HasSwitch(kNoWindow))) {
+ LOG(ERROR) << "could not create window";
+ return false;
+ }
+
+ reporter_tag_ = L"crash svc";
+ if (cmd_line.HasSwitch(kReporterTag))
+ reporter_tag_ = cmd_line.GetSwitchValue(kReporterTag);
+
+ // Log basic information.
+ LOG(INFO) << "pipe name is " << pipe_name;
+ LOG(INFO) << "dumps at " << dumps_path;
+ LOG(INFO) << "reports at " << report_path_;
+
+ if (sender_) {
+ LOG(INFO) << "checkpoint is " << checkpoint_path;
+ LOG(INFO) << "server is " << kCrashReportURL;
+ LOG(INFO) << "maximum " << sender_->max_reports_per_day() << " reports/day";
+ LOG(INFO) << "reporter is " << reporter_tag_;
+ }
+ // Start servicing clients.
+ if (!dumper_->Start()) {
+ LOG(ERROR) << "could not start dumper";
+ return false;
+ }
+
+ // This is throwaway code. We don't need to sync with the browser process
+ // once Google Update is updated to a version supporting OOP crash handling.
+ // Create or open an event to signal the browser process that the crash
+ // service is initialized.
+ HANDLE running_event =
+ ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc");
+ // If the browser already had the event open, the CreateEvent call did not
+ // signal it. We need to do it manually.
+ ::SetEvent(running_event);
+
+ return true;
+}
+
+void CrashService::OnClientConnected(void* context,
+ const google_breakpad::ClientInfo* client_info) {
+ ProcessingLock lock;
+ LOG(INFO) << "client start. pid = " << client_info->pid();
+ CrashService* self = static_cast<CrashService*>(context);
+ ::InterlockedIncrement(&self->clients_connected_);
+}
+
+void CrashService::OnClientExited(void* context,
+ const google_breakpad::ClientInfo* client_info) {
+ ProcessingLock lock;
+ LOG(INFO) << "client end. pid = " << client_info->pid();
+ CrashService* self = static_cast<CrashService*>(context);
+ ::InterlockedIncrement(&self->clients_terminated_);
+
+ if (!self->sender_)
+ return;
+
+ // When we are instructed to send reports we need to exit if there are
+ // no more clients to service. The next client that runs will start us.
+ // Only chrome.exe starts crash_service with a non-zero max_reports.
+ if (self->clients_connected_ > self->clients_terminated_)
+ return;
+ if (self->sender_->max_reports_per_day() > 0) {
+ // Wait for the other thread to send crashes, if applicable. The sender
+ // thread takes the sending_ lock, so the sleep is just to give it a
+ // chance to start.
+ ::Sleep(1000);
+ AutoLock lock(self->sending_);
+ // Some people can restart chrome very fast, check again if we have
+ // a new client before exiting for real.
+ if (self->clients_connected_ == self->clients_terminated_) {
+ LOG(INFO) << "zero clients. exiting";
+ ::PostMessage(g_top_window, WM_CLOSE, 0, 0);
+ }
+ }
+}
+
+void CrashService::OnClientDumpRequest(void* context,
+ const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path) {
+ ProcessingLock lock;
+
+ if (!file_path) {
+ LOG(ERROR) << "dump with no file path";
+ return;
+ }
+ if (!client_info) {
+ LOG(ERROR) << "dump with no client info";
+ return;
+ }
+
+ DWORD pid = client_info->pid();
+ LOG(INFO) << "dump for pid = " << pid << " is " << *file_path;
+
+ CrashService* self = static_cast<CrashService*>(context);
+ if (!self) {
+ LOG(ERROR) << "dump with no context";
+ return;
+ }
+
+ CrashMap map;
+ CustomInfoToMap(client_info, self->reporter_tag_, &map);
+
+ if (!WriteCustomInfoToFile(*file_path, map)) {
+ LOG(ERROR) << "could not write custom info file";
+ }
+
+ if (!self->sender_)
+ return;
+
+ // Send the crash dump using a worker thread. This operation has retry
+ // logic in case there is no internet connection at the time.
+ DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map, *file_path);
+ if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
+ dump_job, WT_EXECUTELONGFUNCTION)) {
+ LOG(ERROR) << "could not queue job";
+ }
+}
+
+// We are going to try sending the report several times. If we can't send,
+// we sleep from one minute to several hours depending on the retry round.
+unsigned long CrashService::AsyncSendDump(void* context) {
+ if (!context)
+ return 0;
+
+ DumpJobInfo* info = static_cast<DumpJobInfo*>(context);
+
+ std::wstring report_id = L"<unsent>";
+
+ const DWORD kOneMinute = 60*1000;
+ const DWORD kOneHour = 60*kOneMinute;
+
+ const DWORD kSleepSchedule[] = {
+ 24*kOneHour,
+ 8*kOneHour,
+ 4*kOneHour,
+ kOneHour,
+ 15*kOneMinute,
+ 0};
+
+ int retry_round = arraysize(kSleepSchedule) - 1;
+
+ do {
+ ::Sleep(kSleepSchedule[retry_round]);
+ {
+ // Take the server lock while sending. This also prevent early
+ // termination of the service object.
+ AutoLock lock(info->self->sending_);
+ LOG(INFO) << "trying to send report for pid = " << info->pid;
+ google_breakpad::ReportResult send_result
+ = info->self->sender_->SendCrashReport(kCrashReportURL, info->map,
+ info->dump_path, &report_id);
+ switch (send_result) {
+ case google_breakpad::RESULT_FAILED:
+ report_id = L"<network issue>";
+ break;
+ case google_breakpad::RESULT_REJECTED:
+ report_id = L"<rejected>";
+ ++info->self->requests_handled_;
+ retry_round = 0;
+ break;
+ case google_breakpad::RESULT_SUCCEEDED:
+ ++info->self->requests_sent_;
+ ++info->self->requests_handled_;
+ retry_round = 0;
+ break;
+ case google_breakpad::RESULT_THROTTLED:
+ report_id = L"<throttled>";
+ break;
+ default:
+ report_id = L"<unknown>";
+ break;
+ };
+ }
+
+ LOG(INFO) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
+ --retry_round;
+ } while(retry_round >= 0);
+
+ if (!::DeleteFileW(info->dump_path.c_str()))
+ LOG(WARNING) << "could not delete " << info->dump_path;
+
+ delete info;
+ return 0;
+}
+
+int CrashService::ProcessingLoop() {
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ LOG(INFO) << "session ending..";
+ while (ProcessingLock::IsWorking()) {
+ ::Sleep(50);
+ }
+
+ LOG(INFO) << "clients connected :" << clients_connected_;
+ LOG(INFO) << "clients terminated :" << clients_terminated_;
+ LOG(INFO) << "dumps serviced :" << requests_handled_;
+ LOG(INFO) << "dumps reported :" << requests_sent_;
+
+ return static_cast<int>(msg.wParam);
+}
diff --git a/chrome/tools/crash_service/crash_service.exe.manifest b/chrome/tools/crash_service/crash_service.exe.manifest
new file mode 100644
index 0000000..28469a3
--- /dev/null
+++ b/chrome/tools/crash_service/crash_service.exe.manifest
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
+ <ms_asmv2:security>
+ <ms_asmv2:requestedPrivileges>
+ <ms_asmv2:requestedExecutionLevel level="asInvoker">
+ </ms_asmv2:requestedExecutionLevel>
+ </ms_asmv2:requestedPrivileges>
+ </ms_asmv2:security>
+ </ms_asmv2:trustInfo>
+</assembly>
diff --git a/chrome/tools/crash_service/crash_service.h b/chrome/tools/crash_service/crash_service.h
new file mode 100644
index 0000000..d49f50c
--- /dev/null
+++ b/chrome/tools/crash_service/crash_service.h
@@ -0,0 +1,138 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifndef CHROME_TOOLS_CRASH_SERVICE__
+#define CHROME_TOOLS_CRASH_SERVICE__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+
+namespace google_breakpad {
+
+class CrashReportSender;
+class CrashGenerationServer;
+class ClientInfo;
+
+}
+
+// This class implements an out-of-process crash server. It uses breakpad's
+// CrashGenerationServer and CrashReportSender to generate and then send the
+// crash dumps. Internally, it uses OS specific pipe to allow applications to
+// register for crash dumps and later on when a registered application crashes
+// it will signal an event that causes this code to wake up and perform a
+// crash dump on the signaling process. The dump is then stored on disk and
+// possibly sent to the crash2 servers.
+class CrashService {
+ public:
+ // The ctor takes a directory that needs to be writable and will create
+ // a subdirectory inside to keep logs, crashes and checkpoint files.
+ CrashService(const std::wstring& report_dir);
+ ~CrashService();
+
+ // Starts servicing crash dumps. The command_line specifies various behaviors,
+ // see below for more information. Returns false if it failed. Do not use
+ // other members in that case.
+ bool Initialize(const std::wstring& command_line);
+
+ // Command line switches:
+ //
+ // --max-reports=<number>
+ // Allows to override the maximum number for reports per day. Normally
+ // the crash dumps are never sent so if you want to send any you must
+ // specify a positive number here.
+ static const wchar_t kMaxReports[];
+ // --no-window
+ // Does not create a visible window on the desktop. The window does not have
+ // any other functionality other than allowing the crash service to be
+ // gracefully closed.
+ static const wchar_t kNoWindow[];
+ // --reporter=<string>
+ // Allows to specify a custom string that appears on the detail crash report
+ // page in the crash server. This should be a 25 chars or less string.
+ // The default tag if not specified is 'crash svc'.
+ static const wchar_t kReporterTag[];
+
+ // Returns the actual report path.
+ std::wstring report_path() const {
+ return report_path_;
+ }
+ // Returns number of crash dumps handled.
+ int requests_handled() const {
+ return requests_handled_;
+ }
+ // Returns number of crash clients registered.
+ int clients_connected() const {
+ return clients_connected_;
+ }
+ // Returns number of crash clients terminated.
+ int clients_terminated() const {
+ return clients_terminated_;
+ }
+
+ // Starts the processing loop. This function does not return unless the
+ // user is logging off or the user closes the crash service window. The
+ // return value is a good number to pass in ExitProcess().
+ int ProcessingLoop();
+
+ private:
+ static void OnClientConnected(void* context,
+ const google_breakpad::ClientInfo* client_info);
+
+ static void OnClientDumpRequest(void* context,
+ const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path);
+
+ static void OnClientExited(void* context,
+ const google_breakpad::ClientInfo* client_info);
+
+ // This routine sends the crash dump to the server. It takes the sending_
+ // lock when it is performing the send.
+ static unsigned long __stdcall AsyncSendDump(void* context);
+
+ google_breakpad::CrashGenerationServer* dumper_;
+ google_breakpad::CrashReportSender* sender_;
+
+ // the path to dumps and logs directory.
+ std::wstring report_path_;
+ // the extra tag sent to the server with each dump.
+ std::wstring reporter_tag_;
+
+ // clients serviced statistics:
+ int requests_handled_;
+ int requests_sent_;
+ volatile long clients_connected_;
+ volatile long clients_terminated_;
+ Lock sending_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CrashService);
+};
+
+
+#endif // CHROME_TOOLS_CRASH_SERVICE__
diff --git a/chrome/tools/crash_service/crash_service.vcproj b/chrome/tools/crash_service/crash_service.vcproj
new file mode 100644
index 0000000..5cbc496
--- /dev/null
+++ b/chrome/tools/crash_service/crash_service.vcproj
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="crash_service"
+ ProjectGUID="{89C1C190-A5D1-4EC4-BD6A-67FF2195C7CC}"
+ RootNamespace="crash_service"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\breakpad\using_breakpad.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\breakpad\using_breakpad.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\crash_service.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\crash_service.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/tools/crash_service/main.cc b/chrome/tools/crash_service/main.cc
new file mode 100644
index 0000000..ab27ddd
--- /dev/null
+++ b/chrome/tools/crash_service/main.cc
@@ -0,0 +1,84 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/tools/crash_service/crash_service.h"
+
+#include <windows.h>
+#include <stdlib.h>
+#include <tchar.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+namespace {
+
+const wchar_t kStandardLogFile[] = L"operation_log.txt";
+
+bool GetCrashServiceDirectory(std::wstring* dir) {
+ std::wstring temp_dir;
+ if (!file_util::GetTempDir(&temp_dir))
+ return false;
+ file_util::AppendToPath(&temp_dir, L"chrome_crashes");
+ if (!file_util::PathExists(temp_dir)) {
+ if (!file_util::CreateDirectory(temp_dir))
+ return false;
+ }
+ *dir = temp_dir;
+ return true;
+}
+
+} // namespace.
+
+int __stdcall wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd_line,
+ int show_mode) {
+ // We use/create a directory under the user's temp folder, for logging.
+ std::wstring operating_dir;
+ GetCrashServiceDirectory(&operating_dir);
+ std::wstring log_file(operating_dir);
+ file_util::AppendToPath(&log_file, kStandardLogFile);
+
+ // Logging to a file with pid, tid and timestamp.
+ logging::InitLogging(log_file.c_str(), logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE, logging::APPEND_TO_OLD_LOG_FILE);
+ logging::SetLogItems(true, true, true, false);
+
+ LOG(INFO) << "session start. cmdline is [" << cmd_line << "]";
+
+ CrashService crash_service(operating_dir);
+ if (!crash_service.Initialize(::GetCommandLineW()))
+ return 1;
+
+ LOG(INFO) << "ready to process crash requests";
+
+ // Enter the message loop.
+ int retv = crash_service.ProcessingLoop();
+ // Time to exit.
+ LOG(INFO) << "session end. return code is " << retv;
+ return retv;
+}
diff --git a/chrome/tools/extract_actions.py b/chrome/tools/extract_actions.py
new file mode 100644
index 0000000..8eefbb6
--- /dev/null
+++ b/chrome/tools/extract_actions.py
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+# Copyright 2007 Google Inc. All rights reserved.
+
+"""Extract UserMetrics "actions" strings from the Chrome source.
+
+This program generates the list of known actions we expect to see in the
+user behavior logs. It walks the Chrome source, looking for calls to
+UserMetrics functions, extracting actions and warning on improper calls,
+as well as generating the lists of possible actions in situations where
+there are many possible actions.
+
+See also:
+ chrome/browser/user_metrics.h
+ http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUserExperienceMetrics
+
+Run it from the chrome/browser directory like:
+ extract_actions.py > actions_list
+"""
+
+__author__ = 'evanm (Evan Martin)'
+
+import os
+import re
+import sys
+
+from google import path_utils
+
+# Files that are known to use UserMetrics::RecordComputedAction(), which means
+# they require special handling code in this script.
+# To add a new file, add it to this list and add the appropriate logic to
+# generate the known actions to AddComputedActions() below.
+KNOWN_COMPUTED_USERS = [
+ 'back_forward_menu_model.cc',
+ 'options_page_view.cc',
+ 'render_view_host.cc', # called using webkit identifiers
+ 'user_metrics.cc', # method definition
+ 'new_tab_ui.cc', # most visited clicks 1-9
+]
+
+def AddComputedActions(actions):
+ """Add computed actions to the actions list.
+
+ Arguments:
+ actions: set of actions to add to.
+ """
+
+ # Actions for back_forward_menu_model.cc.
+ for dir in ['BackMenu_', 'ForwardMenu_']:
+ actions.add(dir + 'ShowFullHistory')
+ actions.add(dir + 'Popup')
+ for i in range(1, 20):
+ actions.add(dir + 'HistoryClick' + str(i))
+ actions.add(dir + 'ChapterClick' + str(i))
+
+ # Actions for new_tab_ui.cc.
+ for i in range(1, 10):
+ actions.add('MostVisited%d' % i)
+
+def AddWebKitEditorActions(actions):
+ """Add editor actions from editor_client_impl.cc.
+
+ Arguments:
+ actions: set of actions to add to.
+ """
+ action_re = re.compile(r'''\{ [\w']+, +\w+, +"(.*)" +\},''')
+
+ editor_file = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit',
+ 'glue', 'editor_client_impl.cc')
+ for line in open(editor_file):
+ match = action_re.search(line)
+ if match: # Plain call to RecordAction
+ actions.add(match.group(1))
+
+def GrepForActions(path, actions):
+ """Grep a source file for calls to UserMetrics functions.
+
+ Arguments:
+ path: path to the file
+ actions: set of actions to add to
+ """
+
+ action_re = re.compile(r'[> ]UserMetrics:?:?RecordAction\(L"(.*)"')
+ other_action_re = re.compile(r'[> ]UserMetrics:?:?RecordAction\(')
+ computed_action_re = re.compile(r'UserMetrics::RecordComputedAction')
+ for line in open(path):
+ match = action_re.search(line)
+ if match: # Plain call to RecordAction
+ actions.add(match.group(1))
+ elif other_action_re.search(line):
+ # Warn if this file shouldn't be mentioning RecordAction.
+ if os.path.basename(path) != 'user_metrics.cc':
+ print >>sys.stderr, 'WARNING: %s has funny RecordAction' % path
+ elif computed_action_re.search(line):
+ # Warn if this file shouldn't be calling RecordComputedAction.
+ if os.path.basename(path) not in KNOWN_COMPUTED_USERS:
+ print >>sys.stderr, 'WARNING: %s has RecordComputedAction' % path
+
+def WalkDirectory(root_path, actions):
+ for path, dirs, files in os.walk(root_path):
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ for file in files:
+ ext = os.path.splitext(file)[1]
+ if ext == '.cc':
+ GrepForActions(os.path.join(path, file), actions)
+
+def main(argv):
+ actions = set()
+ AddComputedActions(actions)
+ AddWebKitEditorActions(actions)
+
+ # Walk the source tree to process all .cc files.
+ chrome_root = os.path.join(path_utils.ScriptDir(), '..')
+ WalkDirectory(chrome_root, actions)
+ webkit_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit')
+ WalkDirectory(os.path.join(webkit_root, 'glue'), actions)
+ WalkDirectory(os.path.join(webkit_root, 'port'), actions)
+
+ # Print out the actions as a sorted list.
+ for action in sorted(actions):
+ print action
+
+if '__main__' == __name__:
+ main(sys.argv)
diff --git a/chrome/tools/extract_actions.sh b/chrome/tools/extract_actions.sh
new file mode 100755
index 0000000..a5316d4
--- /dev/null
+++ b/chrome/tools/extract_actions.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+system_root=`cygpath "$SYSTEMROOT"`
+export PATH="/usr/bin:$system_root/system32:$system_root:$system_root/system32/WBEM"
+
+exec_dir=$(dirname $0)
+
+"$exec_dir/../../third_party/python_24/python.exe" \
+ "$exec_dir/extract_actions.py" "$@"
diff --git a/chrome/tools/extract_histograms.py b/chrome/tools/extract_histograms.py
new file mode 100644
index 0000000..97008a2
--- /dev/null
+++ b/chrome/tools/extract_histograms.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+# Copyright 2007 Google Inc. All rights reserved.
+
+"""Extract UMA histogram strings from the Chrome source.
+
+This program generates the list of known histograms we expect to see in
+the user behavior logs. It walks the Chrome source, looking for calls
+to UMA histogram macros.
+
+Run it from the chrome/browser directory like:
+ extract_histograms.py > histogram_list
+"""
+
+# TODO(evanm): get all the jankometer histogram names.
+
+__author__ = 'evanm (Evan Martin)'
+
+import os
+import re
+import sys
+
+def GrepForHistograms(path, histograms):
+ """Grep a source file for calls to histogram macros functions.
+
+ Arguments:
+ path: path to the file
+ histograms: set of histograms to add to
+ """
+
+ histogram_re = re.compile(r'HISTOGRAM_\w+\(L"(.*)"')
+ for line in open(path):
+ match = histogram_re.search(line)
+ if match:
+ histograms.add(match.group(1))
+
+def WalkDirectory(root_path, histograms):
+ for path, dirs, files in os.walk(root_path):
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ for file in files:
+ ext = os.path.splitext(file)[1]
+ if ext == '.cc':
+ GrepForHistograms(os.path.join(path, file), histograms)
+
+def main(argv):
+ histograms = set()
+
+ # Walk the source tree to process all .cc files.
+ WalkDirectory('..', histograms)
+
+ # Print out the histograms as a sorted list.
+ for histogram in sorted(histograms):
+ print histogram
+
+if '__main__' == __name__:
+ main(sys.argv)
diff --git a/chrome/tools/history-viz.py b/chrome/tools/history-viz.py
new file mode 100644
index 0000000..2f94d9a
--- /dev/null
+++ b/chrome/tools/history-viz.py
@@ -0,0 +1,240 @@
+#!/usr/bin/python
+#
+# Copyright 2008 Google Inc. All Rights Reserved.
+
+"""Process a History database and dump a .dot file suitable for GraphViz.
+
+This is useful for debugging history redirect flows.
+
+An example run of this program:
+ python /src/history-viz.py History > foo.dot
+ /c/Program\ Files/Graphviz2.18/bin/dot -Tpng foo.dot -o foo.png
+"""
+
+import struct
+import subprocess
+import sys
+import urlparse
+
+class URL:
+ """Represents a broken-down URL from our most visited database."""
+
+ def __init__(self, id, url):
+ """Initialize a new URL object. |id| is the database id of the URL."""
+ self.id = id
+ self.url = url
+ scheme, loc, path, query, fragment = urlparse.urlsplit(url)
+ if scheme == 'http':
+ scheme = '' # Shorten for display purposes.
+ if len(scheme) > 0:
+ scheme += '://'
+ self.host = scheme + loc
+ self.path = path
+
+ extra = ''
+ if len(query) > 0:
+ extra += '?' + query
+ if len(fragment) > 0 or url.find('#') > 0:
+ extra += '#' + fragment
+ self.extra = extra
+
+ def PrettyPrint(self, include_host=True, include_path=True):
+ """Pretty-print this URL in a form more suitable for the graph.
+
+ This will elide very long paths and potentially puts newlines between parts
+ of long components. include_host and include_path determine whether to
+ include the host and path in the output.
+
+ Returns: the pretty-printed string."""
+ MAX_LEN = 30 # Maximum length of a line in the output.
+ parts = []
+ if include_host:
+ parts.append(self.host)
+ if include_path:
+ parts.append(self.path)
+ parts.append(self.extra)
+ lines = []
+ line = ''
+ for part in parts:
+ if len(part) > MAX_LEN:
+ part = part[0:MAX_LEN-3] + '...'
+ if len(line)+len(part) > MAX_LEN:
+ lines.append(line)
+ line = ''
+ line += part
+ if len(line) > 0:
+ lines.append(line)
+ return '\n'.join(lines)
+
+class Edge:
+ """Represents an edge in the history graph, connecting two pages.
+
+ If a link is traversed twice, it is one Edge with two entries in
+ the .transitions array."""
+ def __init__(self, src, dst):
+ self.src = src
+ self.dst = dst
+ self.transitions = []
+
+ def Transitions(self):
+ """Return a dictionary mapping transition type -> occurences."""
+ all = {}
+ for trans in self.transitions:
+ all[trans] = all.get(trans, 0) + 1
+ # We currently don't use the chain type.
+ # TODO(evanm): make this a command-line option.
+ # if trans & 0x30000000 != 0:
+ # chain = ''
+ # if trans & 0x10000000:
+ # chain = 'start'
+ # if trans & 0x20000000:
+ # if len(chain) == 0:
+ # chain = 'end'
+ # else:
+ # chain = ''
+ # if len(chain) > 0:
+ # edge['chain'] = chain
+ return all
+
+def ClusterBy(objs, pred):
+ """Group a list of objects by a predicate.
+
+ Given a list of objects and a predicate over the objects, return a
+ dictionary mapping pred(obj) -> all objs with the same pred(obj)."""
+ clusters = {}
+ for obj in objs:
+ cluster = pred(obj)
+ clusters[cluster] = clusters.get(cluster, [])
+ clusters[cluster].append(obj)
+ return clusters
+
+def EscapeDot(str):
+ """Escape a string suitable for embedding in a graphviz graph."""
+ # TODO(evanm): this is likely not sufficient.
+ return str.replace('\n', '\\n')
+
+class SQLite:
+ """Trivial interface to executing SQLite queries.
+ Spawns a new process with each call."""
+ def __init__(self, file=None):
+ self.file = file
+
+ def Run(self, sql):
+ """Execute |sql|, yielding each row of results as an array."""
+ subproc = subprocess.Popen(['sqlite', self.file],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ subproc.stdin.write('.mode tabs\n')
+ subproc.stdin.write(sql + ';')
+ subproc.stdin.close()
+ for line in subproc.stdout:
+ row = line.strip().split('\t')
+ yield row
+
+def LoadHistory(filename):
+ db = SQLite(filename)
+
+ urls = {} # Map of urlid => url.
+ urls['0'] = URL('0', 'start') # Node name '0' is our special 'start' node.
+ for id, url in db.Run('SELECT id, url FROM urls'):
+ urls[id] = URL(id, url)
+
+ visiturlids = {} # Map of visitid => urlid.
+ visiturlids['0'] = '0' # '0' is our special 'start' node.
+ edges = {} # Map of urlid->urlid->Edge.
+ for src, dst, url, trans in db.Run('SELECT from_visit, id, url, transition '
+ 'FROM visits ORDER BY id'):
+ visiturlids[dst] = url
+ src = visiturlids[src]
+ dst = visiturlids[dst]
+ edges[src] = edges.get(src, {})
+ edge = edges[src][dst] = edges[src].get(dst, Edge(src, dst))
+ # SQLite outputs transition values as signed integers, but they're really
+ # a bitfield. Below does "unsigned trans = static_cast<unsigned>(trans)".
+ trans = struct.unpack('I', struct.pack('i', int(trans)))[0]
+ edge.transitions.append(trans)
+
+ return urls, edges
+
+# Some transition types, copied from page_transition_types.h.
+TRANS_TYPES = {
+ 0: 'link',
+ 1: 'typed',
+ 2: 'most-visited',
+ 3: 'auto subframe',
+ 7: 'form',
+}
+
+urls, edges = LoadHistory(sys.argv[1])
+
+print 'digraph G {'
+print ' graph [rankdir=LR]' # Display left to right.
+print ' node [shape=box]' # Display nodes as boxes.
+print ' subgraph { rank=source; 0 [label="start"] }'
+
+# Output all the nodes within graph clusters.
+hosts = ClusterBy(urls.values(), lambda url: url.host)
+for i, (host, urls) in enumerate(hosts.items()):
+ # Cluster all URLs under this host if it has more than one entry.
+ host_clustered = len(urls) > 1
+ if host_clustered:
+ print 'subgraph clusterhost%d {' % i
+ print ' label="%s"' % host
+ paths = ClusterBy(urls, lambda url: url.path)
+ for j, (path, urls) in enumerate(paths.items()):
+ # Cluster all URLs under this host if it has more than one entry.
+ path_clustered = host_clustered and len(urls) > 1
+ if path_clustered:
+ print ' subgraph cluster%d%d {' % (i, j)
+ print ' label="%s"' % path
+ for url in urls:
+ if url.id == '0': continue # We already output the special start node.
+ pretty = url.PrettyPrint(include_host=not host_clustered,
+ include_path=not path_clustered)
+ print ' %s [label="%s"]' % (url.id, EscapeDot(pretty))
+ if path_clustered:
+ print ' }'
+ if host_clustered:
+ print '}'
+
+# Output all the edges between nodes.
+for src, dsts in edges.items():
+ for dst, edge in dsts.items():
+ # Gather up all the transitions into the label.
+ label = [] # Label for the edge.
+ transitions = edge.Transitions()
+ for trans, count in transitions.items():
+ text = ''
+ if count > 1:
+ text = '%dx ' % count
+ base_type = trans & 0xFF
+ redir = (trans & 0xC0000000) != 0
+ start = (trans & 0x10000000) != 0
+ end = (trans & 0x20000000) != 0
+ if start or end:
+ if start:
+ text += '<'
+ if end:
+ text += '>'
+ text += ' '
+ if redir:
+ text += 'R '
+ text += TRANS_TYPES.get(base_type, 'trans%d' % base_type)
+ label.append(text)
+ if len(label) == 0:
+ continue
+
+ edgeattrs = [] # Graphviz attributes for the edge.
+ # If the edge is from the start and the transitions are fishy, make it
+ # display as a dotted line.
+ if src == '0' and len(transitions.keys()) == 1 and transitions.has_key(0):
+ edgeattrs.append('style=dashed')
+ if len(label) > 0:
+ edgeattrs.append('label="%s"' % EscapeDot('\n'.join(label)))
+
+ out = '%s -> %s' % (src, dst)
+ if len(edgeattrs) > 0:
+ out += ' [%s]' % ','.join(edgeattrs)
+ print out
+print '}'
+
diff --git a/chrome/tools/icudt38.dll b/chrome/tools/icudt38.dll
new file mode 100644
index 0000000..b5b40f5
--- /dev/null
+++ b/chrome/tools/icudt38.dll
Binary files differ
diff --git a/chrome/tools/inconsistent-eol.py b/chrome/tools/inconsistent-eol.py
new file mode 100644
index 0000000..9f8ef64
--- /dev/null
+++ b/chrome/tools/inconsistent-eol.py
@@ -0,0 +1,130 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Find and fix files with inconsistent line endings.
+
+This script requires 'dos2unix.exe' and 'unix2dos.exe' from Cygwin; they
+must be in the user's PATH.
+
+Arg: a file containing a list of relative or absolute file paths. The
+ argument passed to this script, as well as the paths in the file, may be
+ relative paths or absolute Windows-style paths (with either type of
+ slash). The list might be generated with 'find -type f' or extracted from
+ a gvn changebranch listing, for example.
+"""
+
+import errno
+import logging
+import subprocess
+import sys
+
+
+# Whether to produce excessive debugging output for each file in the list.
+DEBUGGING = False
+
+
+class Error(Exception):
+ """Local exception class."""
+ pass
+
+
+def CountChars(text, str):
+ """Count the number of instances of the given string in the text."""
+ split = text.split(str)
+ logging.debug(len(split) - 1)
+ return len(split) - 1
+
+def PrevailingEOLName(crlf, cr, lf):
+ """Describe the most common line ending.
+
+ Args:
+ crlf: How many CRLF (\r\n) sequences are in the file.
+ cr: How many CR (\r) characters are in the file, excluding CRLF sequences.
+ lf: How many LF (\n) characters are in the file, excluding CRLF sequences.
+
+ Returns:
+ A string describing the most common of the three line endings.
+ """
+ most = max(crlf, cr, lf)
+ if most == cr:
+ return 'cr'
+ if most == crlf:
+ return 'crlf'
+ return 'lf'
+
+def FixEndings(file, crlf, cr, lf):
+ """Change the file's line endings to CRLF or LF, whichever is more common."""
+ most = max(crlf, cr, lf)
+ if most == crlf:
+ result = subprocess.call('unix2dos.exe %s' % file, shell=True)
+ if result:
+ raise Error('Error running unix2dos.exe %s' % file)
+ else:
+ result = subprocess.call('dos2unix.exe %s' % file, shell=True)
+ if result:
+ raise Error('Error running dos2unix.exe %s' % file)
+
+
+def main(argv=None):
+ """Process the list of files."""
+ if not argv or len(argv) < 2:
+ raise Error('No file list given.')
+
+ for filename in open(argv[1], 'r'):
+ filename = filename.strip()
+ logging.debug(filename)
+ try:
+ # Open in binary mode to preserve existing line endings.
+ text = open(filename, 'rb').read()
+ except IOError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ logging.warning('File %s not found.' % filename)
+ continue
+ crlf = CountChars(text, '\r\n')
+ cr = CountChars(text, '\r') - crlf
+ lf = CountChars(text, '\n') - crlf
+
+ if ((crlf > 0 and cr > 0) or
+ (crlf > 0 and lf > 0) or
+ ( lf > 0 and cr > 0)):
+ print ('%s: mostly %s' % (filename, PrevailingEOLName(crlf, cr, lf)))
+ FixEndings(filename, crlf, cr, lf)
+
+if '__main__' == __name__:
+ if DEBUGGING:
+ debug_level = logging.DEBUG
+ else:
+ debug_level = logging.INFO
+ logging.basicConfig(level=debug_level,
+ format='%(asctime)s %(levelname)-7s: %(message)s',
+ datefmt='%H:%M:%S')
+
+ sys.exit(main(sys.argv))
diff --git a/chrome/tools/optipng.exe b/chrome/tools/optipng.exe
new file mode 100644
index 0000000..85c993a
--- /dev/null
+++ b/chrome/tools/optipng.exe
Binary files differ
diff --git a/chrome/tools/profiles/generate_profile.cc b/chrome/tools/profiles/generate_profile.cc
new file mode 100644
index 0000000..43927b67
--- /dev/null
+++ b/chrome/tools/profiles/generate_profile.cc
@@ -0,0 +1,238 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This program generates a user profile and history by randomly generating
+// data and feeding it to the history service.
+
+#include "chrome/tools/profiles/thumbnail-inl.h"
+
+#include "base/icu_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/common/jpeg_codec.h"
+#include "chrome/common/thumbnail_score.h"
+#include "SkBitmap.h"
+
+// Probabilities of different word lengths, as measured from Darin's profile.
+// kWordLengthProbabilities[n-1] = P(word of length n)
+const float kWordLengthProbabilities[] = { 0.069f, 0.132f, 0.199f,
+ 0.137f, 0.088f, 0.115f, 0.081f, 0.055f, 0.034f, 0.021f, 0.019f, 0.018f,
+ 0.007f, 0.007f, 0.005f, 0.004f, 0.003f, 0.003f, 0.003f };
+
+// Return a float uniformly in [0,1].
+// Useful for making probabilistic decisions.
+float RandomFloat() {
+ return rand() / static_cast<float>(RAND_MAX);
+}
+
+// Return an integer uniformly in [min,max).
+int RandomInt(int min, int max) {
+ return min + (rand() % (max-min));
+}
+
+// Return a string of |count| lowercase random characters.
+std::wstring RandomChars(int count) {
+ std::wstring str;
+ for (int i = 0; i < count; ++i)
+ str += L'a' + rand() % 26;
+ return str;
+}
+
+std::wstring RandomWord() {
+ // TODO(evanm): should we instead use the markov chain based
+ // version of this that I already wrote?
+
+ // Sample a word length from kWordLengthProbabilities.
+ float sample = RandomFloat();
+ int i;
+ for (i = 0; i < arraysize(kWordLengthProbabilities); ++i) {
+ sample -= kWordLengthProbabilities[i];
+ if (sample < 0) break;
+ }
+ const int word_length = i + 1;
+ return RandomChars(word_length);
+}
+
+// Return a string of |count| random words.
+std::wstring RandomWords(int count) {
+ std::wstring str;
+ for (int i = 0; i < count; ++i) {
+ if (!str.empty())
+ str += L' ';
+ str += RandomWord();
+ }
+ return str;
+}
+
+// Return a random URL-looking string.
+GURL ConstructRandomURL() {
+ return GURL(std::wstring(L"http://") + RandomChars(3) + L".com/" +
+ RandomChars(RandomInt(5,20)));
+}
+
+// Return a random page title-looking string.
+std::wstring ConstructRandomTitle() {
+ return RandomWords(RandomInt(3, 15));
+}
+
+// Return a random string that could function as page contents.
+std::wstring ConstructRandomPage() {
+ return RandomWords(RandomInt(10, 4000));
+}
+
+// Insert a batch of |batch_size| URLs, starting at pageid |page_id|.
+// When history_only is set, we will not generate thumbnail or full text data.
+void InsertURLBatch(const std::wstring& profile_dir, int page_id,
+ int batch_size, bool history_only) {
+ scoped_refptr<HistoryService> history_service(new HistoryService);
+ if (!history_service->Init(profile_dir)) {
+ printf("Could not init the history service\n");
+ exit(1);
+ }
+
+ // Probability of following a link on the current "page"
+ // (vs randomly jumping to a new page).
+ const float kFollowLinkProbability = 0.85f;
+ // Probability of visiting a page we've visited before.
+ const float kRevisitLinkProbability = 0.1f;
+ // Probability of a URL being "good enough" to revisit.
+ const float kRevisitableURLProbability = 0.05f;
+ // Probability of a URL being the end of a redirect chain.
+ const float kRedirectProbability = 0.05f;
+
+ // A list of URLs that we sometimes revisit.
+ std::vector<GURL> revisit_urls;
+
+ // Scoping value for page IDs (required by the history service).
+ void* id_scope = reinterpret_cast<void*>(1);
+
+ printf("Inserting %d URLs...\n", batch_size);
+ GURL previous_url;
+ PageTransition::Type transition = PageTransition::TYPED;
+ const int end_page_id = page_id + batch_size;
+ for (; page_id < end_page_id; ++page_id) {
+ // Randomly decide whether this new URL simulates following a link or whether
+ // it's a jump to a new URL.
+ if (!previous_url.is_empty() && RandomFloat() < kFollowLinkProbability) {
+ transition = PageTransition::LINK;
+ } else {
+ previous_url = GURL();
+ transition = PageTransition::TYPED;
+ }
+
+ // Pick a URL, either newly at random or from our list of previously
+ // visited URLs.
+ GURL url;
+ if (!revisit_urls.empty() && RandomFloat() < kRevisitLinkProbability) {
+ // Draw a URL from revisit_urls at random.
+ url = revisit_urls[RandomInt(0, static_cast<int>(revisit_urls.size()))];
+ } else {
+ url = ConstructRandomURL();
+ }
+
+ // Randomly construct a redirect chain.
+ HistoryService::RedirectList redirects;
+ if (RandomFloat() < kRedirectProbability) {
+ const int redir_count = RandomInt(1, 4);
+ for (int i = 0; i < redir_count; ++i)
+ redirects.push_back(ConstructRandomURL());
+ redirects.push_back(url);
+ }
+
+ // Add all of this information to the history service.
+ history_service->AddPage(url,
+ id_scope, page_id,
+ previous_url, transition,
+ redirects);
+ ThumbnailScore score(0.75, false, false);
+ history_service->SetPageTitle(url, ConstructRandomTitle());
+ if (!history_only) {
+ history_service->SetPageContents(url, ConstructRandomPage());
+ if (RandomInt(0, 2) == 0) {
+ scoped_ptr<SkBitmap> bitmap(
+ JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
+ history_service->SetPageThumbnail(url, *bitmap, score);
+ } else {
+ scoped_ptr<SkBitmap> bitmap(
+ JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail)));
+ history_service->SetPageThumbnail(url, *bitmap, score);
+ }
+ }
+
+ previous_url = url;
+
+ if (revisit_urls.empty() || RandomFloat() < kRevisitableURLProbability)
+ revisit_urls.push_back(url);
+ }
+
+ printf("Letting the history service catch up...\n");
+ history_service->SetOnBackendDestroyTask(new MessageLoop::QuitTask);
+ history_service->Cleanup();
+ history_service = NULL;
+ MessageLoop::current()->Run();
+}
+
+int main(int argc, const char* argv[]) {
+ int next_arg = 1;
+ bool history_only = false;
+ if (strcmp(argv[next_arg], "--history-only") == 0) {
+ history_only = true;
+ next_arg++;
+ }
+
+ // We require two arguments: urlcount and profiledir.
+ if (argc - next_arg < 2) {
+ printf("usage: %s [--history-only] <urlcount> <profiledir>\n", argv[0]);
+ printf("\n --history-only Generate only history, not thumbnails or full");
+ printf("\n text index data.\n\n");
+ return -1;
+ }
+
+ const int url_count = atoi(argv[next_arg++]);
+ std::wstring profile_dir = UTF8ToWide(argv[next_arg++]);
+
+ MessageLoop main_message_loop;
+
+ srand(static_cast<unsigned int>(Time::Now().ToInternalValue()));
+ icu_util::Initialize();
+
+ // The maximum number of URLs to insert into history in one batch.
+ const int kBatchSize = 2000;
+ int page_id = 0;
+ while (page_id < url_count) {
+ const int batch_size = std::min(kBatchSize, url_count - page_id);
+ InsertURLBatch(profile_dir, page_id, batch_size, history_only);
+ page_id += batch_size;
+ }
+
+ return 0;
+}
diff --git a/chrome/tools/profiles/generate_profile.vcproj b/chrome/tools/profiles/generate_profile.vcproj
new file mode 100644
index 0000000..078e4c1
--- /dev/null
+++ b/chrome/tools/profiles/generate_profile.vcproj
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="generate_profile"
+ ProjectGUID="{2E969AE9-7B12-4EDB-8E8B-48C7AE7BE357}"
+ RootNamespace="generate_profile"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;..\..\..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="PERF_TEST"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib rpcrt4.lib winmm.lib"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;..\..\..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="PERF_TEST"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib rpcrt4.lib winmm.lib"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\generate_profile.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\thumbnail-inl.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/tools/profiles/thumbnail-inl.h b/chrome/tools/profiles/thumbnail-inl.h
new file mode 100644
index 0000000..18ec4a8
--- /dev/null
+++ b/chrome/tools/profiles/thumbnail-inl.h
@@ -0,0 +1,807 @@
+const unsigned char kGoogleThumbnail[] =
+"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00"
+"\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03"
+"\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a"
+"\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15"
+"\x15\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03"
+"\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\x14\xff\xc0\x00\x11\x08\x00\x88\x00\xc4\x03\x01\x22\x00"
+"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01"
+"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+"\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00"
+"\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06\x13\x51\x61\x07"
+"\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62"
+"\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37"
+"\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a"
+"\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79\x7a\x83\x84\x85"
+"\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6"
+"\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7"
+"\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+"\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00"
+"\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03"
+"\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04"
+"\x03\x04\x07\x05\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31"
+"\x06\x12\x41\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09"
+"\x23\x33\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a"
+"\x26\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
+"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
+"\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
+"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7"
+"\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
+"\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9"
+"\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\xfd\x26\x9b\xc1"
+"\x57\xf2\x78\x99\xf5\x64\xf1\x46\xb3\x14\x0d\x32\x4b\xfd\x98\xb2\xc4\x6d\x80"
+"\x50\x99\x40\x0c\x65\x82\x9d\xac\x4f\xcd\x9f\x9c\xf2\x00\x18\x87\x50\xf0\x6e"
+"\xbd\x75\xa8\x49\x3d\xbf\x8b\xaf\x6c\xe1\x79\x1d\xc5\xba\xc7\x1b\x2a\x82\x72"
+"\x14\x12\x33\x81\xd3\xe9\xf9\xd7\x67\x45\x6d\x4e\xac\xa9\x3b\xc6\xdf\x34\x9f"
+"\xe6\x26\xae\x72\xfa\xdf\x86\x35\x6d\x52\x78\x24\xb5\xf1\x15\xde\x98\x23\x8d"
+"\x11\x96\x15\x56\x0e\x54\xe4\xb1\x04\x63\x27\xa1\xe3\x18\xa6\xeb\x9e\x15\xd6"
+"\x35\x4b\x98\x25\xb4\xf1\x2d\xe6\x96\x12\x21\x1b\xc7\x02\xa3\x2c\x87\x8f\x98"
+"\xee\x53\x83\xc7\x6f\x53\x5d\x55\x15\x6b\x11\x38\xda\xd6\xd3\xc9\x7f\x90\xac"
+"\x72\x5a\xd7\x84\x75\x9d\x46\xfd\x6e\x6d\x3c\x4f\x7b\xa6\xa8\x45\x46\x82\x25"
+"\x56\x8d\x88\x18\x27\x04\x70\x4f\x5e\x2a\x4d\x6b\xc2\xfa\xbe\xa5\x72\x65\xb6"
+"\xf1\x25\xde\x9c\x86\x15\x8b\xcb\x85\x51\x86\xe1\x9c\xbf\x2b\xd4\xe7\xe9\xc7"
+"\x4a\xea\x68\xa1\x62\x2a\x2b\x6d\xa7\x92\xff\x00\x20\xe5\x47\x35\xaa\x78\x6f"
+"\x56\xbe\xb8\x8e\x4b\x7f\x10\xdd\x58\xa2\xc6\xa8\x63\x8d\x51\x83\x11\xd5\xb9"
+"\x1d\x4d\x6d\xd9\x5b\x4b\x67\x01\x8d\xa5\x7b\x83\xbd\xdf\x7c\xcf\x96\xf9\x98"
+"\xb6\xde\x00\xe0\x67\x03\xd8\x0e\xbd\x6a\xd5\x15\x9c\xaa\x4a\x49\x45\xf4\xf2"
+"\x43\xb5\x86\x66\x4f\xee\xaf\xfd\xf5\xff\x00\xd6\xa3\x32\x7f\x75\x7f\xef\xaf"
+"\xfe\xb5\x3e\x8a\xcc\x63\x33\x27\xf7\x57\xfe\xfa\xff\x00\xeb\x51\x99\x3f\xba"
+"\xbf\xf7\xd7\xff\x00\x5a\x9f\x45\x00\x33\x32\x7f\x75\x7f\xef\xaf\xfe\xb5\x19"
+"\x93\xfb\xab\xff\x00\x7d\x7f\xf5\xab\x90\xf8\xbb\xf1\x1a\x0f\x85\x3f\x0f\xf5"
+"\x4f\x12\x4d\x01\xbb\x7b\x60\x91\xc1\x6c\x1b\x6f\x9b\x33\xb8\x48\xd7\x3d\x81"
+"\x66\x19\x3e\x99\xac\x9b\xcf\x0b\x7c\x44\xb8\xf0\xfc\x33\xda\x78\xe2\x3b\x6d"
+"\x77\x0b\x23\xc1\x26\x9b\x03\xd9\x13\xc1\x64\xc6\xdf\x33\x1d\x46\x77\x66\xb7"
+"\x8d\x26\xe2\xa7\x26\x92\x6e\xda\xdf\xa6\xfb\x5f\xba\x33\x73\xd5\xc5\x6a\xd1"
+"\xe8\xb9\x93\xfb\xab\xff\x00\x7d\x7f\xf5\xa8\xcc\x9f\xdd\x5f\xfb\xeb\xff\x00"
+"\xad\x5c\x37\x89\xbe\x33\xf8\x77\xc2\x4f\x7e\x2f\x5a\xea\xe2\xdf\x4b\x64\x8f"
+"\x54\xbd\xb1\xb7\x33\x41\x60\xcc\x01\xfd\xf1\x5e\x57\x00\x86\x38\x07\x68\x20"
+"\x9c\x66\xb5\xf5\x3f\x88\x5a\x3e\x99\x2c\x88\x5e\x6b\xb5\x86\x24\x9e\x79\x6c"
+"\xe2\x33\x24\x31\xb8\xca\xb3\x15\xec\x47\x3c\x67\x8e\x7a\x11\x5e\x7d\x4c\x4d"
+"\x1a\x49\xb9\xc9\x2b\x7f\xc1\xff\x00\x27\xf7\x3e\xc7\x63\xc3\x56\x49\x49\xc5"
+"\xd9\xed\xf8\x7f\x9a\xfb\xd7\x73\xa2\xcc\x9f\xdd\x5f\xfb\xeb\xff\x00\xad\x46"
+"\x64\xfe\xea\xff\x00\xdf\x5f\xfd\x6a\xf3\xff\x00\x19\xfc\x77\xf0\x8f\x81\xec"
+"\x65\xbc\xbc\xbc\x96\xf2\xd2\x04\x85\xe7\xb8\xb0\x8f\xce\x8a\x15\x97\xfd\x51"
+"\x91\xc1\xda\x81\xb8\x39\x24\x00\x08\x24\x80\x46\x79\xff\x00\x0c\x7c\x55\x96"
+"\xcb\xc6\x3e\x3e\x8b\x59\xbd\xb8\xbb\xb4\xb7\xd5\x2d\x2c\xb4\x9d\x3d\x62\x4f"
+"\x3b\x74\x96\x8b\x33\x46\xa0\x63\x27\x97\x62\x58\xf0\x14\xf3\xc5\x74\xd4\xbd"
+"\x28\xc2\x72\x5a\x4b\x67\xf2\xbf\xf9\x7d\xe8\xe3\x53\x52\xa8\xa9\x47\x56\xff"
+"\x00\xc9\xbf\xd1\x9e\xc1\x99\x3f\xba\xbf\xf7\xd7\xff\x00\x5a\x8c\xc9\xfd\xd5"
+"\xff\x00\xbe\xbf\xfa\xd5\xce\x78\x1b\xe2\x0e\x97\xf1\x0f\x4e\x37\xda\x38\xb8"
+"\x7b\x65\x79\x21\x95\xa6\x8f\x61\x8a\x64\x6d\xaf\x13\x02\x72\x18\x11\x9f\x42"
+"\x08\x20\x9c\xd7\x4d\x52\xa4\xa4\xae\xb6\x37\x9c\x25\x4e\x4e\x13\x56\x68\x66"
+"\x64\xfe\xea\xff\x00\xdf\x5f\xfd\x6a\x33\x27\xf7\x57\xfe\xfa\xff\x00\xeb\x53"
+"\xe8\xaa\x20\x66\x64\xfe\xea\xff\x00\xdf\x5f\xfd\x6a\xe7\xfc\x4b\xe1\x9d\x47"
+"\x5e\x9a\x27\xb5\xf1\x16\xa5\xa1\x84\x00\x14\xb0\x30\x95\x7c\x12\x79\xf3\x22"
+"\x63\xdc\x74\x23\xa0\xf7\xcf\x47\x45\x20\x38\x99\x3c\x07\xad\x3b\x96\x4f\x1c"
+"\x6b\xb1\x03\x8f\x95\x45\xa9\x1d\x31\xfc\x56\xe6\x8a\xed\xa8\xa7\x71\x58\x28"
+"\xa2\x8a\x43\x0a\x28\xa2\x80\x0a\x28\xa2\x80\x0a\x28\xa2\x80\x0a\x28\xa2\x80"
+"\x0a\x2a\x87\xfc\x24\x1a\x5e\x58\x7f\x69\x5a\x65\x49\x52\x3c\xf5\xe0\x83\x82"
+"\x3a\xf5\x06\x8f\xed\xfd\x2f\xfe\x82\x56\x9f\xf7\xfd\x7f\xc6\xa7\x99\x77\x1d"
+"\x99\xcd\x7c\x61\xf8\x63\x65\xf1\x83\xe1\xe6\xad\xe1\x5b\xdb\x89\x2c\xd6\xf1"
+"\x55\xa2\xba\x88\x65\xe0\x95\x18\x3c\x6e\x07\x7c\x32\x8c\x8e\xe3\x23\x8c\xd7"
+"\x09\xae\x69\x5f\x17\xbc\x43\xe0\x3b\xaf\x0f\x4f\x6d\x61\xa7\x6b\xc9\x6c\xd0"
+"\xdb\x78\x97\x48\xd6\xa4\x85\x1e\x5d\xa5\x56\x66\x87\xcb\xdc\x3f\xbc\x53\x71"
+"\x19\xef\x5e\xc1\xfd\xbf\xa5\xff\x00\xd0\x4a\xd3\xfe\xff\x00\xaf\xf8\xd1\xfd"
+"\xbf\xa5\xff\x00\xd0\x4a\xd3\xfe\xff\x00\xaf\xf8\xd7\x5d\x3c\x5c\xa9\xc5\x42"
+"\xe9\xa4\xee\xaf\xd1\xe9\xf9\xd9\x5d\x6c\x63\x2a\x2a\x4d\xcb\xbe\x87\x8c\x69"
+"\x7f\x06\xbc\x53\xe1\x7b\x5f\x1e\xe9\x56\x37\x76\x5a\xa6\x9d\xe3\x3d\xd7\x13"
+"\xdc\xdf\xbb\x79\x96\x57\x12\x5b\xac\x12\xe5\x71\xfb\xd4\x21\x03\x0e\x54\xf6"
+"\x39\xeb\x5d\x1e\x83\xf0\xe3\x59\xf0\x14\x37\x9a\x76\x88\x6d\xb5\x1d\x32\xf3"
+"\x4f\xb6\xb4\x06\xf2\x42\xaf\x04\x91\x40\x21\xde\x46\x0e\xf5\x65\x0a\x71\x90"
+"\x46\x2b\xd1\x3f\xb7\xf4\xbf\xfa\x09\x5a\x7f\xdf\xf5\xff\x00\x1a\x3f\xb7\xf4"
+"\xbf\xfa\x09\x5a\x7f\xdf\xf5\xff\x00\x1a\xf1\xea\x61\x68\x54\x49\x4b\xa7\x9f"
+"\x74\xd3\xfc\x1b\xfe\x92\x3d\x3a\x98\xba\xf5\x7e\x27\xf8\x7a\x6b\xeb\xa2\xfe"
+"\x9b\x3e\x39\xf1\x77\x82\xa5\xd2\x3c\x2b\xe3\x2b\x7f\x0a\xda\x5e\x78\x9b\xc3"
+"\xfa\x60\x83\x4d\xf1\xad\xac\xf7\x51\xc0\x97\xff\x00\x66\x89\x5e\x63\x6f\xb8"
+"\x12\x8e\x10\xa0\x6e\x40\x21\x48\xda\x7a\x9f\x41\xd6\x7e\x06\xeb\x9e\x2e\xb9"
+"\xd4\x3c\x4f\xa3\xcb\xa6\x32\xdd\x6a\xb6\x7a\xfe\x97\x67\xa8\xa3\x34\x53\xc4"
+"\x2c\xc4\x0f\x6f\x70\xb8\xf9\x77\x23\x13\x91\x9c\x1c\x71\x5e\xaf\x7f\xe0\x4f"
+"\x87\xba\x96\xbf\x71\xad\x5c\x5a\x69\x6f\xa9\xdc\x10\x67\x9b\xcf\x03\xce\x20"
+"\x60\x17\x50\xd8\x63\x80\x39\x20\xf4\xae\xb1\x35\xcd\x2a\x34\x55\x5d\x46\xcd"
+"\x55\x46\x00\x13\xa6\x00\xfc\xeb\xd4\xc5\x4e\x85\x7a\x34\xe8\xa5\x65\x0d\xb5"
+"\xf2\x49\x2d\xfa\x5b\x47\xa6\x96\x5b\x24\x79\xf8\x75\x53\x0f\x5f\xeb\x09\xfb"
+"\xca\xff\x00\x8d\xd3\xfb\xd3\x7a\x7c\xf7\x2b\x78\x46\x1b\xe8\x74\x38\x57\x51"
+"\xd3\x6c\xf4\x9b\xb3\xcb\xda\xd8\xc9\xe6\x46\x9f\xf0\x2d\xab\x9f\xae\x2b\x6a"
+"\xa8\x7f\x6f\xe9\x7f\xf4\x12\xb4\xff\x00\xbf\xeb\xfe\x34\x7f\x6f\xe9\x7f\xf4"
+"\x12\xb4\xff\x00\xbf\xeb\xfe\x35\x82\x92\xee\x68\xee\xdd\xec\x5f\xa2\xa8\x7f"
+"\x6f\xe9\x7f\xf4\x12\xb4\xff\x00\xbf\xeb\xfe\x34\xb2\xeb\xba\x6c\x0c\x16\x4d"
+"\x46\xd2\x36\x2a\x1c\x06\x9d\x41\x2a\x7a\x1e\xbd\x3d\xea\xa3\xef\x3b\x47\x52"
+"\x76\x2f\x51\x59\xdf\xf0\x91\x69\x3c\x7f\xc4\xce\xcf\x9e\x9f\xe9\x09\xcf\xeb"
+"\x57\xa1\x9a\x3b\x88\x92\x58\x9d\x65\x8d\xc6\x55\xd0\xe4\x30\xf5\x06\xa9\xc6"
+"\x51\xdd\x00\xfa\x28\xa2\xa4\x02\x8a\x28\xa0\x02\x8a\x28\xa0\x02\x8a\x28\xa0"
+"\x02\x8a\x28\xa0\x02\x8a\x28\xa0\x0c\xdb\x1d\x36\xe3\x4e\xb7\xf2\x21\xb8\x88"
+"\xc6\x1d\xd8\x79\x90\x92\x7e\x66\x2c\x79\x0c\x3d\x6a\xc7\x97\x7b\xff\x00\x3f"
+"\x10\x7f\xdf\x86\xff\x00\xe2\xea\xd5\x15\x1c\x8b\xfa\x6c\xae\x66\x63\xeb\x9a"
+"\x8d\xde\x89\xa4\xdc\xdf\x3c\x90\xca\xb0\xae\xe2\x8b\x03\x64\xf3\x8f\xef\xd7"
+"\x93\xcf\xfb\x4f\x68\xf6\x90\x07\xba\x59\xad\x98\xbf\x96\x23\x7b\x09\x49\x2d"
+"\x9c\x60\x15\x24\x37\xaf\xca\x4f\x1c\xd7\xb4\x5d\x5b\x5b\xea\x16\xcd\x0c\xe8"
+"\x93\xc1\x20\xc9\x46\xe4\x30\x07\x3f\xe1\x59\xbf\xf0\x86\xe8\x59\xc7\xf6\x5d"
+"\xb6\x7f\xdc\x15\x2e\x1d\x9f\xe2\x35\x23\xcb\xcf\xed\x25\xa7\x47\x34\x91\xcd"
+"\x05\xc4\x0c\xb2\x79\x6b\x9d\x3e\x57\xdf\xe8\x46\xc2\x70\x38\x3d\x70\x78\xe9"
+"\xd2\xaf\x0f\x8f\x11\x1d\x1e\x3d\x4c\x59\xcc\x6d\x9d\xc2\x60\x5a\x3f\x98\xbf"
+"\x2b\x31\x25\x37\x67\x00\x29\xe8\x0e\x78\xc6\x72\x2b\xd0\x5b\xc2\x1a\x0a\x0c"
+"\xb6\x9b\x6a\xa3\xdd\x05\x3c\x78\x43\x44\x31\xec\x1a\x65\xb6\xdc\xee\xc0\x41"
+"\xd6\x97\x23\xee\x1c\xde\x47\x97\x4f\xfb\x47\xd8\xdb\x24\x6d\x2d\xa5\xd2\x79"
+"\xa4\x79\x43\xfb\x3e\x52\x64\x53\xfc\x43\x07\x81\xc1\xe1\xb0\x78\xe9\xd2\x9a"
+"\x9f\xb4\xae\x93\x34\x12\x4d\x1e\xf9\x23\x43\xb7\xe5\xb2\x97\x71\x6c\xb0\x2b"
+"\xb4\x9c\xe4\x15\xe7\x20\x01\x91\xcd\x7a\x87\xfc\x21\xfa\x0e\x14\xff\x00\x66"
+"\xda\xfc\xdd\x3e\x41\xcd\x1f\xf0\x87\xe8\x43\xfe\x61\x96\xbd\x71\xf7\x05\x1c"
+"\x8f\xb8\x73\x79\x18\x7e\x02\xf8\x80\x7e\x20\x2d\xdc\x96\x45\x23\x86\x05\x8d"
+"\xb7\x4b\x6c\xe8\x49\x62\xe3\x1b\x59\x81\x18\xd9\xdc\x77\xae\xb7\xcb\xbd\xff"
+"\x00\x9f\x88\x3f\xef\xc3\x7f\xf1\x75\x1e\x9d\xa2\xd8\x68\xe6\x43\x65\x69\x15"
+"\xa9\x97\x01\xcc\x6b\x82\xd8\xce\x33\xf4\xc9\xfc\xea\xf5\x52\x82\xeb\xf9\xb1"
+"\x73\x15\x7c\xbb\xdf\xf9\xf8\x83\xfe\xfc\x37\xff\x00\x17\x5e\x69\xe3\xef\x04"
+"\x6a\x9a\xae\xb9\x1d\xc5\x9e\x8d\x6b\xa8\xa0\x8d\x10\xcf\x2a\xc2\x4e\x46\x73"
+"\xc4\x84\x90\x39\xed\xfa\xe2\xbd\x56\x8a\xeb\xc3\xd6\x78\x69\xfb\x48\x6a\xfc"
+"\xef\xfe\x64\xcb\xde\x56\x67\x8e\xe8\x7e\x08\xd4\x2c\x60\x91\xaf\x3c\x0d\xa5"
+"\x5f\x3b\x33\x05\xb7\x96\x3b\x55\x8e\x30\x08\xc3\x29\x00\xfd\xee\x49\xe3\xd3"
+"\xf0\xf5\xeb\x7b\x78\xad\x20\x48\x60\x89\x21\x85\x06\xd4\x8e\x35\x0a\xaa\x3d"
+"\x00\x1d\x2a\x4a\x2b\x4c\x46\x26\x78\x87\x79\x2b\x7a\x5f\xf5\x6c\x95\x1b\x05"
+"\x14\x51\x5c\x85\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51"
+"\x40\x05\x14\x51\x40\x05\x21\x19\x18\x3d\x29\x69\x92\xab\x3c\x4e\xa8\xfe\x5b"
+"\x95\x21\x5f\x19\xda\x7b\x1c\x77\xa0\x0e\x42\x6f\x84\x3e\x13\xb8\x50\xaf\xa6"
+"\xc8\x54\x63\x81\x79\x38\xe0\x10\x71\xc3\xf4\xc8\x19\x1d\x0e\x39\xa9\xd3\xe1"
+"\x77\x86\x91\xb7\x7f\x67\xb3\x1d\xd9\x05\xae\x24\xf9\x7d\x87\xcd\xd3\xbe\x2a"
+"\xa4\x3e\x08\xf1\x14\x32\x96\x1e\x38\xbf\x92\x3c\x60\x47\x25\xac\x27\x07\x18"
+"\x07\x38\xc9\x3f\xa6\x49\x38\xe9\x89\x0f\x83\x7c\x43\xe4\xe0\x78\xce\xef\xce"
+"\x0a\x54\x48\x6d\x23\xc6\x37\x12\x32\xbd\x09\x00\x81\x9e\x38\x51\x9e\xf9\xeb"
+"\xf6\x34\xff\x00\xe7\xea\xfb\xa5\xfe\x44\xdd\xf6\x34\x07\xc3\xfd\x07\xed\x97"
+"\xb7\x46\xc9\x9e\x7b\xc0\xe2\x66\x7b\x89\x5b\x76\xfc\xee\xc0\x2d\x85\xea\x71"
+"\x8c\x63\x27\x18\xa8\x62\xf8\x69\xe1\xe8\x27\xb7\x95\x6d\x25\xdd\x6e\xc1\xa2"
+"\x06\xea\x52\x15\x81\x27\x3f\x7b\x9c\xe7\x9c\xe4\x60\x01\x50\xc5\xe1\xed\x57"
+"\x4a\x82\xfa\xe3\x50\xf1\x85\xc3\xdb\x7d\x96\x50\x64\x9a\x18\xa2\x58\x0f\x5f"
+"\x37\x77\x6d\xa0\x1e\xbf\x5a\xc1\x83\xcc\x96\x28\x64\x8f\xe2\x9c\x12\xc6\x87"
+"\xcb\x2c\xa2\xd8\xac\x87\x6f\x00\x9c\xfd\xec\x60\xf0\x47\xd2\x92\xa5\x4e\xf6"
+"\xf6\x8b\xee\x97\xf9\x0e\xef\xb1\xbe\xff\x00\x0b\x7c\x3a\x6d\x7e\xcf\x15\xa4"
+"\xb6\xf0\xe4\x1d\x91\x5c\x49\x8e\x33\x81\x82\x4e\x07\x3d\x07\xb7\xa0\xa8\x97"
+"\xe1\x17\x86\x0c\x16\xd1\x4d\x67\x3d\xca\xdb\xb3\x3c\x66\x6b\xc9\x89\x0c\x58"
+"\xb1\x3f\x7f\x93\x93\xde\xab\x5a\x68\x7a\xa6\xb1\x23\xdc\xe9\xfe\x3d\x7b\x88"
+"\x14\xec\x75\xb6\x86\x29\x10\x36\x17\xb8\x27\x07\x80\x71\x9f\xe2\x3c\x72\x31"
+"\xa5\x37\x85\xb5\xa9\xb4\xa8\xed\xc7\x8a\xef\x12\xed\x26\xf3\x3e\xd6\xb6\xf1"
+"\x02\xcb\xcf\xc8\x57\x18\x23\x9f\xd0\x7e\x3a\x34\xb9\x15\x3f\x6c\xb9\x7b\x7b"
+"\xda\x7e\x02\xf3\xb0\xd6\xf8\x5f\xe1\xa6\x80\x44\x6c\x65\x28\x1f\x78\xff\x00"
+"\x4c\x9f\x21\xb0\x06\x41\xdf\x9e\x80\x57\x41\xa5\x69\x90\x68\xda\x7c\x16\x56"
+"\xa1\x96\x08\x57\x6a\x07\x72\xed\x8f\x72\x79\x3f\x8d\x73\x6d\xe1\x2f\x10\x34"
+"\x68\x07\x8b\xee\x52\x40\x5b\x32\x0b\x48\xce\xe0\x76\xe0\x60\xe4\x71\x8f\x4e"
+"\xe6\xb6\xbc\x35\xa5\x5f\x68\xfa\x60\xb7\xd4\x35\x69\x75\x9b\x80\xc5\xbe\xd3"
+"\x34\x4b\x1b\x63\x03\x8c\x2f\x1e\xbf\x9d\x61\x3a\x70\x8c\x6e\xa6\x9b\xed\xaf"
+"\xea\x87\x7f\x23\x56\x8a\x28\xac\x06\x14\x51\x45\x00\x14\x51\x45\x00\x14\x51"
+"\x45\x00\x14\x51\x45\x00\x14\x51\x45\x00\x14\x51\x45\x00\x14\x51\x45\x00\x14"
+"\x51\x45\x00\x14\x51\x45\x00\x36\x48\xd2\x68\xd9\x1d\x43\xa3\x02\xac\xac\x32"
+"\x08\x3d\x41\x15\x59\x74\x9b\x15\x8b\xcb\x16\x56\xe2\x3c\x83\xb0\x44\xb8\xcf"
+"\xd3\x15\x6e\x8a\x00\xaf\x67\x61\x6b\xa7\x46\xd1\xda\xdb\x43\x6c\x8c\x77\x15"
+"\x86\x30\x80\x9c\x01\x9c\x0e\xf8\x03\xf2\xab\x14\x51\x40\x05\x14\x51\x40\x05"
+"\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40"
+"\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51"
+"\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14"
+"\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05"
+"\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40"
+"\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51"
+"\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14"
+"\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05"
+"\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40"
+"\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51"
+"\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x05\x14"
+"\x51\x40\x05\x14\x51\x40\x05\x14\x51\x40\x1f\xff\xd9";
+
+const unsigned char kWeewarThumbnail[] =
+"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00"
+"\x01\x00\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03"
+"\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08"
+"\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b"
+"\x0b\x10\x16\x10\x11\x13\x14\x15\x15\x15\x0c\x0f\x17\x18\x16\x14\x18"
+"\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05"
+"\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14"
+"\x14\x14\x14\x14\x14\xff\xc0\x00\x11\x08\x00\x88\x00\xc4\x03\x01\x22"
+"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01"
+"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05"
+"\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02"
+"\x04\x03\x05\x05\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05"
+"\x12\x21\x31\x41\x06\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08"
+"\x23\x42\xb1\xc1\x15\x52\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17"
+"\x18\x19\x1a\x25\x26\x27\x28\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43"
+"\x44\x45\x46\x47\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64"
+"\x65\x66\x67\x68\x69\x6a\x73\x74\x75\x76\x77\x78\x79\x7a\x83\x84\x85"
+"\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4"
+"\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3"
+"\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1"
+"\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"
+"\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01"
+"\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+"\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04"
+"\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
+"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23"
+"\x33\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19"
+"\x1a\x26\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47"
+"\x48\x49\x4a\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68"
+"\x69\x6a\x73\x74\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88"
+"\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7"
+"\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6"
+"\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5"
+"\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00"
+"\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\xfa\x18\x8f\x9b\x39\x60"
+"\x58\xff\x00\x9c\x53\x98\x28\x62\x06\x48\x07\x8a\xe4\x3c\x65\xe3\xc9"
+"\x7c\x2b\xaa\xd9\xdb\x8b\x41\x73\x1c\xe4\x17\x72\xe4\x6c\x04\x90\x4e"
+"\x02\xb1\x38\x0a\x5b\x00\x16\x38\xc0\x1c\xd6\xed\x9e\xbb\x05\xce\xad"
+"\xac\x69\x8e\x24\x86\xeb\x4e\x96\x1f\xde\x11\x98\xa7\x8a\x58\xf7\x21"
+"\x56\xfe\xf0\x21\x81\x1e\xe0\x76\x39\xfe\x0b\xa9\x94\x62\xa9\x61\x61"
+"\x8c\x95\xb9\x24\x9c\x96\xaa\xf6\x52\x8c\x76\xf5\x92\xfe\x91\xad\x6c"
+"\xb3\x11\x42\x85\x3c\x4c\xd2\xe5\x9a\x6d\x6a\xb6\x4d\x2f\xcd\xec\x74"
+"\x72\x78\xc6\xee\xc2\xc9\x9e\x49\xe3\xb7\xb7\x86\x3d\xf2\x4b\x22\x80"
+"\x36\x28\x39\x2c\x4f\x00\x00\x3a\xfa\x75\xaa\x53\x78\x85\x7c\x4d\x14"
+"\x37\x91\xdc\xc5\x77\x6e\xc9\x98\xa4\xb6\x60\xe8\xcb\xec\x41\x39\x1d"
+"\x7b\x9a\xcd\xd6\x74\xb8\x35\xfd\x22\xff\x00\x4b\xb9\x69\x05\xb5\xed"
+"\xbc\x96\xf2\x34\x2e\x51\xb6\x3a\x95\x6d\xac\x3a\x1c\x1e\x0f\x6a\xa1"
+"\xe1\x0f\x0b\xdb\xf8\x2f\x42\x8b\x4c\xb6\xbb\xb8\xba\x82\x0c\xed\x6b"
+"\x8d\xa5\xc0\xc9\x24\x7c\xaa\x3b\x92\x7d\x79\x35\xe6\x46\x86\x19\x51"
+"\x75\x2f\x6a\xb7\x56\x56\xd1\xa6\x9d\xdd\xfb\xde\xda\x1c\xf2\xc4\x4e"
+"\xa5\x27\x19\xd4\x6d\xdd\x69\xd2\xdf\xd5\x8d\x4b\xeb\x58\x6f\x6d\x66"
+"\x86\xe1\x4b\x5b\xc8\x9b\x59\x77\x11\x90\x7d\xfb\x51\xff\x00\x0a\x93"
+"\xc2\xfa\x97\x82\xef\x3f\xb2\x96\x7b\xad\x5e\x1b\x77\x95\x5c\x4a\x4c"
+"\x9e\x76\x1b\x6c\x65\x48\xda\x06\xe0\x57\xee\xe4\xe3\x83\x91\x9a\x75"
+"\xdc\x0b\x77\x6f\x2c\x0e\xc5\x51\xd7\x6e\x47\x5a\xc3\xf0\x97\x89\xee"
+"\x3c\x0b\xaa\x5c\xdb\x5a\xc2\x2f\xcd\xe4\xfb\x25\x8c\x9d\x8d\x1a\x21"
+"\x7d\xac\xa3\x91\xb7\xe6\x03\x18\xe7\x24\xf1\x8c\x9f\xaa\xc9\x38\x8f"
+"\x3d\xc8\xe1\xff\x00\x09\x18\xb9\xd3\x4a\x51\x9b\x84\x64\xe3\x19\xb8"
+"\xff\x00\x36\xbb\x35\xa3\x5d\x55\x93\xe9\x6f\x16\xae\x5f\x96\xe3\x6a"
+"\x5b\x30\xa1\x19\xa9\x45\xc7\x99\xc5\x37\x1b\xf5\x5e\x77\xdb\xb7\x42"
+"\xff\x00\xc2\x8b\xdb\xb8\xed\xe4\xb0\xf1\x2c\x77\xa9\xa6\x59\xba\x98"
+"\x6d\xd9\x0c\x2e\xc4\xe4\xc8\x9d\x33\x8c\xa8\x19\xf7\xeb\xc7\x1a\xd2"
+"\x95\xcb\xac\x41\xfc\xb2\xdb\x97\x77\xde\xef\x8c\xfb\xd7\x18\xdf\x12"
+"\xb5\x4d\x63\x46\x5d\x51\xfc\x37\x79\x16\xa7\x23\x86\x9e\xc8\x0f\x96"
+"\x2c\x99\x01\x5d\xe7\x01\x9b\x31\xec\x2a\xbb\x88\x67\x18\x0c\x36\x96"
+"\xb1\x6d\xe3\xa1\x76\xb6\x85\x20\x92\xdd\x6e\x0c\x65\xad\xee\x00\x59"
+"\xa3\xdc\xb9\xf9\xc6\xef\x94\xa6\x76\xb0\xe7\x96\x18\xcf\xcc\x57\xd5"
+"\xe2\xd9\xe3\xf3\xac\xc2\xbe\x69\x57\x0b\x4e\x8a\x9c\xdd\xe3\x4d\xc5"
+"\xa5\x25\x18\xdf\x54\xdb\x6a\x5f\x12\x7f\x0c\x9b\x93\x8f\x53\x3c\x15"
+"\x05\x96\xe1\x29\xe0\xa5\x51\xcf\x92\xe9\x4a\x5f\x13\x57\x76\x5f\x25"
+"\xa2\x5d\x15\x8e\x98\x28\xda\x77\xf2\x30\x7b\x60\x01\xff\x00\xea\xa2"
+"\x52\x44\x6d\xb1\x4b\x3e\xd2\x54\x1f\x5a\x19\x95\xe3\xc1\x6f\x91\x81"
+"\x19\x1c\xd0\xf2\x15\x57\x2b\x97\x65\x19\x0a\x3b\xfb\x57\xe7\x7a\xdc"
+"\xf4\x48\x27\xbf\xb3\xb6\xbb\x82\xd2\x6b\x98\xa3\xb9\xb8\x0d\xe4\xc0"
+"\xee\x03\xcb\xb4\x65\xb6\xae\x72\x76\x8c\x13\x8e\x99\xad\xcd\x36\x5b"
+"\x6b\x4b\x79\xee\x3c\xe9\x61\xd4\x15\x81\x80\xa6\x78\xf9\x5b\x91\xe8"
+"\x73\xb7\xae\x46\x37\x71\x92\x08\xe8\x6f\x7e\x04\xf8\x57\x59\xf0\xce"
+"\x8d\xe2\xaf\x10\x6b\xba\xf5\xad\xb0\x92\xd9\x9e\xd2\xc9\xa4\x92\xd9"
+"\xe4\x13\x8f\x24\xbc\x08\xad\x9d\xb2\x14\x6d\xf8\xf9\x76\x06\x24\x2a"
+"\x9c\x57\xf1\x05\xdf\xc2\xdf\x0b\xce\x53\x52\xf1\x07\x89\x20\x1b\x24"
+"\x94\xca\xbe\x1e\xbe\x92\x15\x8e\x39\x1a\x37\x73\x22\x5b\x14\x0a\x19"
+"\x09\xdc\x4e\x0a\x95\x60\x4a\xb2\x93\xfa\x5c\x78\x2f\x30\xa7\x46\x86"
+"\x22\x8c\xa1\xcd\x52\x1c\xcd\x4e\x50\x8d\xb9\xaf\x66\xaf\x2b\xbd\x1a"
+"\x77\xb2\xb4\xb4\x5b\x1d\x54\xa9\x55\xbf\x3c\x63\x7f\xbf\xfa\xb9\xcb"
+"\x36\xb2\xd6\x6d\x2a\x1b\xa9\x23\xf3\x48\xdf\x92\x7e\x60\x32\x46\x7d"
+"\xb8\x3f\x91\xa8\xdb\x2c\xfb\xb9\xc9\x39\x20\x8f\xf3\x8a\xde\xb9\xd0"
+"\xfe\x11\xf8\xa2\xd7\x4f\xd5\x60\xf1\xbe\xaa\xf6\x57\x4a\xc2\x3b\x8b"
+"\x5b\x19\x4a\x18\xd2\x63\x13\x3b\x3f\x92\x7c\xb8\xf7\x6e\x3b\xd8\x85"
+"\x28\xac\xf9\x31\x86\x6a\xef\xfc\x23\xf0\xd7\xc2\x1f\x10\x74\xbf\xed"
+"\x4d\x0b\x5e\xd4\x2e\xec\x8c\x85\x44\x8f\x6e\x61\xdd\xe8\xc1\x64\x45"
+"\x25\x0f\x05\x5c\x0d\xac\x30\x54\x90\x73\x4a\x3c\x03\x9c\x56\x71\x85"
+"\x37\x09\x4b\x5d\x3d\xa4\x5d\xbf\x1f\x51\x3a\x35\xef\xfb\xc5\x64\xbb"
+"\xdc\xf2\x59\x15\x04\x8c\x14\x12\x06\x70\x7d\x45\x5d\x93\xc6\x37\x7a"
+"\x65\x94\x92\x49\x71\x15\xb5\xac\x11\xef\x92\x69\x00\x03\x62\x83\x9d"
+"\xc4\xf0\x00\x00\x73\xe8\x39\x3c\x71\xce\xc7\xae\x20\xf1\x0e\xa7\xa4"
+"\xcd\x1b\xa4\xb6\x82\x29\x92\xe5\x7e\x68\x66\x8a\x45\xf9\x40\x23\xf8"
+"\xc3\x89\x01\x5f\xf7\x70\x0f\x53\x2e\xb7\xa5\xc3\xaf\xe9\x17\xfa\x55"
+"\xd3\x4a\xb6\xb7\xb6\xcf\x6d\x23\xc3\x21\x8d\xf6\xba\x95\x6d\xac\x39"
+"\x0d\x83\xc1\x1c\x8a\xfc\xf2\xae\x15\x53\x9c\x61\x88\xd9\xa8\xbd\x2c"
+"\xf4\x69\x3f\xbe\xcf\x6e\x9d\x47\x19\x55\xc3\x4a\xca\x5c\xb7\xb5\xec"
+"\xfa\x3d\x7a\x1a\x93\x78\x91\x7c\x53\x0c\x57\x71\xdc\x43\x75\x6c\xcb"
+"\x98\xa4\xb6\x60\xd1\x95\x27\xaa\x90\x4e\x47\x1e\xb5\x4e\xf2\xde\x1b"
+"\xab\x5b\x88\xae\x14\x9b\x79\x10\xa3\xae\xec\x64\x1e\xbc\xf6\xac\x8f"
+"\x07\x78\x4e\xd7\xc1\x5a\x30\xd3\x6c\xee\xae\x6e\x20\x46\x2c\x0d\xcb"
+"\x86\x65\x07\xb0\xc0\x1c\x75\xfc\x49\x35\xb3\x75\x0a\xdc\xdb\xcb\x0b"
+"\xb1\x55\x91\x76\x92\x3a\xf3\xe9\x56\xf9\x30\xb8\x8b\xe0\xe6\xf9\x62"
+"\xef\x19\x7c\x2f\x47\x74\xfc\x9f\x5d\xf7\x31\xc4\xf2\xd6\x94\xee\xf9"
+"\x93\xbe\xfd\x57\x9a\xd7\xa7\x42\x28\xfe\x14\x78\x6b\x53\xf0\x4d\xe8"
+"\xd3\x12\x6b\x9d\x5a\x38\x24\x95\x58\x4a\x4c\x9e\x70\xdc\x11\x76\x9f"
+"\x94\x0c\x82\xbf\x77\x27\x1d\x72\x33\x54\x7e\x15\xdc\xde\xc3\x6b\x2e"
+"\x9d\xe2\x08\xee\xe0\xd3\xad\x64\x06\x38\x65\x8c\xc4\xd9\x20\x97\x07"
+"\x8c\xf5\x55\x1e\xbe\xe3\xb3\x3c\x25\xe2\x39\xbc\x0b\xaa\xcd\x69\x08"
+"\xfb\x67\xdb\xa7\x58\xdd\x1c\x05\x31\xaa\xe7\x6b\x28\x07\x85\x1b\xf1"
+"\xd3\x92\x7d\x89\xac\x84\xf8\xa5\xa8\x78\x83\x4b\xbc\xd4\xa4\xf0\xd5"
+"\xfd\xae\xa5\x14\xeb\x1d\xcd\x9b\x2f\xee\xe3\xc8\x62\xc4\x48\x70\x24"
+"\xda\x53\x61\x11\xef\x21\x98\x60\x30\x20\x9f\xd6\x61\xc5\x9c\x45\x9b"
+"\xe5\x78\x9c\xa3\x1c\xd6\x2e\x35\x2a\x53\x9c\x2a\x56\x9a\x72\xa7\x26"
+"\xf6\x8a\x9b\x49\x42\x56\x71\x93\xd2\x31\x4d\xde\xdc\xc8\xf0\xa9\xe4"
+"\xb9\x7d\x1c\x4e\x1f\x30\xc0\x47\xd9\x4e\x9c\x5c\x67\x08\x46\xd1\x9a"
+"\xd6\xdc\xd6\xdd\xf5\xee\xda\xf2\x3b\x39\xf6\x79\x8f\xe4\xab\xec\x24"
+"\x94\xf3\x3e\xf0\x1d\xb3\xef\x51\xb2\xa9\x53\xbf\xee\x63\x9c\xf4\xc5"
+"\x72\xb1\xf8\xf0\x5d\x45\x69\x34\x56\xd2\xc5\xe7\xaa\x31\xb5\xb9\x50"
+"\x93\xc7\xb9\x03\x61\xd7\x3f\x21\x5f\xba\xc3\x92\x18\xa8\xc1\xf9\x8a"
+"\xf5\x4f\xb2\x48\x88\x2d\xfb\xb7\x04\x13\xed\x5f\x92\xd7\xc3\x55\xc3"
+"\x4b\x96\xb4\x6c\xcf\x5a\x35\x23\x52\xee\x22\xb0\xe0\xe0\x12\x48\xa8"
+"\x66\xbd\xb5\xb6\x9e\x0b\x79\xa7\x8e\x39\xa7\xca\xc3\x1c\x8e\x03\x4a"
+"\x40\xdc\x76\x8e\xf8\x00\x93\x8e\x83\x35\x31\x7c\x6e\x00\x92\x40\xcf"
+"\x4a\xe7\xbc\x49\xe0\x7d\x3f\xc5\x1a\xa6\x9b\x7d\x79\x73\x76\xad\x63"
+"\x2c\x72\x2c\x11\x4d\x88\xa5\x29\x22\xca\x81\xd4\x82\x0e\x24\x8e\x37"
+"\xe3\x07\x31\xaf\x3c\x51\x86\x8d\x19\xd4\xb6\x22\x4e\x31\xb3\xd5\x2b"
+"\xbb\xdb\x4d\x2e\xb4\x6e\xc9\xeb\xa2\xd7\x52\xce\xf8\xe9\x52\x41\xb2"
+"\x68\xb5\x2b\xe8\xfe\xd3\x1c\x72\xb4\x66\x45\x74\x43\xb1\x57\x08\x19"
+"\x4e\xc5\xc2\x83\xb5\x70\x32\x59\xba\xb1\x24\xad\x09\xb9\xb7\xb1\xff"
+"\x00\xaf\x68\xff\x00\x95\x15\xfd\xc3\x93\xff\x00\xc8\xb7\x0d\xff\x00"
+"\x5e\xe1\xff\x00\xa4\xa3\xd3\x8e\xc8\xf3\x1b\x7f\x1f\x5b\x43\x73\x0c"
+"\xfe\x5a\x92\x92\xe5\x73\xc0\x62\x38\xf9\x4f\xde\x24\xf6\x23\x1c\x1c"
+"\xe4\x01\xba\xa6\xf8\x8d\xe1\x69\x3c\x7f\xac\xe9\x9a\x84\xda\x94\xd6"
+"\x52\x58\x5d\xfd\xa1\xa0\x88\x29\x86\x73\x80\x30\xe3\xae\x70\x08\xdc"
+"\x08\x38\x77\x1c\xee\xae\x5b\xc2\x3a\xa5\x94\x5a\xe4\x17\xba\x85\xac"
+"\x32\x69\xb6\xf2\xa9\x30\x4c\x01\xdd\xc2\x81\xc6\x00\x51\xd8\x2e\x5b"
+"\x71\x6c\xf5\x24\x57\xad\xeb\x76\x57\x0c\x13\x52\x36\xf1\x5a\x43\x76"
+"\xe4\x24\x51\xf4\x50\x30\x33\x81\xc0\x04\xe7\xf1\x0d\xc7\x15\xfc\x89"
+"\x8d\xc2\x63\x72\x1a\xf2\xa3\x69\x53\xab\x49\xbe\x68\xb5\x7b\x7d\x9b"
+"\xbb\xe9\xd6\xdd\x77\x3c\x8c\xbf\x16\xf1\x18\x7f\x6f\x42\xa7\x34\x5e"
+"\xaa\xdd\x9a\xb5\xff\x00\x1b\x19\x39\xf9\xb0\x0f\x41\x48\x17\x07\x76"
+"\x41\x38\x03\x1d\x87\x5f\xf1\xa7\x0e\x5b\x18\xc0\x14\xd0\xaf\x9c\xfc"
+"\xa3\x20\x00\x3b\x0e\xbc\xfb\xf6\xaf\x8f\x34\x14\x7d\xe3\xce\x00\x1d"
+"\x2b\xcb\xc6\x8f\x63\x3d\xe4\x1e\x06\xfe\xd4\xd4\x52\xea\x39\x8d\xcb"
+"\xcb\x15\xab\xa0\xf2\xc8\xde\xea\xb2\x0f\xba\x1b\x79\xcc\x8c\xcc\x37"
+"\x3c\x8a\x0a\xb7\x96\x17\xd4\x01\x05\xfa\x0e\x07\x5a\xe7\xfc\x61\xe1"
+"\xab\xff\x00\x10\xd9\x2a\xe9\xba\x9c\xba\x3d\xf2\x3e\x44\xd0\x2f\xfa"
+"\xc5\xf2\xdd\x44\x6e\x41\x0c\x54\x33\x87\x01\x48\xf9\x91\x73\x95\xdc"
+"\x1b\xe9\xb2\x2c\x7c\xb0\x95\xfd\x9f\x3f\x27\x33\x4d\x4b\x6e\x49\xab"
+"\xa8\xcd\xbe\x59\x3b\x45\x4a\x57\x4a\xd7\xbe\xbb\x21\x34\x9e\xe5\x6f"
+"\x88\x56\x17\xbe\x07\x9b\x64\x9a\xb9\xd4\xad\xa6\xd3\x06\x9a\x96\xfb"
+"\x63\x3b\x1f\x2c\x5e\x66\x93\x6e\xf2\xcc\x36\x64\x71\x9d\x8b\xf4\x0f"
+"\xf0\x87\xc2\xab\xad\x43\xc1\x4f\xaa\xa6\xa3\x1c\x53\xcf\x18\x9a\x18"
+"\xfc\xb6\x0a\xaa\x9b\x86\x0b\x29\x07\x39\xe7\x03\x23\x81\x9c\x83\x5e"
+"\xad\xa8\xf8\x55\x75\x6f\x0c\x5e\xe8\xb2\xd8\xda\x8b\x91\x6c\xc0\x49"
+"\x3e\x19\x52\x76\x0c\x23\x71\x80\x73\x82\x1b\x9e\xa3\x6f\x19\x06\xbc"
+"\xb6\x0f\x84\x7e\x37\xb5\xf0\xe5\xed\x8a\x5e\x20\x8f\xcf\x42\xb6\x49"
+"\x76\xc1\x65\x55\x0c\x0b\x0e\x80\x03\x90\x70\x48\x27\x1c\x80\x40\xaf"
+"\xdd\xb2\x4c\xe3\x2d\xce\xb8\x4a\xaf\x0f\x56\xc7\xd3\xcb\xab\xfb\x68"
+"\x2a\x8e\xad\xa5\xed\x29\xa9\x37\x1e\x54\xed\xcb\xc8\xed\x77\xd7\x97"
+"\xde\x92\x52\xd3\xe4\xb3\x7c\xb7\x1b\x84\xcf\x21\x99\xfd\x56\x78\xaa"
+"\x5e\xce\x5c\x91\x85\xd7\xb3\x95\xb5\x4d\xfd\xab\xeb\x6d\x3a\xe8\x9b"
+"\x5a\xeb\x69\x7a\x7b\xd8\xe9\x70\xda\x3c\xed\x3b\x2a\x15\x32\xb9\xcb"
+"\x31\x3d\xf3\xde\xad\xc8\xfe\x52\x33\xf0\x76\xa9\x38\x15\xd9\xf8\x43"
+"\xc2\xb7\x3a\x3f\x83\xed\x74\xcd\x48\xc1\x75\x79\xe5\xb8\x96\xe3\x71"
+"\x6c\xb1\x63\xb3\x92\x32\x70\xa4\x0c\xf6\xc7\x1d\x68\xd1\x3c\x3b\x73"
+"\xa3\xc9\x73\x75\x3c\x76\xf2\xb4\x48\xde\x4a\x33\x1c\x6e\x1c\x86\xce"
+"\x0e\x01\xfa\x64\x73\xc5\x7f\x33\x63\xea\x51\xc3\xe3\x2b\xd1\xa7\x55"
+"\x54\x8c\x25\x24\xa4\xb6\x9a\x52\x69\x4a\x3e\x52\xb5\xd5\xfa\x33\xf4"
+"\x2c\x3e\x5b\x5e\xa5\x2a\x33\x94\x5c\x5c\xd2\x6d\x3d\xe0\xda\x4e\xcf"
+"\xcd\x6d\xea\x7a\x75\x86\xbb\xa0\xe9\x7f\x0f\x2c\xf4\x8d\x4f\x57\x92"
+"\xcc\x88\x84\x0f\x34\x70\x12\xea\xf9\x27\x80\x51\x86\x78\xe0\x91\xef"
+"\xe9\x5c\x17\x88\xfc\x1f\xf0\xc7\xc5\x1a\x46\xad\xa6\xdf\xf8\xaf\x52"
+"\xfb\x3e\xa9\x0c\x90\x5e\x34\x36\x50\x45\x24\xa8\xe8\x11\xb2\xeb\x6a"
+"\x0e\x4a\xa2\x0d\xd9\xcf\xc8\xbc\xf0\x2b\xe7\x2f\x89\x3e\x3e\x9b\xc4"
+"\xde\x28\x92\xeb\x4d\xba\xbb\x86\xca\x30\x16\xda\x36\x72\xb8\x20\x60"
+"\xb2\xa8\x38\xe4\x83\xee\x73\xcd\x73\x07\x57\xd4\x40\x9b\xfd\x2e\xe4"
+"\xf9\xa3\x74\xc3\x7b\x72\x0f\x73\xcf\x4c\x11\xf9\xe2\xbf\xbf\xb2\x2e"
+"\x00\xe2\x8a\xf9\x56\x12\xa6\x2e\x78\x78\x4f\xd9\xc3\xdd\x9d\x19\xb9"
+"\x43\x6f\x76\x4f\xda\x6e\x96\xae\xd6\x5c\xda\x59\x2d\x4f\xc7\xb3\x0f"
+"\x12\x70\x58\x5c\x5d\x4a\x38\x4a\x52\x94\x21\x26\x94\x94\xf4\x76\xd9"
+"\xab\x74\x6f\x6f\x2d\x7c\x8f\xa4\x7e\x1c\xf8\x77\xe0\xaf\xc3\x8d\x26"
+"\x0d\x17\x47\xf1\xc6\xad\x74\x90\xbb\x90\xf7\xf0\x2d\xcc\xa8\xed\xb7"
+"\x8d\xed\x6b\x94\xc1\x48\x98\x28\xda\x3e\x44\x6c\x70\x0d\x7a\x47\x87"
+"\xbe\x32\x7c\x33\xf0\xe3\xde\x5c\x43\xe2\xe7\xb8\x17\x45\x77\x09\x2c"
+"\x59\x43\x3a\xb3\xe5\xd3\xcb\x81\x4b\x16\xdd\x9e\xe3\x0a\x08\x00\x64"
+"\x9f\x87\xb7\xcf\x11\x77\x04\x8d\xa4\x02\x47\x63\xf5\xfc\x0f\x5a\xec"
+"\xfc\x37\xab\x59\x26\xaf\x6f\x79\x79\x6f\x0b\x69\x96\xae\x19\xad\xe7"
+"\x55\x04\xf1\xc7\x60\x00\x03\x03\x04\x92\x4b\x77\xe0\x57\xcf\xf1\xae"
+"\x0b\x3d\xe0\x8a\x14\xb1\xbc\x94\x6a\xc2\x4d\xa9\x4a\x34\xe5\x1e\x47"
+"\xa7\x2d\xdf\x3b\x6d\xce\xef\xe6\x9a\xd7\x46\xfd\x4c\x8b\x8d\x21\x9f"
+"\x57\x9d\x16\xb9\x25\xa3\xf7\x9b\x77\xbe\xf6\xf4\xfc\x8e\xaf\xe2\x87"
+"\x85\x5b\xe2\x3d\xf5\x9d\xc5\xcd\xf1\xb3\x92\xd2\xed\x6e\x1a\x24\x40"
+"\xf0\xce\x14\x6d\x29\x20\x38\x38\x61\x90\x59\x4a\xb6\x19\xb0\x46\xe3"
+"\x5b\x5b\xbe\x7d\xa3\xb2\x83\x8f\xaf\xff\x00\xaa\xb6\x35\xbb\x1b\x87"
+"\xc6\xa6\x6d\xe2\xb3\x82\xe9\xb6\xa4\x51\x91\x81\x8e\x33\x81\xc6\x09"
+"\x07\xf1\x07\x81\xc5\x64\x02\x19\xf6\xf4\xc0\x07\xf3\xff\x00\xf5\x1a"
+"\xfe\x2f\xc5\xd6\xc4\x35\x1a\x15\xa5\x78\xc6\xee\x37\x56\xf8\xac\xef"
+"\xdf\x5d\x1e\xad\x9f\xa0\xd6\x94\xdb\xb4\xdd\xed\xb7\xcd\xdc\x6e\xcc"
+"\x16\x6d\xc3\x38\x03\xe9\x8a\x7f\x73\xc8\xc6\x07\x18\xa6\x01\x20\xc9"
+"\xc0\xe4\x00\x07\x61\xd7\xf3\xa7\xe4\x16\x3c\x0e\x3a\xd7\x9c\xce\x73"
+"\xca\xee\x2d\xb4\x76\xbf\x4f\x03\x5c\x6a\xda\x9f\xdb\x1e\xeb\xcf\xcc"
+"\x36\xed\x10\x0a\xe0\xbb\x22\xba\x80\xdb\x58\x79\x84\xc9\xb9\x80\x66"
+"\x93\xe6\x05\x42\xa7\x5f\xe3\x9b\x0b\xbf\x00\xc2\xbe\x6e\xae\xda\xaa"
+"\xdc\xe9\x89\xa7\x6c\x78\xe3\xde\x1c\x16\x66\x95\xe4\xd8\x1b\x73\x7c"
+"\x84\x80\x14\x7c\xa3\x81\xd0\x4f\xe3\x1d\x02\xff\x00\xc4\x1a\x15\xcd"
+"\xb6\x9b\xa8\x36\x93\xa8\xbe\x16\x2b\xc8\x57\x2c\x8b\xdc\x71\xc9\xf5"
+"\xc0\xc7\x20\x76\xce\x7d\x3e\xf3\xc2\x6b\xa9\xf8\x5e\xeb\x48\x9e\xca"
+"\xd4\xcc\xd6\xec\x0c\x92\x90\xc1\x26\x6d\xc2\x36\xe0\x1c\xe0\x86\xe4"
+"\x1c\x8c\x0c\x67\x35\xfa\xde\x49\xc4\xb8\x7c\x1e\x2f\x09\x8e\xc7\x53"
+"\x75\x28\x29\xb5\x56\x0a\x56\xb3\x92\x5c\xf5\x34\x8a\x51\x8c\x9b\x4f"
+"\x95\x5d\xfe\xee\x49\x35\x7b\x99\x62\x30\x38\x8c\x7e\x0e\xbd\x0c\x1c"
+"\xb9\x2a\xb8\xfb\xb2\x6a\xff\x00\x2f\x3b\xea\xb5\xdb\xcc\xf2\x1f\x04"
+"\xfc\x28\xb9\xd6\x3c\x23\xfd\xb6\x9a\x92\x45\x2c\x90\xb3\x41\x01\x47"
+"\x0b\x85\x66\x07\x73\xab\x29\xea\x09\xc0\xc8\x23\x83\x90\x71\x5b\xfa"
+"\x66\x9a\xf6\x3a\x54\x56\x72\x5c\x34\xcc\xa8\x55\xa6\x63\x96\x62\x49"
+"\x39\xcf\x7e\xb5\x8d\x0f\xc2\x1f\x1c\x58\xe8\xd7\x96\xc9\x7f\x10\xb5"
+"\x59\x79\xb1\x8e\xed\xc2\xdc\x28\xe0\xb0\x1c\x0c\x1c\xf4\x62\x09\x03"
+"\x90\x30\x2b\xd6\x3c\x19\xe1\x5b\x8d\x1b\xc1\xd6\x9a\x6e\xa5\xe4\x5c"
+"\xde\x14\x71\x3c\xdb\xcb\x72\x58\x94\xe4\x8e\x70\x08\x1e\xd8\xe2\xbd"
+"\x7f\x15\x6a\x60\xf1\x78\xca\xf9\xe5\x1c\xe6\x8e\x32\x55\x2a\xf2\xc6"
+"\x9d\x34\x94\xa1\x4d\x45\xb8\xb6\xd3\xb3\x51\x56\x83\x7f\x69\xd9\xdd"
+"\xbb\xa8\xf8\x1c\x23\x81\xc4\x2a\x34\xb2\xe9\x60\x27\x87\x50\xa7\x79"
+"\x4e\x6d\xb5\x29\xde\xce\xcb\xa5\xf5\x76\xe8\xba\x6c\xdf\x1a\xc7\x01"
+"\xba\x70\x2b\x6b\x5c\xf0\xd9\xd2\x6c\x23\x9b\xed\x5e\x66\x5c\x44\xf9"
+"\x4d\xa7\x77\x3d\x33\xdb\x8c\x76\xcf\x51\x5b\x3a\x07\x86\x6e\x34\xdb"
+"\xb9\x6e\x2e\x92\x07\x08\xa4\x44\xb9\x24\xee\x04\x1d\xdd\x38\x1f\xa8"
+"\xf4\xaf\x06\xf8\x9b\xf1\x02\x4f\x14\x78\x93\xed\x3a\x55\xcd\xdc\x56"
+"\x10\xa8\x48\x55\x98\xa0\x0c\x33\x97\x55\x07\x1c\xfb\xf2\x6b\xe4\x38"
+"\x0b\x82\x31\xde\x20\x66\x55\x30\x78\x2a\x8a\x9d\x3a\x51\xe6\x9d\x46"
+"\xb9\xa2\x9b\xf8\x23\xa3\x56\x72\xb3\xb7\x92\x6f\x5b\x58\xf4\xb8\x87"
+"\x36\xc3\x70\xc6\x01\x62\x31\x90\xe6\xa9\x51\xda\x31\xbd\x9d\x97\xc5"
+"\x2f\x34\xb4\xfb\xd7\x73\xdc\xa7\xff\x00\x8f\x7b\x1f\xfa\xf6\x8f\xf9"
+"\x51\x59\x9e\x1f\x9d\xee\x7c\x31\xa2\x4b\x2b\x97\x91\xec\xa3\x2c\xcc"
+"\x72\x49\xc5\x15\xfd\x73\x43\x05\x2c\xb6\x94\x30\x53\x77\x74\x92\x83"
+"\x6b\x66\xe2\xad\x7f\xc0\xf7\x70\x95\xd6\x27\x0f\x4e\xbc\x55\x94\xa2"
+"\x9f\xde\xae\x7d\x33\x71\xe2\x3d\x24\xdb\xca\x20\xd5\x34\xe5\x9c\xa9"
+"\xf2\xda\x49\x15\x94\x36\x38\x24\x02\x09\x19\xf7\x15\x4f\xfb\x7a\xdc"
+"\x2b\xa8\xd6\xf4\x72\x47\xdc\x66\xc6\x4f\x4f\xbc\x03\x8f\x7e\x9f\xa5"
+"\x63\xeb\xd7\xcd\xa2\xe8\x7a\x8e\xa1\x0e\x9d\x71\xaa\x4b\x69\x6d\x24"
+"\xe9\x63\x64\xaa\x67\xb9\x65\x52\xc2\x38\xc3\x10\x0b\xb6\x30\x32\x40"
+"\xc9\x19\x22\xb8\x59\x3e\x2b\x6a\xf0\x49\x3c\x52\x7c\x30\xf1\x54\xb2"
+"\x42\xce\x86\x4b\x54\xb4\x68\xa4\x2a\x40\xdd\x1b\x34\xea\x4a\x92\x78"
+"\x25\x54\x90\x33\x80\x33\x8f\x4d\x68\x68\xd5\xcf\x45\x7d\x76\xe4\x49"
+"\x26\xcd\x77\xc3\xa5\x0b\xa9\x5d\xc8\x72\x17\x0b\xb9\x78\x93\xaf\x0f"
+"\x83\xfe\xd0\xe3\x8e\x75\xed\x3c\x45\xa5\xad\xba\x8b\xad\x53\x4d\x79"
+"\xf9\xdc\xd0\xc8\xaa\xa7\x9e\x30\x0b\x13\xfa\xd7\x95\x69\xbf\x13\xf5"
+"\x2d\x46\xdc\x4e\xdf\x0e\xbc\x4d\x62\x3e\xdd\x35\x97\x95\x77\x15\xb8"
+"\x94\x84\xd8\x12\x60\x16\x56\x1e\x53\x97\x20\x31\x60\x40\x46\x2c\x00"
+"\x15\xa3\x67\xe3\x7b\xfb\xab\x0b\x4b\x99\x3c\x19\xac\x5a\x35\xc4\xb6"
+"\xe8\x20\x9d\x62\xf3\x22\x49\x02\x17\x79\x02\xbb\x05\xf2\xf7\x3e\x46"
+"\x49\x3e\x59\xc6\x72\xb9\x72\x95\xd2\x4d\x21\x25\xab\x67\xa5\x7f\xc2"
+"\x49\xa2\x7f\xd0\x4a\xc7\xfe\xff\x00\x27\xf8\xd1\xff\x00\x09\x26\x89"
+"\xff\x00\x41\x2b\x1f\xfb\xfc\x9f\xe3\x5e\x7b\xa3\xf8\xc6\x7d\x4b\x40"
+"\x8b\x52\xb9\xf0\xa6\xb5\xa6\x4b\x24\xc2\x11\xa7\xdc\xc3\x11\xb8\x50"
+"\x40\x3b\xc8\x49\x18\x05\xfc\x73\xc7\x20\x55\xad\x3b\xc4\xb2\x5f\xc9"
+"\x3a\xbf\x87\xb5\x5b\x31\x13\x05\x0d\x71\x0c\x60\x48\x37\x28\xca\xe1"
+"\xcf\x1f\x36\x79\xc7\x0a\x7b\xf1\x59\xf7\x28\xee\x3f\xe1\x24\xd1\x3f"
+"\xe8\x25\x63\xff\x00\x7f\x93\xfc\x68\xff\x00\x84\x93\x44\xff\x00\xa0"
+"\x95\x8f\xfd\xfe\x4f\xf1\xae\x11\xbc\x52\xea\xd0\x03\xe1\xbd\x5f\x12"
+"\xab\xb1\x61\x04\x64\x26\xd2\x38\x6c\x3e\x41\x39\xe3\x8e\x71\x53\xe9"
+"\xfe\x22\xfb\x76\xb4\x74\xe6\xd1\x75\x2b\x5f\xdd\x4b\x28\xba\x9e\x05"
+"\x10\x10\x8e\xa9\x8d\xc1\x8e\x19\xb7\x6e\x50\x46\x4a\x82\x78\xc6\x29"
+"\xda\xe0\x76\x9f\xf0\x92\x68\x9f\xf4\x12\xb1\xff\x00\xbf\xc9\xfe\x34"
+"\x7f\xc2\x49\xa2\x7f\xd0\x4a\xc7\xfe\xff\x00\x27\xf8\xd6\x2f\x96\x9f"
+"\xdd\x5f\xca\x8f\x2d\x3f\xba\xbf\x95\x00\x6d\x7f\xc2\x49\xa2\x7f\xd0"
+"\x4a\xc7\xfe\xff\x00\x27\xf8\xd1\xff\x00\x09\x26\x89\xff\x00\x41\x2b"
+"\x1f\xfb\xfc\x9f\xe3\x58\xbe\x5a\x7f\x75\x7f\x2a\x3c\xb4\xfe\xea\xfe"
+"\x54\x01\xb5\xff\x00\x09\x26\x89\xff\x00\x41\x2b\x1f\xfb\xfc\x9f\xe3"
+"\x47\xfc\x24\x9a\x27\xfd\x04\xac\x7f\xef\xf2\x7f\x8d\x62\xf9\x69\xfd"
+"\xd5\xfc\xa8\xf2\xd3\xfb\xab\xf9\x50\x06\xd7\xfc\x24\x9a\x27\xfd\x04"
+"\xac\x7f\xef\xf2\x7f\x8d\x1f\xf0\x92\x68\x9f\xf4\x12\xb1\xff\x00\xbf"
+"\xc9\xfe\x35\x8b\xe5\xa7\xf7\x57\xf2\xa3\xcb\x4f\xee\xaf\xe5\x40\x1b"
+"\x5f\xf0\x92\x68\x9f\xf4\x12\xb1\xff\x00\xbf\xc9\xfe\x34\x7f\xc2\x49"
+"\xa2\x7f\xd0\x4a\xc7\xfe\xff\x00\x27\xf8\xd6\x2f\x96\x9f\xdd\x5f\xca"
+"\x8f\x2d\x3f\xba\xbf\x95\x00\x6d\x7f\xc2\x49\xa2\x7f\xd0\x4a\xc7\xfe"
+"\xff\x00\x27\xf8\xd1\xff\x00\x09\x26\x89\xff\x00\x41\x2b\x1f\xfb\xfc"
+"\x9f\xe3\x58\xbe\x5a\x7f\x75\x7f\x2a\x3c\xb4\xfe\xea\xfe\x54\x01\xb5"
+"\xff\x00\x09\x26\x89\xff\x00\x41\x2b\x1f\xfb\xfc\x9f\xe3\x47\xfc\x24"
+"\x9a\x27\xfd\x04\xac\x7f\xef\xf2\x7f\x8d\x62\xf9\x69\xfd\xd5\xfc\xa8"
+"\xf2\xd3\xfb\xab\xf9\x50\x05\xcd\x5b\xc4\x56\xcd\x0a\x7f\x66\x6a\xba"
+"\x32\x4a\x18\xef\x37\x6f\xb9\x48\xda\x70\x06\xd6\x18\x3b\xb6\x9c\xfa"
+"\x02\x31\xce\x45\x9b\x6f\x11\xe9\x42\x32\x2e\x35\x4d\x35\x9f\x71\xc1"
+"\x8e\x55\x03\x6e\x78\xe0\x93\xce\x2b\x2b\xcb\x4f\xee\xaf\xe5\x47\x96"
+"\x9f\xdd\x5f\xca\x95\xb5\xb9\x6e\x77\x8a\x8d\x97\xea\x79\x87\xc4\x69"
+"\xa2\xb8\xf1\x65\xd4\xb0\x3a\x49\x13\x05\x2a\xe8\x72\xa4\x63\xb1\xa2"
+"\xa2\xf1\xf0\x03\xc4\x93\x80\x30\x36\x27\xf2\xa2\xb9\x9e\xe5\x1e\xac"
+"\x46\x41\xe7\x1e\xf5\xe6\x1e\x12\xf8\xd5\x17\x8b\x75\x4d\x62\xda\xdb"
+"\x47\xd4\x65\xb7\xd3\xd2\xd5\xbe\xd3\x68\x04\xe2\x43\x2b\x5c\x06\xc0"
+"\x0a\x3e\xe7\xd9\xf9\x2a\x5b\x3e\x62\xe3\xad\x7a\x7d\x79\x1f\x80\xbe"
+"\x2e\xd9\xf8\x93\x57\xd7\xe2\xd3\x3c\x25\x70\xb6\xf6\x51\xd9\x91\x75"
+"\xa6\x95\x91\xae\x04\x86\xe0\x72\x30\x83\x6a\x79\x3c\x10\xcd\x9f\x34"
+"\x60\x01\xc9\xea\x8e\xb3\x4b\xf0\x12\x5f\xbb\x93\xb7\x6d\x7b\x1d\x8c"
+"\x9e\x39\x48\xac\x20\xbb\x3a\x4e\xbe\x63\x95\x88\xf2\xd7\x4e\x63\x22"
+"\xe0\xe3\x2c\x80\x64\x02\x7d\xbd\xfa\x73\x56\x6e\x3c\x58\xb0\x69\xf3"
+"\x5d\x2e\x9d\xac\xcd\xe5\x67\xf7\x31\xd8\x9f\x31\xfe\x4d\xdf\x2a\x90"
+"\x33\xd4\x0f\xaf\xd0\xe2\x93\x7c\x40\x64\xb2\x82\xe5\xbc\x2d\xe2\x40"
+"\x25\x66\x5f\x2c\x59\xab\x48\xa0\x30\x5d\xcc\xa1\xc9\x00\xe7\x23\xbe"
+"\x01\x38\xab\xb7\x3e\x2e\x36\xf6\x73\x4e\xba\x0e\xb5\x3b\x46\x4f\xee"
+"\x62\xb7\x1b\xdc\x04\x0f\x95\x05\x80\x3d\x42\xf5\xce\xef\x60\x48\xef"
+"\x95\x2b\x35\xee\x75\xee\x71\x42\x77\xfb\x57\xf9\x12\xd8\x78\x91\x2f"
+"\xb4\xab\x6b\xe6\xb3\xd5\x6d\x7c\xe8\xcc\x9f\x67\x9e\xcd\x84\xd1\xe1"
+"\x82\x90\xca\x01\xc1\xc9\xce\x3b\x80\x48\xe0\x53\xa2\xf1\x24\x33\x49"
+"\x6a\xa2\x1d\x49\x45\xc1\x50\xae\xf6\x6c\xaa\xb9\x0c\x46\xe2\x57\xe5"
+"\xfb\xa7\xaf\x72\xa3\xab\x0c\xc5\x6b\xe2\xb1\x73\x0e\x97\x21\xd1\xb5"
+"\x88\x1a\xff\x00\x3f\xbb\x96\xd8\x86\xb7\xc3\x05\xfd\xee\x09\x09\xd7"
+"\x3d\x79\x00\x9a\x22\xf1\x58\x94\x31\x3a\x36\xb1\x1e\xd1\x2f\x0f\x6a"
+"\x79\x31\x82\x48\x1c\xf3\xbb\x18\x5e\xcd\xda\xb9\x26\xad\x26\xad\x6d"
+"\x4d\xe2\xef\x14\xcb\x16\xba\xf4\x77\x77\x4b\x02\xc3\xa9\x46\xc4\x29"
+"\xdf\x2d\x9b\xa2\x72\xa0\x8f\x98\xae\x3b\xf3\xe8\x41\x07\x91\x5e\x7d"
+"\x69\xf1\xe6\xd6\xe7\xe2\x53\x78\x4b\xfb\x2e\xf4\x03\x33\xdb\xad\xea"
+"\x32\x32\x6f\x42\x41\x0c\x31\xc6\x7b\x00\x49\xc0\x24\x81\x5d\xfc\xde"
+"\x22\xf2\xee\xde\x04\xd2\xb5\x49\xf6\x01\x99\x12\x1c\x21\x25\x9c\x60"
+"\x16\x61\x9f\xb9\x9c\xf4\xc3\x29\xcf\xcc\x2a\xae\x93\x0e\x9f\x71\xae"
+"\x49\x7d\x1f\x86\xe6\xb2\xbf\x95\x48\x96\xfe\x6b\x78\xd1\x8e\xde\x30"
+"\x5b\x76\xe3\x9f\x6c\xf1\xd7\x8c\x56\x68\xa7\xa1\xb9\x04\x86\x71\x90"
+"\xd3\x27\x00\xfc\xe8\x17\xaf\xd4\x54\xbe\x5b\x7f\xcf\x57\xfc\x87\xf8"
+"\x57\x0d\xf0\xbb\xe2\xc5\x87\xc5\x49\x3c\x41\xfd\x9f\x69\x3d\xb4\x3a"
+"\x4d\xfc\xd6\x3e\x6c\xac\x48\x9f\xcb\x9a\x48\xc4\x8a\x71\xb4\xab\x79"
+"\x5b\xd4\xa9\x6f\x95\xd7\x38\x6c\xa8\xee\xf6\x8f\x7f\xcc\xd3\x69\xad"
+"\xc4\x9a\x7b\x0d\xf2\xdb\xfe\x7a\xbf\xe4\x3f\xc2\x8f\x2d\xbf\xe7\xab"
+"\xfe\x43\xfc\x29\xdb\x47\xbf\xe6\x68\xda\x3d\xff\x00\x33\x48\x63\x7c"
+"\xb6\xff\x00\x9e\xaf\xf9\x0f\xf0\xa3\xcb\x6f\xf9\xea\xff\x00\x90\xff"
+"\x00\x0a\x76\xd1\xef\xf9\x9a\x36\x8f\x7f\xcc\xd0\x03\x7c\xb6\xff\x00"
+"\x9e\xaf\xf9\x0f\xf0\xa3\xcb\x6f\xf9\xea\xff\x00\x90\xff\x00\x0a\x76"
+"\xd1\xef\xf9\x9a\x36\x8f\x7f\xcc\xd0\x03\x7c\xb6\xff\x00\x9e\xaf\xf9"
+"\x0f\xf0\xa3\xcb\x6f\xf9\xea\xff\x00\x90\xff\x00\x0a\x76\xd1\xef\xf9"
+"\x9a\x36\x8f\x7f\xcc\xd0\x03\x7c\xb6\xff\x00\x9e\xaf\xf9\x0f\xf0\xa3"
+"\xcb\x6f\xf9\xea\xff\x00\x90\xff\x00\x0a\x76\xd1\xef\xf9\x9a\x36\x8f"
+"\x7f\xcc\xd0\x03\x7c\xb6\xff\x00\x9e\xaf\xf9\x0f\xf0\xac\xbd\x73\x59"
+"\x97\x46\x58\xfc\xbb\x0d\x47\x53\x79\x08\x01\x2c\xa3\x46\x23\x90\x39"
+"\x2c\x54\x0e\xbd\xc8\xe8\x6b\x5b\x68\xf7\xfc\xcd\x65\x6b\xba\xd4\x9a"
+"\x32\xc4\x62\xd2\xaf\xf5\x46\x91\x82\xec\xb2\x55\x24\x64\x81\xc9\x66"
+"\x50\x39\x23\xa9\x03\x19\x39\xe2\xb4\xa6\xaf\x24\xad\x72\x64\xec\xb7"
+"\x39\xfb\xaf\x89\x02\xd3\x53\x36\x0d\xe1\xdf\x12\xcb\x38\x85\x66\x66"
+"\x86\xc1\x64\x8d\x77\x67\xe5\x2e\x18\xae\xee\x09\xc6\x7d\x3b\xf1\x5d"
+"\x76\x9d\x72\x6f\x6c\x2d\xee\x1a\x29\xad\xcc\xa8\xb2\x18\x6e\x14\x2c"
+"\x91\xe4\x67\x6b\x01\xc0\x23\xbd\x72\xb7\xbf\x10\xe5\xb1\xd4\x05\x9b"
+"\x78\x47\xc4\xb3\x48\x62\x59\x77\xc1\x6b\x1b\xc6\x33\xbb\xe5\xde\x24"
+"\xdb\xb8\x6d\xe4\x67\xb8\xf5\xae\xab\x4e\xbb\xfb\x7d\x85\xbd\xc9\x82"
+"\x6b\x5f\x3a\x35\x93\xc8\xb9\x5d\xb2\x47\x91\x9d\xac\x32\x70\x47\x71"
+"\x9a\xe8\xad\x0e\x58\xa7\xc9\x6f\x9d\xee\x63\x4e\x57\x93\x5c\xd7\xf9"
+"\x58\xf3\x2f\x1f\x7f\xc8\xcb\x3f\xfb\x89\xfc\xa8\xa3\xc7\xdf\xf2\x32"
+"\xcf\xfe\xe2\x7f\x2a\x2b\xc9\x7b\x9d\xa7\xaa\x91\xb8\x11\xeb\xe8\x71"
+"\x5e\x23\xf0\x77\xe3\xa5\x8f\xc4\xdd\x35\xee\xb4\xdf\x0e\xdc\xb5\xe0"
+"\xfb\x32\xdc\xc3\xa7\x39\x22\x23\x20\x9c\x8d\xcf\x21\x8f\x2a\x82\x1c"
+"\x12\x70\x37\x36\x17\x70\x2a\x5b\xdb\x99\x77\x29\x07\x38\x23\x1c\x1c"
+"\x57\xc9\xff\x00\xb2\x37\xed\x37\xe2\xbf\x8f\x2d\xe2\x91\xaa\xf8\x62"
+"\xd5\x7f\xb3\x20\xd2\xa7\x8b\xfb\x1d\xe4\x83\x70\xb9\x17\x26\x5d\xde"
+"\x74\xc4\x36\xcf\x21\x40\xc1\x07\x2d\xc8\x1d\xb7\x57\xf6\xd0\xd7\x4d"
+"\x74\xef\xf3\xe9\x6f\xc4\xe9\x82\x87\xd5\x2a\xb7\x0b\xca\xf1\xb4\xaf"
+"\xf0\xef\x75\xcb\xd7\x9b\x4d\x7a\x5b\xcc\xfa\x5c\xea\x13\xaf\x9f\x9d"
+"\x1f\x51\x3b\x06\x53\x6c\xd1\x9f\x33\xe4\x56\xc0\xcc\x83\x07\x24\xa7"
+"\x38\xe5\x4f\x38\xc1\xa6\xdc\x6a\x57\x31\x5b\x5d\x49\x1e\x89\xa9\x4b"
+"\x24\x4a\xc6\x38\x84\xf1\x83\x31\x00\x10\x14\xf9\xb8\x19\x27\x1f\x36"
+"\x3a\x1f\x6a\x86\xf3\x50\xbd\x84\xaa\xdb\xe8\x1a\x95\xc3\x19\xa2\x8c"
+"\xb3\x5e\x44\x88\x11\x88\xdf\x26\x7c\xd2\x70\x80\x9c\x8c\x64\x91\x80"
+"\x08\x39\xa7\x9b\xeb\xb8\xb4\xe5\x9e\x4d\x0f\x51\x92\xe7\x6f\xcd\x6b"
+"\x6f\x77\x1b\x10\x7d\x03\x34\x8a\x08\xf7\x38\xfc\x2b\xa7\xd9\xcb\x7d"
+"\x35\xf3\x5f\xe6\x79\xdc\xeb\x6f\xd1\x8f\x4d\x4e\x59\x62\x9e\x44\xd2"
+"\x35\x22\x23\x97\xcb\x55\x69\x15\x5a\x4f\x9d\x94\xb2\x83\x20\xf9\x46"
+"\xdd\xd9\x38\xc8\x23\x19\xa1\xb5\x88\xed\xac\x35\x1b\xcb\xdb\x2b\xfb"
+"\x08\x6c\xa2\x33\xb9\x99\xb7\xef\x40\x85\x89\x51\x1b\x31\x24\x60\x82"
+"\x3a\xe4\x70\x0e\x46\x64\x79\xe5\x51\x19\x1a\x65\xfb\x07\x38\x21\x6e"
+"\x13\x72\x7d\xfe\x5b\x32\x63\x1f\x2a\xf4\x24\xfc\xe3\x8e\x0e\x2b\xcf"
+"\x79\x75\xf6\x98\xa1\x4d\x0b\x51\x92\x16\x62\x25\x95\xae\xe3\x0a\xab"
+"\xe5\x33\x64\x0f\x34\x96\x25\x82\xa6\x38\xe5\xb3\x9c\x0c\xd6\x36\xb3"
+"\xb1\x6b\x55\x73\x33\xe1\x47\x8d\x17\xe2\x77\x80\x34\xaf\x13\x7d\x8d"
+"\xf4\xdf\xb7\x09\x4f\xd9\x7e\xd2\xf2\x79\x7b\x25\x78\xf1\xb8\xaa\x92"
+"\x7e\x4c\xfd\xd1\x8c\xe3\x9c\x64\xf5\xbf\x66\x4f\x59\x3f\xef\xe3\x7f"
+"\x8d\x64\xc7\x73\x70\xca\xc8\x74\x9b\xd8\xe5\xe4\x26\xeb\x95\xf2\xc9"
+"\xc1\xc7\xcc\x1c\x90\x0e\x07\x3b\x78\xcf\x7a\xa7\xe2\x2f\x12\x43\xe1"
+"\x8f\x0d\x6a\xfa\xcd\xfd\x8d\xf4\x10\x69\xf0\xb4\xa1\x0c\xdb\x9a\x62"
+"\x09\x01\x57\x63\x37\xde\x21\x71\x9c\x7d\xf1\x9c\x60\xe1\xbb\x37\xa0"
+"\x95\xd2\xd4\xde\xb7\xd3\x6d\xed\x23\x11\xc1\x19\x86\x30\x49\x09\x1b"
+"\x15\x1f\x90\x35\x27\xd9\x93\xd6\x4f\xfb\xf8\xdf\xe3\x5c\x1f\xc1\x9f"
+"\x88\xab\xf1\x77\xc2\x12\x6b\x8d\xa1\x6a\xbe\x1e\x29\x79\x35\xa0\xb7"
+"\xbf\x77\x1e\x68\x42\x31\x2c\x6c\x70\x59\x08\x20\x67\x03\xe6\x57\x03"
+"\x20\x06\x3d\xdf\xd8\xe3\xfe\xf4\xbf\xf7\xf9\xff\x00\xc6\x86\xac\xec"
+"\xc1\x3b\xab\x8b\xf6\x64\xf5\x93\xfe\xfe\x37\xf8\xd1\xf6\x64\xf5\x93"
+"\xfe\xfe\x37\xf8\xd2\x7d\x8e\x3f\xef\x4b\xff\x00\x7f\x9f\xfc\x68\xfb"
+"\x1c\x7f\xde\x97\xfe\xff\x00\x3f\xf8\xd2\x18\xbf\x66\x4f\x59\x3f\xef"
+"\xe3\x7f\x8d\x1f\x66\x4f\x59\x3f\xef\xe3\x7f\x8d\x27\xd8\xe3\xfe\xf4"
+"\xbf\xf7\xf9\xff\x00\xc6\x8f\xb1\xc7\xfd\xe9\x7f\xef\xf3\xff\x00\x8d"
+"\x00\x2f\xd9\x93\xd6\x4f\xfb\xf8\xdf\xe3\x47\xd9\x93\xd6\x4f\xfb\xf8"
+"\xdf\xe3\x49\xf6\x38\xff\x00\xbd\x2f\xfd\xfe\x7f\xf1\xa3\xec\x71\xff"
+"\x00\x7a\x5f\xfb\xfc\xff\x00\xe3\x40\x0b\xf6\x64\xf5\x93\xfe\xfe\x37"
+"\xf8\xd1\xf6\x64\xf5\x93\xfe\xfe\x37\xf8\xd2\x7d\x8e\x3f\xef\x4b\xff"
+"\x00\x7f\x9f\xfc\x68\xfb\x1c\x7f\xde\x97\xfe\xff\x00\x3f\xf8\xd0\x02"
+"\xfd\x99\x3d\x64\xff\x00\xbf\x8d\xfe\x34\x7d\x99\x3d\x64\xff\x00\xbf"
+"\x8d\xfe\x34\x9f\x63\x8f\xfb\xd2\xff\x00\xdf\xe7\xff\x00\x1a\x3e\xc7"
+"\x1f\xf7\xa5\xff\x00\xbf\xcf\xfe\x34\x00\xbf\x66\x4f\x59\x3f\xef\xe3"
+"\x7f\x8d\x67\xea\xd3\xdc\x58\x2c\x66\xd7\x4e\xba\xd4\x4b\x1c\x11\x15"
+"\xc0\x4d\xbf\xf7\xd3\x0a\xbf\xf6\x38\xff\x00\xbd\x2f\xfd\xfe\x7f\xf1"
+"\xaa\xba\x98\x7b\x2b\x39\x25\xb7\xb4\xba\xd4\x26\x55\x25\x2d\xe1\xb8"
+"\xda\xce\x40\x24\x0c\xbb\x80\x32\x46\x33\x9e\xf5\x51\x69\x3d\x55\xc4"
+"\xd5\xcc\x19\x7c\x51\xa8\x26\xa5\x25\x9a\x78\x4f\x5a\x98\xc7\x1a\x48"
+"\xd3\xac\xd1\x08\xb2\xdb\xbe\x50\xcd\x28\xdc\x46\xde\x71\xd3\x23\xd6"
+"\xba\x7b\x09\x1e\x6b\x38\x64\x92\x09\x2d\xa4\x75\x0c\xd0\xca\xc1\x99"
+"\x0f\xa1\x20\x91\xf9\x1a\xa1\x1c\xd2\xbc\x45\x9b\x4d\xbf\x8d\xb7\x60"
+"\x46\x6e\x10\xb6\x3e\x5e\x78\x93\x1f\xc4\x7b\xe7\xe5\x3e\xd9\xbf\x62"
+"\xc5\xed\x22\x76\x86\x5b\x77\x75\x0c\xd0\xce\xe1\x9d\x09\xfe\x12\x43"
+"\x30\xc8\xf6\x24\x7a\x1a\xb9\xca\x32\x4b\x96\x29\x7d\xff\x00\xe6\x44"
+"\x62\xd3\xd6\x57\xfb\x8f\x3f\xf1\x6f\x86\x75\xad\x5b\x5d\xb8\xb8\xb7"
+"\x8f\x4f\x10\x1c\x2a\x79\xb7\x4e\xac\x40\x1d\x48\x11\x9c\x7e\x66\x8a"
+"\xed\xa7\xff\x00\x5c\xf8\x6d\xbc\xfa\xe3\x3f\xa8\xa2\xb9\x1a\x46\xf7"
+"\x34\x6b\xc7\x7e\x10\xfc\x5a\xf1\x67\xc4\x0b\x5d\x68\xea\x1e\x16\x4b"
+"\x4b\x9d\x39\xad\xe3\x54\xf3\x1e\xdc\x4e\x58\xc8\x24\x65\xdf\xbb\x18"
+"\xd8\xb8\x52\x73\xc9\xc9\xe9\x5e\xc4\x46\x46\x0d\x79\x1f\xc2\x7f\x88"
+"\x9e\x36\xf1\xa5\xb6\xb6\x75\xcf\x08\x47\xa5\x5c\xd8\x98\x16\x04\x74"
+"\x96\xdd\x6e\x4b\x79\x9e\x61\x06\x40\x78\x1b\x53\x81\x9c\x67\x92\x72"
+"\x2a\x5b\xfd\xfd\x38\xdf\x7b\xe9\xd1\xfa\xbe\x84\xb5\xee\x36\x77\xf2"
+"\xea\xfa\xb2\x2b\x14\xf0\xf5\xc4\x84\x79\x7c\x7d\xae\x20\x4e\xe0\x0b"
+"\x63\xe6\xfe\x12\x48\x39\xc6\x70\x71\x9e\x33\x25\x8e\xa9\xa9\xdc\xbc"
+"\x4b\x3e\x87\x35\xa6\xe8\xd9\xdd\x9e\xea\x36\x08\xc1\x88\x0b\xf2\xb1"
+"\x24\x90\x01\xce\x31\xcf\x3c\xf1\x55\xe5\xd4\xb5\xb5\x52\x53\xc3\xa8"
+"\xe4\x2a\x1c\x1b\xd4\x19\x25\x41\x60\x38\xfe\x12\x71\x9e\xf8\x24\x76"
+"\x06\x7b\x2b\xed\x52\x76\x85\x6e\x34\x35\xb5\xdd\x1b\x34\x8c\x6e\x51"
+"\x82\x30\x24\x05\x18\xe4\xe4\x00\x73\xd3\x9f\x6a\xf4\x65\x1b\x41\xfb"
+"\xab\xef\xd7\x6e\xd7\xfd\x37\x30\x4f\xde\x5a\xbf\xbb\xfe\x01\x1a\xeb"
+"\x3a\xab\x5b\x47\x27\xfc\x23\xb7\x4b\x23\x06\xcc\x2d\x75\x0e\xe4\x21"
+"\x54\x80\x48\x72\x39\x24\xaf\x04\xe3\x6f\xa1\x15\x32\xea\xd7\xe7\x78"
+"\x3a\x1d\xd8\x28\xc1\x7f\xd7\xc5\x86\xcb\xa8\x24\x7c\xfd\x02\xb1\x6e"
+"\xdf\x74\x8e\xb8\x06\x38\xaf\xf5\x57\x82\x07\x6d\x05\x63\x91\xdf\x6c"
+"\x91\x9b\xb4\x3e\x58\xd8\x0e\xec\x81\xcf\xcd\x95\xc0\xfa\xf4\xa9\x5a"
+"\xf3\x51\x10\x23\x8d\x1d\x4b\x9d\x9b\xe3\x17\x09\x91\x92\x43\x60\xf4"
+"\x3b\x46\x0f\xb8\x27\x1c\x8c\x1e\x6e\xa6\xe6\x0f\xc4\x2f\x18\x6b\x1e"
+"\x14\xf8\x7f\xe2\x5d\x76\xcf\x43\x92\x5b\xcd\x33\x4c\xbb\xbd\x86\x19"
+"\x64\x12\x07\x78\xa1\x95\xd0\x15\x46\xdc\xd9\x28\x83\x6a\xf2\x77\x8c"
+"\x74\x38\xf2\x0f\xd9\x6f\xf6\x85\xf1\xef\xc6\x69\xbc\x4e\x3c\x47\xe1"
+"\xab\x2b\x78\xb4\xe8\xb4\xd9\xad\xce\x9f\x0c\xf6\xac\x7e\xd3\x1c\xcf"
+"\x22\x37\x9e\xe4\x33\xc6\x63\x89\x48\x52\x00\xdc\xdc\x92\x31\x5e\xbf"
+"\xe2\xaf\x17\xeb\x5e\x1d\xf0\x4e\xab\xaa\x45\xe1\x49\xaf\xb5\xb8\x87"
+"\x97\x61\xa4\xdb\xca\x24\x37\x72\x94\x05\x15\x9d\x54\x88\xd4\xb9\x2a"
+"\x59\xb8\x1b\x73\xdc\x0a\xd3\xf0\x1e\xb7\x7f\xe2\x9f\x0b\xda\xea\x7a"
+"\xb6\x83\x37\x87\x6f\x27\x69\x3f\xe2\x5d\x74\x41\x96\x34\x12\x32\xa1"
+"\x70\x38\x52\xca\x03\x6d\xc9\xc6\xec\x66\x84\xec\xf5\x2b\x4e\x47\xa7"
+"\xcc\xa9\x77\xe2\x5f\x11\xc5\x67\x6b\x24\x1e\x0c\xbb\x9e\x79\x24\x55"
+"\x96\x13\xa8\xdb\xa7\x94\x85\xc8\x2d\x9d\xe4\x12\x14\x6e\xc0\xeb\x90"
+"\x33\xd7\x1a\x96\xba\x9e\xa7\x35\xe5\x94\x72\xe8\x92\xdb\xc1\x34\x65"
+"\xe6\x9d\xae\xa3\x6f\x21\xbe\x6c\x21\x50\x72\xc7\x81\xc8\xe3\xe6\xef"
+"\x83\x5a\xfe\x52\x7f\x74\x7e\x54\x79\x49\xfd\xd1\xf9\x56\xee\xa4\x5a"
+"\xb2\x82\x5f\x7f\xf9\xf4\xff\x00\x87\x30\x51\x69\xdf\x99\xfe\x1f\xe4"
+"\x79\x3e\x91\xf1\xba\xff\x00\x56\xf8\x89\xe2\x0d\x15\x7c\x1d\xa8\xc7"
+"\xe1\xad\x1a\x39\x5e\x5f\x12\x17\x63\x1c\x86\x36\x85\x4a\xac\x41\x0b"
+"\x16\xcb\xcc\x71\xfd\xdb\x72\x46\xed\xc0\x0d\x3f\x83\x1f\x14\xb5\x2f"
+"\x8a\xba\x6d\xf5\xed\xff\x00\x84\x75\x0f\x0a\x45\x09\x8b\xc9\x17\xee"
+"\x49\x9f\x7a\x96\x3b\x48\x50\xa7\x68\xdb\x92\xac\xc3\x2c\x47\x05\x48"
+"\xaf\x45\xf2\x93\xfb\xa3\xf2\xa3\xca\x4f\xee\x8f\xca\xb9\x92\x69\xea"
+"\xcd\x9b\x4d\x68\x85\xda\x3d\xff\x00\x33\x46\xd1\xef\xf9\x9a\x4f\x29"
+"\x3f\xba\x3f\x2a\x3c\xa4\xfe\xe8\xfc\xaa\x89\x17\x68\xf7\xfc\xcd\x1b"
+"\x47\xbf\xe6\x69\x3c\xa4\xfe\xe8\xfc\xa8\xf2\x93\xfb\xa3\xf2\xa0\x05"
+"\xda\x3d\xff\x00\x33\x46\xd1\xef\xf9\x9a\x4f\x29\x3f\xba\x3f\x2a\x3c"
+"\xa4\xfe\xe8\xfc\xa8\x01\x76\x8f\x7f\xcc\xd1\xb4\x7b\xfe\x66\x93\xca"
+"\x4f\xee\x8f\xca\x8f\x29\x3f\xba\x3f\x2a\x00\x5d\xa3\xdf\xf3\x35\x97"
+"\xad\xde\xea\x76\x42\x11\xa6\xe9\x63\x52\x67\x60\x18\xbd\xd0\x85\x63"
+"\x04\x81\x92\x48\x27\x00\x12\x78\x04\xf0\x78\xce\x2b\x4f\xca\x4f\xee"
+"\x8f\xca\xb2\xf5\xa9\xf5\x2b\x5f\x24\x69\x9a\x64\x37\xec\xcc\x03\xf9"
+"\xb7\x02\x15\x41\x90\x32\x4e\x09\xe3\x39\xe0\x1e\x87\x8c\xe2\xb4\xa7"
+"\xac\x96\x89\xfa\xe8\xbf\x34\x4c\xb6\x31\x6f\xbc\x4b\xe2\x7b\x6d\x48"
+"\x5a\xc3\xe0\xd9\x2f\x21\xf2\x55\xcd\xdc\x5a\x9c\x4b\x18\x73\x9c\xa6"
+"\x1f\x0c\x71\x81\xc8\x18\xe7\xb5\x75\x1a\x6c\xd3\xdc\x69\xf6\xd2\xdd"
+"\x5b\x1b\x3b\x99\x23\x57\x96\xd8\xc8\x24\x31\x31\x19\x2b\xb8\x70\x70"
+"\x78\xc8\xe2\xb9\x5b\xdd\x77\xc5\xb0\x6a\x86\xda\x0f\x07\x5b\x5d\x5a"
+"\x88\x55\xfe\xd8\x35\x54\x55\xde\x73\x94\x0a\x53\x77\x18\x1c\xe3\x1c"
+"\xfb\x57\x55\xa6\xc9\x3c\xd6\x16\xf2\x5d\x5b\x0b\x3b\x97\x8d\x5a\x5b"
+"\x75\x70\xe2\x36\x23\x95\xdc\x38\x38\xe9\x9a\xe8\xad\x1b\x45\x7b\xb1"
+"\x5e\x8e\xef\xe7\xef\x3b\x7d\xc8\xc6\x9b\xbc\x9e\xad\xfa\xab\x7e\x88"
+"\xad\x70\xc4\x4c\xdb\x7d\x79\xa2\x89\xd8\xac\xcf\x8c\x72\x73\xc9\xc7"
+"\xf5\x14\x57\x9a\x75\xa3\x45\x94\x30\x20\x80\x41\xe0\x83\xde\xbc\x53"
+"\xe1\x37\x8f\x7c\x6d\xe2\xed\x21\x9f\x53\xf0\x4c\xd6\xda\x84\x62\xd8"
+"\x4c\xba\xad\xb9\xd3\x63\xcb\xf9\xe6\x56\x8f\x2b\x21\x65\x4d\x91\x28"
+"\x1c\x93\xb8\x31\x09\x9d\xab\xed\x64\x02\x08\x23\x20\xf5\x06\xbc\x2f"
+"\xe0\xaf\x89\x3e\x28\xf8\x94\xeb\xcd\xe2\x7d\x22\xca\xdb\xc8\xba\x85"
+"\x6d\x92\xff\x00\x4f\x92\xc0\x18\x88\x9b\xcc\xd8\x70\xdb\xcf\x10\x10"
+"\x7a\x7c\xcd\x9c\x1f\x94\x74\x42\x94\xa5\x35\x51\x35\x65\xba\x7d\x6f"
+"\xb7\x55\xb5\x8b\x55\xe1\x0a\x33\xa2\xe1\x79\x4a\xcd\x4b\xaa\xb5\xee"
+"\xb6\x7a\x3b\xfe\x08\xf5\x39\xbf\xb6\x04\x77\x1e\x56\x89\xa6\xb3\x84"
+"\xcc\x3b\xee\x88\x05\xbc\xb5\x38\x6f\xdd\xff\x00\x7f\x70\xc8\xec\x01"
+"\xef\x81\x2b\x1d\x45\x44\x18\xd0\xec\xd8\xb1\x22\x50\x2e\x87\xc9\xf3"
+"\x0e\x9f\xbb\xf9\xbe\x5c\x9e\xdc\xe0\x74\x3b\x84\xb6\xb0\xea\x46\x61"
+"\xf6\x9b\x7d\x35\x62\xcf\x26\x22\xc5\xb1\xb3\xa7\x20\x7f\x17\x19\xee"
+"\x06\x70\x09\xc0\x8e\x44\xd5\xc4\x65\x92\xcb\x4a\x2e\x19\xc0\x46\x99"
+"\xc0\x2b\x8f\x90\xee\xf2\xf8\x24\xf5\x18\x38\xcf\x04\xe3\x05\xc9\x59"
+"\x9c\xe9\xdd\x12\x3a\x5e\x09\x50\x2e\x93\x66\x62\xdc\xc1\x98\xcd\xce"
+"\xdf\xde\x6d\x20\x6c\xea\x71\x1f\x7e\x37\xb7\x5d\xbc\xf8\x7d\x8f\x8f"
+"\x3e\x28\x4f\xfb\x46\xea\x5e\x1c\x93\xc3\x50\x27\x82\xad\xae\x12\x28"
+"\xa6\x3a\x34\xeb\x1c\xc8\xda\x50\x9c\xb8\xbe\x3f\xbb\xc0\xbb\xdd\x11"
+"\xf9\x09\xe4\x0c\x64\x1a\xf6\xf2\x9a\x98\x57\x22\xc7\x4c\x2d\x92\x14"
+"\x19\xd8\x02\x37\x8c\x12\x7c\xbe\x3e\x4d\xc7\x1d\x88\x03\x24\x1c\x8b"
+"\x56\xb0\xca\xd2\x49\xf6\x9b\x7b\x54\x4c\x0d\x9e\x51\x2c\x49\xe7\x39"
+"\xc8\x1e\xd8\xfc\x6a\x0b\x4e\xdd\x0c\x88\x1b\x5d\x66\xbc\xf3\x74\x0d"
+"\x29\x42\x85\x36\xc0\x5f\x37\xce\x76\xfc\xc1\xcf\x93\xf2\x80\x7b\x80"
+"\x78\x3d\x38\xab\xfa\x4a\x5d\xdc\x09\x3f\xb4\x74\x9b\x4b\x22\x31\xb3"
+"\xc8\x9f\xce\x0d\xc7\x39\xca\x2e\x2b\x47\xec\xf0\x7f\xcf\x38\xff\x00"
+"\xef\x91\x47\xd9\xe0\xff\x00\x9e\x71\xff\x00\xdf\x22\xb5\x94\xd3\x5a"
+"\x45\x2f\xbf\xfc\xcc\xd4\x5a\xea\x79\x17\x86\x7e\x28\xf8\xa3\x5c\xd0"
+"\xbc\x53\x77\x37\xc3\x5b\xab\x3b\xbd\x36\x2b\x99\xf4\xf8\x6e\x73\x08"
+"\xbe\x0a\xae\xf0\x26\x36\x33\x23\xb6\x11\x4e\x03\x10\x58\x9d\xa3\x85"
+"\xae\x97\xe0\xee\xbf\xe2\x0f\x19\x78\x31\x35\x1f\x17\xf8\x51\x3c\x2f"
+"\xab\xfd\xa2\x48\xfe\xc4\x47\x2d\x18\x3f\x2c\x9b\x4e\x4a\xe7\x91\x82"
+"\x4f\x4c\xe7\x07\x03\xb8\xfb\x3c\x1f\xf3\xce\x3f\xfb\xe4\x51\xf6\x78"
+"\x3f\xe7\x9c\x7f\xf7\xc8\xac\x2c\xf9\xaf\x7f\x97\xf5\xaf\x9f\xcf\xb6"
+"\x86\xca\x49\x43\x92\xd7\x7d\xfa\xff\x00\x97\xe1\xd0\x4f\xb1\x5b\xff"
+"\x00\xcf\x08\xbf\xef\x81\x47\xd8\xad\xff\x00\xe7\x84\x5f\xf7\xc0\xa5"
+"\xfb\x3c\x1f\xf3\xce\x3f\xfb\xe4\x51\xf6\x78\x3f\xe7\x9c\x7f\xf7\xc8"
+"\xaa\x20\x4f\xb1\x5b\xff\x00\xcf\x08\xbf\xef\x81\x47\xd8\xad\xff\x00"
+"\xe7\x84\x5f\xf7\xc0\xa5\xfb\x3c\x1f\xf3\xce\x3f\xfb\xe4\x51\xf6\x78"
+"\x3f\xe7\x9c\x7f\xf7\xc8\xa0\x04\xfb\x15\xbf\xfc\xf0\x8b\xfe\xf8\x14"
+"\x7d\x8a\xdf\xfe\x78\x45\xff\x00\x7c\x0a\x5f\xb3\xc1\xff\x00\x3c\xe3"
+"\xff\x00\xbe\x45\x1f\x67\x83\xfe\x79\xc7\xff\x00\x7c\x8a\x00\x4f\xb1"
+"\x5b\xff\x00\xcf\x08\xbf\xef\x81\x47\xd8\xad\xff\x00\xe7\x84\x5f\xf7"
+"\xc0\xa5\xfb\x3c\x1f\xf3\xce\x3f\xfb\xe4\x51\xf6\x78\x3f\xe7\x9c\x7f"
+"\xf7\xc8\xa0\x04\xfb\x15\xbf\xfc\xf0\x8b\xfe\xf8\x14\x7d\x8a\xdf\xfe"
+"\x78\x45\xff\x00\x7c\x0a\x5f\xb3\xc1\xff\x00\x3c\xe3\xff\x00\xbe\x45"
+"\x1f\x67\x83\xfe\x79\xc7\xff\x00\x7c\x8a\x00\x4f\xb1\x5b\xff\x00\xcf"
+"\x08\xbf\xef\x81\x59\xfa\xb4\x17\x91\xf9\x5f\xd9\xb6\x16\x53\x92\xd8"
+"\x73\x70\xdb\x30\x3d\x46\x14\xfd\x7f\x0a\xd1\xfb\x3c\x1f\xf3\xce\x3f"
+"\xfb\xe4\x56\x7e\xad\x05\xff\x00\xee\xc6\x99\x16\x9f\xc9\xf9\xda\xec"
+"\x37\x03\xd8\x28\xe7\xf3\x15\x70\xf8\xbf\xcc\x99\x6c\x62\x4f\x71\xe2"
+"\x75\xd5\x64\x82\x2f\x0c\xe9\x52\x59\x2c\x48\xc2\xed\xef\xf6\xef\x73"
+"\xbb\x72\x84\xf2\x89\xe3\x0b\xd7\x1f\x7b\x8c\xe0\x8a\xea\x34\xf1\x28"
+"\xb2\x87\xcf\x86\x3b\x79\xca\x83\x24\x71\x1c\xaa\xb7\x70\x0e\x06\x6b"
+"\x96\xb9\x3e\x30\xfe\xd3\x78\xad\xb4\xdf\x0f\xb5\x8a\xc4\xa7\xed\x13"
+"\xcf\x22\xb3\xc8\x73\xb8\x04\x08\xdc\x0e\x07\x24\x75\xcf\x3d\x2b\xaa"
+"\xb0\x59\x96\xca\x11\x70\x90\xa5\xc6\xc1\xe6\xad\xbe\x7c\xbd\xd8\xe7"
+"\x6e\x70\x71\x9f\x5a\xde\xb2\x4a\x2a\xca\x2b\xd1\xdf\xef\xd5\x99\x53"
+"\x6d\xb7\xab\xf9\xaf\xf8\x05\x59\xf3\xe7\x3e\x09\x1c\xf6\x19\xa2\x89"
+"\xc0\x33\x3e\x71\xd7\xb9\xc5\x15\xe7\x9d\x26\x89\xc6\x0e\x7a\x77\xcd"
+"\x72\x2c\xde\x31\x0b\x2a\xa8\xf0\xf3\x38\x99\x3c\xb7\x3e\x70\x56\x8b"
+"\x07\x7e\x47\x24\x3e\x71\x8e\x48\xe6\xba\xe2\x70\x32\x78\x15\xe4\x1f"
+"\x09\x2e\xfe\x28\x5d\x5b\xeb\x91\xf8\xc1\xad\x6d\x67\x84\xc0\x9a\x7c"
+"\xb3\x47\x19\x59\x40\xf3\x3c\xd6\x61\x1b\x64\x93\xf2\x7f\x77\x8c\x63"
+"\xbd\x6f\x1a\xde\xce\xa4\x61\xcb\x7e\x6e\xb6\xd1\x5b\xbf\x63\x39\x43"
+"\x99\x5e\xfb\x1d\xae\x80\xfe\x2b\x33\xcc\xba\xda\xe8\x29\x09\x56\xf2"
+"\x9f\x4f\x69\x58\x83\x93\xb7\x70\x7c\x76\x23\xa7\xa1\xe7\x91\x8b\x80"
+"\xeb\xa4\xdb\x67\xfb\x21\x41\x8b\xf7\xf8\xf3\x0e\xc9\x37\x8f\xb9\xd3"
+"\x72\xec\xdd\xd7\x07\x20\x76\x3c\x24\x92\x78\x8b\x69\xf2\xe6\xd1\xc1"
+"\xc2\x00\x59\x64\xe4\xe0\x6f\x3d\x78\x00\xe7\x03\xb8\x03\x24\x67\x22"
+"\x6b\x49\x75\xad\xf0\x8b\xa9\x74\xdd\x82\x36\xf3\x1e\x22\xf9\x2f\x93"
+"\xb7\x68\x3d\x06\x36\xe7\x24\xf7\xfa\xd7\x45\x4f\x7a\xf3\xd3\xd1\x19"
+"\xc3\xdd\xb2\xd7\xe6\x46\xaf\xad\x79\x30\x13\x1e\x92\x26\x2e\xbe\x6a"
+"\x89\x1f\x68\x5d\xad\xbb\x69\xdb\xc9\xdd\xb7\x19\x03\x82\x7d\x00\x37"
+"\x34\xb3\x78\x52\x6f\xed\x21\x64\x1f\x78\xf2\x85\xa9\x24\x6c\xd8\xb9"
+"\xdd\xbb\xbe\xfd\xfd\x3b\x6d\xef\x9a\xa9\x14\x9a\xf9\x82\x0f\x36\x6d"
+"\x24\x4d\xe6\x7e\xf7\x60\x90\xae\xcd\xa3\xee\xf3\x9c\xee\xcf\x5e\xd8"
+"\xef\x52\xa4\x9a\xbb\x40\xbb\xe6\xd3\x92\x70\x13\x76\xd0\xec\xa4\xee"
+"\x3b\xf1\xc8\x23\xe5\xdb\x8e\xbc\xe7\x39\xae\x63\x73\x4f\x30\xff\x00"
+"\xb1\xfa\x51\x98\x7f\xd8\xfd\x2b\x29\x9b\x59\xd9\x1e\xdb\xad\x34\xbe"
+"\x46\xfc\xc6\xe0\x63\x3c\xe3\xe6\xf4\xff\x00\x22\xb0\x7e\x21\x6b\x9e"
+"\x2b\xd2\x7c\x1c\x4f\x87\xf4\xc8\xf5\x4f\x10\xdc\x4a\xb0\x20\xb6\x91"
+"\x16\x3b\x75\x20\x96\x95\x8c\x84\x74\x0a\x40\xe0\xfc\xcc\x99\x1b\x77"
+"\x10\x9b\xb2\x6c\x71\x8f\x34\x94\x7b\x9d\x9e\x61\xff\x00\x63\xf4\xa3"
+"\x30\xff\x00\xb1\xfa\x56\x7f\x86\xae\x6f\xa4\xf0\xe6\x94\xfa\xc7\x97"
+"\x16\xae\xd6\x91\x1b\xc8\xd4\xae\x16\x6d\x83\xcc\x03\x04\x8f\xbd\x9e"
+"\x84\x8f\x73\x5a\x5e\x74\x7f\xdf\x5f\xce\x99\x23\x73\x0f\xfb\x1f\xa5"
+"\x19\x87\xfd\x8f\xd2\x9d\xe7\x47\xfd\xf5\xfc\xe8\xf3\xa3\xfe\xfa\xfe"
+"\x74\x00\xdc\xc3\xfe\xc7\xe9\x46\x61\xff\x00\x63\xf4\xa7\x79\xd1\xff"
+"\x00\x7d\x7f\x3a\x3c\xe8\xff\x00\xbe\xbf\x9d\x00\x37\x30\xff\x00\xb1"
+"\xfa\x51\x98\x7f\xd8\xfd\x29\xde\x74\x7f\xdf\x5f\xce\x8f\x3a\x3f\xef"
+"\xaf\xe7\x40\x0d\xcc\x3f\xec\x7e\x94\x66\x1f\xf6\x3f\x4a\x77\x9d\x1f"
+"\xf7\xd7\xf3\xa3\xce\x8f\xfb\xeb\xf9\xd0\x03\x73\x0f\xfb\x1f\xa5\x19"
+"\x87\xfd\x8f\xd2\x9d\xe7\x47\xfd\xf5\xfc\xe8\xf3\xa3\xfe\xfa\xfe\x74"
+"\x00\xdc\xc3\xfe\xc7\xe9\x59\x7a\xd8\xd5\x18\x46\x34\x89\x34\xd8\xc9"
+"\x3f\x3b\xde\xa3\x3e\xd1\x91\xc8\x55\x23\x3c\x67\x8c\x8e\x71\xcd\x6b"
+"\x79\xd1\xff\x00\x7d\x7f\x3a\xcb\xd6\xce\xab\x22\xc4\x34\x9b\xab\x08"
+"\x1b\x70\xf3\x1a\xf1\x19\xc0\x19\x19\xc0\x56\x19\xe3\x3c\x64\x72\x07"
+"\x35\xa5\x3f\x89\x6d\xf3\xd8\x99\x6c\x73\xb7\xa7\xc7\xa3\x55\x31\xd9"
+"\x9f\x0b\xb6\x9e\x21\x5f\xdf\x5c\x2c\xeb\x23\x4b\xce\xec\x20\x62\x02"
+"\xf4\xea\x73\xd7\xad\x76\x1a\x6f\xda\x3e\xc1\x6f\xf6\xbf\x23\xed\x7b"
+"\x07\x9d\xf6\x6c\xf9\x7b\xf1\xf3\x6d\xcf\x38\xcf\x4c\xd7\x2f\x78\x3c"
+"\x6e\x35\x3c\x5a\xdf\x78\x74\xe9\xde\x4a\xfc\xf3\x41\x30\x94\xc9\xf3"
+"\x6e\xf9\x43\xe3\x6f\xdd\xc7\x39\xeb\xed\x5d\x3e\x9a\x6e\x4e\x9f\x6f"
+"\xf6\xd7\x85\xef\x3c\xb5\xf3\x9a\xd8\x11\x1e\xfc\x7c\xdb\x73\xce\x33"
+"\xd3\x35\xd1\x59\xde\x2b\xe1\xf9\x6f\xf3\x31\xa6\xbd\xe7\xbf\xcc\xaf"
+"\x39\x22\x67\xc7\xaf\xa6\x68\xa4\xb8\x00\xcc\xd9\xc7\x5e\xf4\x57\x9a"
+"\x75\x9a\x44\x80\x09\x27\x00\x77\xaf\x19\xf8\x4d\x73\xf1\x62\xeb\x49"
+"\x31\x78\xb5\x2c\x74\x9b\xd8\xbe\xcf\x99\xef\x24\x8a\xeb\xcf\x1f\xbf"
+"\x33\x60\x40\xe0\x03\x9f\x20\x0f\xba\x00\xe3\xe7\x20\xb3\x14\x56\x96"
+"\x6a\xa4\x6a\x27\xb5\xf4\xe8\xef\xdf\xd3\xa1\xac\x6b\x72\xd0\x9d\x0e"
+"\x54\xf9\x9a\x77\xfb\x4a\xd7\xd9\xdf\x44\xef\xaf\x7b\x2e\xc7\xa6\x01"
+"\xab\x81\x3f\xfc\x4c\xf4\xf6\x66\x5f\xdd\x66\xdd\xb0\x8d\xe5\xa8\xe7"
+"\xf7\x9c\x8d\xe1\x9b\x1c\x1c\x10\x33\xc6\x6a\x5c\xea\x1b\xd3\xfd\x3e"
+"\xcc\x20\x70\x5c\x79\x47\x25\x73\xc8\x1f\x37\x04\x8c\x8c\xfe\x94\x51"
+"\x54\xf5\x77\x39\xcf\x9d\x3f\x67\x0b\x5f\xda\x09\x35\x5b\x49\x3e\x26"
+"\x6a\x13\x43\x6e\xda\x13\x25\xca\x6a\x0d\xa7\x4d\x1a\xea\x22\x68\xb0"
+"\xc8\x2d\x18\x31\x06\x3f\x33\xa9\xc0\xfa\xe0\x9f\xa0\x6d\x63\xd7\x23"
+"\x86\xe8\x4f\xab\xe9\xd3\x48\xc5\xbe\xce\xcb\x68\xca\x10\x67\xe5\xdd"
+"\xfb\xc3\xbb\x03\x23\x8c\x67\xda\x8a\x2a\xe3\x37\x15\x65\xf9\x04\xbd"
+"\xe7\xcc\x50\xf1\x5d\xd7\x89\x6d\x3c\x0f\xae\x9d\x2d\xe0\xd4\x7c\x44"
+"\xd0\xba\xe9\xc2\xc8\x47\x01\x46\x65\x0a\xad\xfb\xe6\x64\x25\x58\xb3"
+"\xfc\xd8\x04\x00\x31\xeb\xe7\xda\xee\xbd\xf1\xaa\xef\xe1\x2c\xcf\x67"
+"\xa2\xe8\xd6\x5e\x3b\x7b\x98\xe1\x48\xec\xef\x22\x68\x92\x21\x93\x24"
+"\xc3\xcd\x25\x70\x48\x0a\x14\x92\x70\xd9\x3c\xf0\x0a\x2a\x26\xf9\xf4"
+"\x7f\x86\x83\xa6\xfd\x9c\x94\x96\xb6\xef\xaa\xfb\x99\xeb\x1e\x17\xb8"
+"\xd4\x4f\x86\x74\x8f\xed\xf9\x2d\x06\xbb\xf6\x38\x7f\xb4\x05\x9b\x7e"
+"\xe7\xed\x1b\x07\x9b\xe5\xe7\x9d\xbb\xf7\x63\x3d\xb1\x5a\x9f\x68\x8b"
+"\xfe\x7a\x27\xfd\xf4\x28\xa2\x80\x7a\xbb\x87\xda\x22\xff\x00\x9e\x89"
+"\xff\x00\x7d\x0a\x3e\xd1\x17\xfc\xf4\x4f\xfb\xe8\x51\x45\x02\x0f\xb4"
+"\x45\xff\x00\x3d\x13\xfe\xfa\x14\x7d\xa2\x2f\xf9\xe8\x9f\xf7\xd0\xa2"
+"\x8a\x00\x3e\xd1\x17\xfc\xf4\x4f\xfb\xe8\x51\xf6\x88\xbf\xe7\xa2\x7f"
+"\xdf\x42\x8a\x28\x00\xfb\x44\x5f\xf3\xd1\x3f\xef\xa1\x47\xda\x22\xff"
+"\x00\x9e\x89\xff\x00\x7d\x0a\x28\xa0\x03\xed\x11\x7f\xcf\x44\xff\x00"
+"\xbe\x85\x1f\x68\x8b\xfe\x7a\x27\xfd\xf4\x28\xa2\x80\x0f\xb4\x45\xff"
+"\x00\x3d\x13\xfe\xfa\x15\x9f\xaa\x8b\xdb\x8f\x2b\xec\x1a\x8d\xb5\x9e"
+"\x0f\xce\x65\x8b\xcc\xdc\x3d\xbe\x61\x8e\x28\xa2\xa9\x3e\x57\x74\x26"
+"\xae\x63\x5c\x5b\xf8\xb1\xf5\x37\x30\x6b\x9a\x3c\x5a\x78\x89\x42\x87"
+"\xb2\x77\x95\x9f\x9d\xc4\xfe\xf4\x00\x3e\xee\x3a\xf7\xae\x96\xc3\xce"
+"\x16\x70\x8b\x89\xa3\x9e\xe0\x28\x12\x49\x12\xed\x56\x6e\xe4\x0c\x9c"
+"\x7e\x74\x51\x57\x3a\x8e\x69\x26\x97\xc9\x24\x44\x60\xa2\xef\x77\xf7"
+"\x95\x67\x19\x99\xf9\xc7\x3d\x86\x68\xa2\x8a\xe5\x36\x3f\xff\xd9"; \ No newline at end of file
diff --git a/chrome/tools/sqlite.exe b/chrome/tools/sqlite.exe
new file mode 100644
index 0000000..908b607
--- /dev/null
+++ b/chrome/tools/sqlite.exe
Binary files differ
diff --git a/chrome/tools/sqlite3_analyzer.exe b/chrome/tools/sqlite3_analyzer.exe
new file mode 100644
index 0000000..9a7e7b4
--- /dev/null
+++ b/chrome/tools/sqlite3_analyzer.exe
Binary files differ
diff --git a/chrome/tools/test/generate_mime_tests.pl b/chrome/tools/test/generate_mime_tests.pl
new file mode 100644
index 0000000..1446b85
--- /dev/null
+++ b/chrome/tools/test/generate_mime_tests.pl
@@ -0,0 +1,270 @@
+#!/usr/bin/perl -w
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Generate html files that will be used test MIME type handling using
+# the layout test framework.
+
+use strict;
+use Switch; # used for switch-case program structure
+
+my $arg_count = $#ARGV + 1;
+
+# Make sure that only one command line argument is provided
+if ($arg_count ne 1) {
+ print "Usage:\n generate_mime_tests.pl < target_path >\n target_path".
+ " - path where the generated tests are to be placed";
+ exit;
+}
+
+# Obtain target path from the command line
+my $target_path = $ARGV[0];
+
+# Set relative path of the page to be requested
+my $root = "resources/getpage.pl?";
+
+# Variables used in the script
+my $content_type;
+my $parameter;
+my $source; # Temporary variable to hold source path
+my $count; # Used to generate number appended to filenames
+my $current_expected = ""; # Temporary storage for expected result
+my $query_description;
+
+# Number of tests each content type goes through, also determines the half
+# size of the expected results matrix
+my $test_set_size = 14;
+
+# List of HTTP content types to be used in generating test HTMLs
+my @content_type = ("NULL",
+ "text/plain",
+ "text/html",
+ "image/gif",
+ "image/bmp",
+ "image/tif",
+ "image/png",
+ "image/jpg",
+ "application/x-shockwave-flash");
+
+# List of URL query parameters to be used in generating test HTMLs
+my @parameter = ("tag=img&content=type_gif.gif",
+ "tag=img&content=type_bmp.bmp",
+ "tag=img&content=type_tif.tif",
+ "tag=img&content=type_png.png",
+ "tag=img&content=type_jpg.jpg",
+ "tag=img&content=type_txt.txt",
+ "tag=embed&content=type_swf.swf",
+ "switch=nohtml&content=type_gif.gif",
+ "switch=nohtml&content=type_bmp.bmp",
+ "switch=nohtml&content=type_tif.tif",
+ "switch=nohtml&content=type_png.png",
+ "switch=nohtml&content=type_jpg.jpg",
+ "switch=nohtml&content=type_txt.txt",
+ "switch=nohtml&content=type_swf.swf");
+
+# Matrix with expected results of all tests.
+# The matrix rows have four parts
+# 1. iframe with a html
+# 2. iframe without a html
+# 3. content within a html page
+# 4. content without a html
+# Each part has the same sequence of column headers
+my @expected = (
+# gif bmp tif png jpg txt swf
+# gif bmp tif png jpg txt swf
+# gif bmp tif png jpg txt swf
+# gif bmp tif png jpg txt swf
+ # NULL
+ "html","html","html","html","html","html","html", # iframe html
+ "void","void","image","void","void", "text","text", # iframe no html
+ "html","html","html","html","html","html","html", # html
+ "image","void","image","image","image","text","text", # no html
+
+ # text/plain
+ "html","html","html","html","html","html","html", # iframe html
+ "void","void","image","void","void", "text","text", # iframe no html
+ "html","html","html","html","html","html","html", # html
+ "image","void","image","image","image","text","text", # no html
+
+ # text/html
+ "rs", "rf", "rf", "rs", "rs", "rf", "rs", # iframe html
+ "text","text","text","text", "text", "text","text", # iframe no html
+ "rs", "rf", "rf", "rs", "rs", "rf", "rs", # html
+ "text", "text","text", "text", "text", "text","text", # no html
+
+ # image/gif
+ "void","void","void","void","void","void","void", # iframe html
+ "void","void","void","void", "void", "void","void", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "image","void","void", "image","image","void","void", # no html
+
+ # image/bmp
+ "void","void","void","void","void","void","void", # iframe html
+ "void","void","void","void", "void", "void","void", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "image","void","void", "image","image","void","void", # no html
+
+ # image/tif
+ "void","void","void","void","void","void","void", # iframe html
+ "void","void","void","void", "void", "void","void", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "void", "void","void", "void", "void", "void","void", # no html
+
+ # image/png
+ "void","void","void","void","void","void","void", # iframe html
+ "void","void","void","void", "void", "void","void", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "image","void","void", "image","image","void","void", # no html
+
+ # image/jpg
+ "void","void","void","void","void","void","void", # iframe html
+ "void","void","void","void", "void", "void","void", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "image","void","void", "image","void", "void","void", # no html
+
+ # application/x-shockwave-flash
+ "void","void","void","void","void","void","void", # iframe html
+ "flash","void","void","flash","flash","void","flash", # iframe no html
+ "void","void","void","void","void","void","void", # html
+ "flash","void","void", "flash","flash","void","flash");# no html
+
+# get_description()
+# Maps the expected word to an appropriate phrase.
+# Used to generated verbal descriptions for expected results of every test.
+# Input : expected result from the matrix
+# Output : returns the associated description
+sub get_result_description
+{
+ switch ($_[0]) {
+ case "void" { return "NOTHING";}
+ case "image" { return "an IMAGE";}
+ case "text" { return "simple TEXT";}
+ case "html" { return "an HTML as text";}
+ case "flash" { return "a FLASH object"}
+ case "rs" { return "been RENDERED CORRECTLY";}
+ case "rf" { return "been RENDERED INCORRECTLY";}
+ else { return "UNKNOWN";}
+ }
+}
+
+# get_query_description()
+# Maps the URL query to an appropriate phrase.
+# Used to generated verbal descriptions for URL queries of every test.
+# Input : URL query
+# Output : returns the associated description
+sub get_query_description
+{
+ switch ($_[0]) {
+ case "tag=img&content=type_gif.gif" { return "HTML page with a GIF image";}
+ case "tag=img&content=type_bmp.bmp" { return "HTML page with a BMP image";}
+ case "tag=img&content=type_tif.tif" { return "HTML page with a TIF image";}
+ case "tag=img&content=type_png.png" { return "HTML page with a PNG image";}
+ case "tag=img&content=type_jpg.jpg" { return "HTML page with a JPEG image"}
+ case "tag=img&content=type_txt.txt" { return "HTML page";}
+ case "tag=embed&content=type_swf.swf" { return "an HTML page with a FLASH object";}
+ case "switch=nohtml&content=type_gif.gif" { return "GIF image and no HTML";}
+ case "switch=nohtml&content=type_bmp.bmp" { return "BMP image and no HTML";}
+ case "switch=nohtml&content=type_tif.tif" { return "TIF image and no HTML";}
+ case "switch=nohtml&content=type_png.png" { return "PNG image and no HTML";}
+ case "switch=nohtml&content=type_jpg.jpg" { return "JPEG image and no HTML"}
+ case "switch=nohtml&content=type_txt.txt" { return "simple TEXT and no HTML";}
+ case "switch=nohtml&content=type_swf.swf" { return "FLASH object and no HTML";}
+ else { return "UNKNOWN TYPE";}
+ }
+}
+
+# This loop generates one HTML page with multiple iframes in it.
+# Generated one iframe for every parameter of every content type.
+my $iframe_index = 0;
+foreach $content_type ( @content_type) {
+ my $infile = join "", "iframe/", $content_type, ".html";
+ $infile =~ tr/\//_/;
+ $infile = $target_path.$infile;
+
+ open OUT, "> $infile" or die "Failed to open file $infile";
+ print OUT "This HTML is used to test HTTP content-type \"$content_type\"".
+ " by having multiple iframes render different types of content for the".
+ " same HTTP content-type header.\n";
+ print OUT "<script>\n if(window.layoutTestController)\n " .
+ "window.layoutTestController.waitUntilDone();\n</script>\n";
+ print OUT "<html>\n<body>\n<br>Well here are the frames !<br>\n";
+
+ foreach $parameter ( @parameter ) {
+
+ # Make sure to iterate only through the first half of the expected
+ # results matrix
+ if (($iframe_index > 0) && (0 == ($iframe_index % $test_set_size))) {
+ $iframe_index += $test_set_size;
+ }
+ $current_expected = get_result_description($expected[$iframe_index++]);
+
+ $source = join "", $root, "type=", $content_type, "&", $parameter;
+ $query_description = get_query_description($parameter);
+ print OUT "<br><br>This frame tests loading of a $query_description when the ".
+ "HTTP content-type is set to \"$content_type\" .<br> Expected : This ",
+ "iframe should have $current_expected .<br>\n<iframe src=\"$source\" ".
+ "height=\"300\" width=\"500\"></iframe>\n\n";
+ }
+
+ print OUT "</body>\n</html>\n";
+ print OUT "<script>\n if(window.layoutTestController)\n ".
+ "layoutTestController.notifyDone();\n</script>";
+ close OUT;
+}
+
+# This loop generates one HTML for every combination of content-type and
+# parameter.
+my $main_index = 0;
+foreach $content_type ( @content_type) {
+ $count = 0;
+ foreach $parameter ( @parameter ) {
+ $count++;
+
+ # Make sure to iterate only through the second half of the expected
+ # results matrix
+ if (0 == ($main_index % $test_set_size)) {
+ $main_index += $test_set_size;
+ }
+
+ $current_expected = get_result_description($expected[$main_index++]);
+
+ my $infile = join "", "main/", $content_type, $count, ".html";
+ $infile =~ tr/\//_/;
+ $source = join "", $root, "type=", $content_type, "&", $parameter;
+ $infile = $target_path.$infile;
+ $query_description = get_query_description($parameter);
+
+ open OUT, "> $infile" or die "Failed to open file $infile";
+ print OUT "This tests loading of a $query_description when the HTTP content-type".
+ " is set to \"$content_type\" .\n Expected : This page should have ".
+ "$current_expected .\n\n";
+ print OUT "<script>\n window.location=\"$source\";\n</script>\n";
+ close OUT;
+ }
+} \ No newline at end of file
diff --git a/chrome/tools/test/image_diff/SConscript b/chrome/tools/test/image_diff/SConscript
new file mode 100644
index 0000000..7a6b825
--- /dev/null
+++ b/chrome/tools/test/image_diff/SConscript
@@ -0,0 +1,106 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env_test')
+
+env_test = env_test.Clone()
+
+env_test.Prepend(
+ CPPDEFINES = [
+ 'PNG_USER_CONFIG',
+ 'CHROME_PNG_WRITE_SUPPORT',
+ '_DEBUG',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CPPPATH = [
+ '$ZLIB_DIR',
+ '$LIBPNG_DIR',
+ '#/..',
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+ '/DEBUG',
+
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+
+ '/SUBSYSTEM:CONSOLE',
+
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+ LIBS = [
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+
+ 'DelayImp.lib',
+ ],
+)
+
+input_files = [
+ 'image_diff.cc',
+]
+
+libs = [
+ '$SKIA_DIR/skia.lib',
+ '$LIBPNG_DIR/libpng.lib',
+ '$BASE_DIR/gfx/base_gfx.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$ZLIB_DIR/zlib.lib',
+ '$BASE_DIR/base.lib',
+]
+
+exe = env_test.Program(['image_diff',
+ 'image_diff.pdb'],
+ input_files + libs)
+i = env_test.Install('$TARGET_ROOT', exe)
+
+env_test.Alias('chrome', i)
diff --git a/chrome/tools/test/image_diff/image_diff.cc b/chrome/tools/test/image_diff/image_diff.cc
new file mode 100644
index 0000000..a2135e9
--- /dev/null
+++ b/chrome/tools/test/image_diff/image_diff.cc
@@ -0,0 +1,291 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file input format is based loosely on
+// WebKitTools/DumpRenderTree/ImageDiff.m
+
+// The exact format of this tool's output to stdout is important, to match
+// what the run-webkit-tests script expects.
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/gfx/png_decoder.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+
+// Causes the app to remain open, waiting for pairs of filenames on stdin.
+// The caller is then responsible for terminating this app.
+static const wchar_t kOptionPollStdin[] = L"use-stdin";
+
+// Return codes used by this utility.
+static const int kStatusSame = 0;
+static const int kStatusDifferent = 1;
+static const int kStatusError = 2;
+
+class Image {
+ public:
+ Image() : w_(0), h_(0) {
+ }
+
+ bool has_image() const {
+ return w_ > 0 && h_ > 0;
+ }
+
+ int w() const {
+ return w_;
+ }
+
+ int h() const {
+ return h_;
+ }
+
+ // Creates the image from stdin with the given data length. On success, it
+ // will return true. On failure, no other methods should be accessed.
+ bool CreateFromStdin(int byte_length) {
+ if (byte_length <= 0)
+ return false;
+
+ scoped_array<unsigned char> source(new unsigned char[byte_length]);
+ if (fread(source.get(), 1, byte_length, stdin) != byte_length)
+ return false;
+
+ if (!PNGDecoder::Decode(source.get(), byte_length, PNGDecoder::FORMAT_RGBA,
+ &data_, &w_, &h_)) {
+ Clear();
+ return false;
+ }
+ return true;
+ }
+
+ // Creates the image from the given filename on disk, and returns true on
+ // success.
+ bool CreateFromFilename(const char* filename) {
+ FILE* f;
+ if (fopen_s(&f, filename, "rb") != 0)
+ return false;
+
+ std::vector<unsigned char> compressed;
+ const int buf_size = 1024;
+ unsigned char buf[buf_size];
+ size_t num_read = 0;
+ while ((num_read = fread(buf, 1, buf_size, f)) > 0) {
+ std::copy(buf, &buf[num_read], std::back_inserter(compressed));
+ }
+
+ fclose(f);
+
+ if (!PNGDecoder::Decode(&compressed[0], compressed.size(),
+ PNGDecoder::FORMAT_RGBA, &data_, &w_, &h_)) {
+ Clear();
+ return false;
+ }
+ return true;
+ }
+
+ void Clear() {
+ w_ = h_ = 0;
+ data_.clear();
+ }
+
+ // Returns the RGBA value of the pixel at the given location
+ uint32 pixel_at(int x, int y) const {
+ DCHECK(x >= 0 && x < w_);
+ DCHECK(y >= 0 && y < h_);
+ return data_[(y * w_ + x) * 4];
+ }
+
+ private:
+ // pixel dimensions of the image
+ int w_, h_;
+
+ std::vector<unsigned char> data_;
+};
+
+float PercentageDifferent(const Image& baseline, const Image& actual) {
+ int w = std::min(baseline.w(), actual.w());
+ int h = std::min(baseline.h(), actual.h());
+
+ // compute pixels different in the overlap
+ int pixels_different = 0;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ if (baseline.pixel_at(x, y) != actual.pixel_at(x, y))
+ pixels_different++;
+ }
+ }
+
+ // count pixels that are a difference in size as also being different
+ int max_w = std::max(baseline.w(), actual.w());
+ int max_h = std::max(baseline.h(), actual.h());
+
+ // ...pixels off the right side, but not including the lower right corner
+ pixels_different += (max_w - w) * h;
+
+ // ...pixels along the bottom, including the lower right corner
+ pixels_different += (max_h - h) * max_w;
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ float total_pixels = static_cast<float>(actual.w()) *
+ static_cast<float>(actual.h());
+ if (total_pixels == 0)
+ return 100.0f; // when the bitmap is empty, they are 100% different
+ return static_cast<float>(pixels_different) / total_pixels * 100;
+}
+
+void PrintHelp() {
+ fprintf(stderr,
+ "Usage:\n"
+ " image_diff <compare file> <reference file>\n"
+ " Compares two files on disk, returning 0 when they are the same\n"
+ " image_diff --use-stdin\n"
+ " Stays open reading pairs of filenames from stdin, comparing them,\n"
+ " and sending 0 to stdout when they are the same\n");
+ /* For unfinished webkit-like-mode (see below)
+ "\n"
+ " image_diff -s\n"
+ " Reads stream input from stdin, should be EXACTLY of the format\n"
+ " \"Content-length: <byte length> <data>Content-length: ...\n"
+ " it will take as many file pairs as given, and will compare them as\n"
+ " (cmp_file, reference_file) pairs\n");
+ */
+}
+
+int CompareImages(const char* file1, const char* file2) {
+ Image actual_image;
+ Image baseline_image;
+
+ if (!actual_image.CreateFromFilename(file1)) {
+ fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1);
+ return kStatusError;
+ }
+ if (!baseline_image.CreateFromFilename(file2)) {
+ fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2);
+ return kStatusError;
+ }
+
+ float percent = PercentageDifferent(actual_image, baseline_image);
+ if (percent > 0.0) {
+ // failure: The WebKit version also writes the difference image to
+ // stdout, which seems excessive for our needs.
+ printf("diff: %01.2f%% failed\n", percent);
+ return kStatusDifferent;
+ }
+
+ // success
+ printf("diff: %01.2f%% passed\n", percent);
+ return kStatusSame;
+
+/* Untested mode that acts like WebKit's image comparator. I wrote this but
+ decided it's too complicated. We may use it in the future if it looks useful
+
+ char buffer[2048];
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+
+ if (strncmp("Content-length: ", buffer, 16) == 0) {
+ char* context;
+ strtok_s(buffer, " ", &context);
+ int image_size = strtol(strtok_s(NULL, " ", &context), NULL, 10);
+
+ bool success = false;
+ if (image_size > 0 && actual_image.has_image() == 0) {
+ if (!actual_image.CreateFromStdin(image_size)) {
+ fputs("Error, input image can't be decoded.\n", stderr);
+ return 1;
+ }
+ } else if (image_size > 0 && baseline_image.has_image() == 0) {
+ if (!baseline_image.CreateFromStdin(image_size)) {
+ fputs("Error, baseline image can't be decoded.\n", stderr);
+ return 1;
+ }
+ } else {
+ fputs("Error, image size must be specified.\n", stderr);
+ return 1;
+ }
+ }
+
+ if (actual_image.has_image() && baseline_image.has_image()) {
+ float percent = PercentageDifferent(actual_image, baseline_image);
+ if (percent > 0.0) {
+ // failure: The WebKit version also writes the difference image to
+ // stdout, which seems excessive for our needs.
+ printf("diff: %01.2f%% failed\n", percent);
+ } else {
+ // success
+ printf("diff: %01.2f%% passed\n", percent);
+ }
+ actual_image.Clear();
+ baseline_image.Clear();
+ }
+
+ fflush(stdout);
+ }
+*/
+}
+
+int main(int argc, const char* argv[]) {
+ CommandLine parsed_command_line;
+ if (parsed_command_line.HasSwitch(kOptionPollStdin)) {
+ // Watch stdin for filenames.
+ char stdin_buffer[2048];
+ char filename1_buffer[2048];
+ bool have_filename1 = false;
+ while (fgets(stdin_buffer, sizeof(stdin_buffer), stdin)) {
+ char *newLine = strchr(stdin_buffer, '\n');
+ if (newLine)
+ *newLine = '\0';
+ if (!*stdin_buffer)
+ continue;
+
+ if (have_filename1) {
+ // CompareImages writes results to stdout unless an error occurred.
+ if (CompareImages(filename1_buffer, stdin_buffer) == kStatusError)
+ printf("error\n");
+ fflush(stdout);
+ have_filename1 = false;
+ } else {
+ // Save the first filename in another buffer and wait for the second
+ // filename to arrive via stdin.
+ strcpy_s(filename1_buffer, sizeof(filename1_buffer), stdin_buffer);
+ have_filename1 = true;
+ }
+ }
+ return 0;
+ }
+
+ if (argc == 3) {
+ return CompareImages(argv[1], argv[2]);
+ }
+
+ PrintHelp();
+ return kStatusError;
+}
diff --git a/chrome/tools/test/image_diff/image_diff.vcproj b/chrome/tools/test/image_diff/image_diff.vcproj
new file mode 100644
index 0000000..77f36ac
--- /dev/null
+++ b/chrome/tools/test/image_diff/image_diff.vcproj
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="image_diff"
+ ProjectGUID="{50B079C7-CD01-42D3-B8C4-9F8D9322E822}"
+ RootNamespace="image_diff"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </Configuration>
+ </Configurations>
+ <Files>
+ <File
+ RelativePath=".\image_diff.cc"
+ >
+ </File>
+ </Files>
+</VisualStudioProject>
diff --git a/chrome/tools/test/smoketests.py b/chrome/tools/test/smoketests.py
new file mode 100644
index 0000000..0d31ec4
--- /dev/null
+++ b/chrome/tools/test/smoketests.py
@@ -0,0 +1,263 @@
+#!/bin/env python
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+Runs all the available unit tests, layout tests, page-cycler tests, etc.
+for a build of Chrome, imitating a buildbot.
+
+Usage examples:
+ smoketests.py
+ smoketests.py --target=debug --build-type=kjs
+ smoketests.py --nopage-cycler
+ smoketests.py --tests=unit,ui --verbose
+
+For a full list of options, pass the '--help' switch.
+
+[Alternatively, this script will kill all the tests' executables, in case one
+got orphaned during a previous run of this script. (This generally only
+happens during script development.)]
+
+"""
+
+import errno
+import optparse
+import os
+import subprocess
+import sys
+import time
+
+import google.httpd_utils
+import google.path_utils
+import google.process_utils
+
+
+# Keep a global httpd object so it can be killed in the event of errors.
+_httpd = None
+
+# All the available commands, by name. Items in the command-line lists may
+# contain various keywords, listed in the "Substitutions" section below.
+# The target build directory will be prepended to the first item in each
+# command list.
+COMMANDS = {'ipc': ['ipc_tests.exe'],
+ 'unit': ['unit_tests.exe'],
+ 'ui': ['ui_tests.exe', '%(page_heap)s'],
+ 'ui-single': ['ui_tests.exe', '--single-process'],
+ 'test_shell': ['test_shell_tests.exe'],
+ 'page-cycler-moz': ['page_cycler_tests.exe',
+ '--gtest_filter=PageCycler*.MozFile'],
+ 'page-cycler-moz-http': ['page_cycler_tests.exe',
+ '--gtest_filter=PageCycler*.MozHttp'],
+ 'page-cycler-intl1': ['page_cycler_tests.exe',
+ '--gtest_filter=PageCycler*.Intl1File'],
+ 'page-cycler-intl2': ['page_cycler_tests.exe',
+ '--gtest_filter=PageCycler*.Intl2File'],
+ 'page-cycler-bloat-http': ['page_cycler_tests.exe',
+ '--gtest_filter=PageCycler*.BloatHttp'],
+ 'startup': ['startup_tests.exe',
+ '--gtest_filter=Startup*.*'],
+ 'dest-startup': ['startup_tests.exe',
+ '--gtest_filter=DestinationsStartupTest.*'],
+ 'selenium': ['selenium_tests.exe'],
+ 'plugin': ['plugin_tests.exe'],
+ 'installer': ['installer_unittests.exe'],
+ 'webkit': ['%(python)s',
+ '%(slave_scripts)s/layout_test_wrapper.py',
+ '--build-type', '%(build_type)s',
+ '--target', '%(target)s',
+ '%(page_heap)s'],
+ }
+
+# Certain tests are not run for each build type.
+SKIPPED = {'Release': ['plugin'],
+ 'Debug': ['selenium', 'webkit']}
+
+def _BuildbotScriptPath(sub_dir):
+ """Returns the full path to the given subdir of tools/buildbot/scripts."""
+ this_script_dir = google.path_utils.ScriptDir()
+ return google.path_utils.FindUpward(this_script_dir, 'tools', 'buildbot',
+ 'scripts', sub_dir)
+
+
+def _MakeSubstitutions(list, options):
+ """Makes substitutions in each item of a list and returns the resulting list.
+
+ Args:
+ list: a list of strings, optionally containing certain %()s substitution
+ tags listed below
+ options: options as returned by optparse
+ """
+ this_script_dir = google.path_utils.ScriptDir()
+ python_path = google.path_utils.FindUpward(this_script_dir,
+ 'third_party',
+ 'python_24',
+ 'python_slave.exe')
+
+ substitutions = {'target': options.target,
+ 'build_type': options.build_type,
+ 'page_heap': '',
+ 'python': python_path,
+ 'slave_scripts': _BuildbotScriptPath('slave'),
+ }
+ if options.build_type == 'kjs':
+ substitutions['page_heap'] = '--enable-pageheap'
+ return [word % substitutions for word in list]
+
+
+def main(options, args):
+ """Runs all the selected tests for the given build type and target."""
+ options.build_type = options.build_type.lower()
+ options.target = options.target.title()
+
+ this_script_dir = google.path_utils.ScriptDir()
+ test_path = google.path_utils.FindUpward(this_script_dir,
+ 'chrome', options.target)
+
+ # Add the buildbot script paths to the module search path.
+ sys.path.insert(0, _BuildbotScriptPath('slave'))
+ sys.path.insert(0, _BuildbotScriptPath('common'))
+
+ # Collect list of tests to run.
+ if options.tests == '':
+ tests = sorted(COMMANDS.keys())
+ else:
+ tests = set()
+ requested_tests = options.tests.lower().split(',')
+ for test in requested_tests:
+ if test in COMMANDS:
+ tests.add(test)
+ else:
+ print 'Ignoring unknown test "%s"' % test
+
+ # Check page-cycler data, since the tests choke if it isn't available.
+ try:
+ page_cycler_data = google.path_utils.FindUpward(this_script_dir,
+ 'data',
+ 'page_cycler')
+ except google.path_utils.PathNotFound:
+ # Were we going to run any page-cycler tests?
+ if (not options.nopage_cycler and
+ len([x for x in tests if x.startswith('page-cycler')])):
+ print 'Skipping page-cycler tests (no data)'
+ options.nopage_cycler = True
+
+ # Start an httpd if needed.
+ http_tests = [x for x in tests if x.endswith('-http')]
+ if http_tests and not options.nopage_cycler and not options.nohttp:
+ try:
+ _httpd = google.httpd_utils.StartServer(document_root=page_cycler_data)
+ except google.httpd_utils.HttpdNotStarted:
+ print 'Skipping http tests (httpd failed to start)'
+ options.nohttp = True
+
+ # Remove tests not desired.
+ if options.nopage_cycler:
+ tests = [x for x in tests if not x.startswith('page-cycler')]
+ if options.nowebkit and 'webkit' in tests:
+ tests.remove('webkit')
+ if options.nohttp:
+ tests = [x for x in tests if not x.endswith('-http')]
+
+ # Remove tests skipped for this build target.
+ for skip in SKIPPED[options.target]:
+ if skip in tests:
+ print 'Skipping %s for %s build' % (skip, options.target)
+ tests.remove(skip)
+
+ if not len(tests):
+ print 'No tests to run.'
+ return 0
+
+ # Run each test, substituting strings as needed.
+ failures = []
+ start_time = time.time()
+ for test in tests:
+ test_start_time = time.time()
+ command = _MakeSubstitutions(COMMANDS[test], options)
+ command[0] = os.path.join(test_path, command[0])
+ if options.verbose:
+ print
+ print 'Running %s:' % test,
+ try:
+ result = google.process_utils.RunCommand(command, options.verbose)
+ except google.process_utils.CommandNotFound:
+ print '%s' % e
+ if options.verbose:
+ print test,
+ print '(%ds)' % (time.time() - test_start_time),
+ if result:
+ print 'FAIL'
+ failures.append(test)
+ else:
+ print 'PASS'
+
+ print 'Total time: %ds' % (time.time() - start_time)
+ if len(failures):
+ print 'Failed tests:'
+ print os.linesep.join(failures)
+ else:
+ print 'All tests passed. Hurrah!'
+
+ return len(failures)
+
+if '__main__' == __name__:
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('', '--target', default='Release',
+ help='build target (Debug or Release)')
+ option_parser.add_option('', '--build-type', default='v8',
+ help='build type (V8 or KJS), used by webkit tests')
+ option_parser.add_option('', '--verbose', action='store_true', default=False,
+ help='show full output from every command')
+ option_parser.add_option('', '--nopage-cycler', action='store_true',
+ default=False, help='disable page-cycler tests')
+ option_parser.add_option('', '--nowebkit', action='store_true',
+ default=False, help='disable webkit (layout) tests')
+ option_parser.add_option('', '--nohttp', action='store_true',
+ default=False,
+ help="don't run tests (e.g. page_cycler) with http")
+ option_parser.add_option('', '--tests', default='',
+ help='comma-separated list of tests to run, from '
+ '{%s}' % ', '.join(sorted(COMMANDS.keys())))
+ option_parser.add_option('', '--killall', action='store_true', default=False,
+ help='kill all test executables (and run no tests)')
+ options, args = option_parser.parse_args()
+
+ if options.killall:
+ kill_list = _MakeSubstitutions([COMMANDS[x][0] for x in COMMANDS.keys()],
+ options)
+ kill_list = set([os.path.basename(x) for x in kill_list])
+ sys.exit(google.process_utils.KillAll(kill_list))
+
+ try:
+ result = main(options, args)
+ finally:
+ # Kill the httpd.
+ if _httpd:
+ _httpd.StopServer(force=True)
+ sys.exit(result)