Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/libwebrtc/moz-patch-stack/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 1 MB image not shown  

Quelle  0091.patch   Sprache: unbekannt

 
Spracherkennung für: .patch vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

From: Nico Grunbaum <na-g@nostrum.com>
Date: Fri, 30 Apr 2021 21:51:00 +0000
Subject: Bug 1654112 - Add grit dep for building webrtc on android; r=mjf

Differential Revision: https://phabricator.services.mozilla.com/D114027
Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/3cce5e6938f0df87bd9ab12a5f556aceb93dfa1d
---
 tools/grit/.gitignore                         |    1 +
 tools/grit/BUILD.gn                           |   48 +
 tools/grit/MANIFEST.in                        |    3 +
 tools/grit/OWNERS                             |    8 +
 tools/grit/PRESUBMIT.py                       |   22 +
 tools/grit/README.md                          |   19 +
 tools/grit/grit.py                            |   31 +
 tools/grit/grit/__init__.py                   |   19 +
 tools/grit/grit/clique.py                     |  491 +++
 tools/grit/grit/clique_unittest.py            |  265 ++
 tools/grit/grit/constants.py                  |   23 +
 tools/grit/grit/exception.py                  |  139 +
 tools/grit/grit/extern/BogoFP.py              |   22 +
 tools/grit/grit/extern/FP.py                  |   72 +
 tools/grit/grit/extern/__init__.py            |    0
 tools/grit/grit/extern/tclib.py               |  503 +++
 tools/grit/grit/format/__init__.py            |    8 +
 tools/grit/grit/format/android_xml.py         |  212 ++
 .../grit/grit/format/android_xml_unittest.py  |  149 +
 tools/grit/grit/format/c_format.py            |   95 +
 tools/grit/grit/format/c_format_unittest.py   |   81 +
 .../grit/grit/format/chrome_messages_json.py  |   59 +
 .../format/chrome_messages_json_unittest.py   |  190 +
 tools/grit/grit/format/data_pack.py           |  321 ++
 tools/grit/grit/format/data_pack_unittest.py  |  102 +
 .../grit/grit/format/gen_predetermined_ids.py |  144 +
 .../format/gen_predetermined_ids_unittest.py  |   46 +
 tools/grit/grit/format/gzip_string.py         |   46 +
 .../grit/grit/format/gzip_string_unittest.py  |   65 +
 tools/grit/grit/format/html_inline.py         |  602 ++++
 .../grit/grit/format/html_inline_unittest.py  |  927 +++++
 tools/grit/grit/format/minifier.py            |   45 +
 .../grit/grit/format/policy_templates_json.py |   26 +
 .../format/policy_templates_json_unittest.py  |  207 ++
 tools/grit/grit/format/rc.py                  |  474 +++
 tools/grit/grit/format/rc_header.py           |   48 +
 tools/grit/grit/format/rc_header_unittest.py  |  138 +
 tools/grit/grit/format/rc_unittest.py         |  415 +++
 tools/grit/grit/format/resource_map.py        |  159 +
 .../grit/grit/format/resource_map_unittest.py |  345 ++
 tools/grit/grit/gather/__init__.py            |    8 +
 tools/grit/grit/gather/admin_template.py      |   62 +
 .../grit/gather/admin_template_unittest.py    |  115 +
 tools/grit/grit/gather/chrome_html.py         |  377 ++
 .../grit/grit/gather/chrome_html_unittest.py  |  610 ++++
 tools/grit/grit/gather/chrome_scaled_image.py |  157 +
 .../gather/chrome_scaled_image_unittest.py    |  209 ++
 tools/grit/grit/gather/interface.py           |  172 +
 tools/grit/grit/gather/json_loader.py         |   27 +
 tools/grit/grit/gather/policy_json.py         |  325 ++
 .../grit/grit/gather/policy_json_unittest.py  |  347 ++
 tools/grit/grit/gather/rc.py                  |  343 ++
 tools/grit/grit/gather/rc_unittest.py         |  372 ++
 tools/grit/grit/gather/regexp.py              |   82 +
 tools/grit/grit/gather/skeleton_gatherer.py   |  149 +
 tools/grit/grit/gather/tr_html.py             |  743 ++++
 tools/grit/grit/gather/tr_html_unittest.py    |  524 +++
 tools/grit/grit/gather/txt.py                 |   38 +
 tools/grit/grit/gather/txt_unittest.py        |   35 +
 tools/grit/grit/grd_reader.py                 |  238 ++
 tools/grit/grit/grd_reader_unittest.py        |  346 ++
 tools/grit/grit/grit-todo.xml                 |   62 +
 tools/grit/grit/grit_runner.py                |  334 ++
 tools/grit/grit/grit_runner_unittest.py       |   42 +
 tools/grit/grit/lazy_re.py                    |   46 +
 tools/grit/grit/lazy_re_unittest.py           |   40 +
 tools/grit/grit/node/__init__.py              |    8 +
 tools/grit/grit/node/base.py                  |  670 ++++
 tools/grit/grit/node/base_unittest.py         |  259 ++
 tools/grit/grit/node/brotli_util.py           |   29 +
 tools/grit/grit/node/custom/__init__.py       |    8 +
 tools/grit/grit/node/custom/filename.py       |   29 +
 .../grit/node/custom/filename_unittest.py     |   34 +
 tools/grit/grit/node/empty.py                 |   64 +
 tools/grit/grit/node/include.py               |  170 +
 tools/grit/grit/node/include_unittest.py      |  134 +
 tools/grit/grit/node/mapping.py               |   60 +
 tools/grit/grit/node/message.py               |  362 ++
 tools/grit/grit/node/message_unittest.py      |  380 ++
 tools/grit/grit/node/misc.py                  |  707 ++++
 tools/grit/grit/node/misc_unittest.py         |  590 ++++
 tools/grit/grit/node/mock_brotli.py           |   10 +
 tools/grit/grit/node/node_io.py               |  117 +
 tools/grit/grit/node/node_io_unittest.py      |  182 +
 tools/grit/grit/node/structure.py             |  375 ++
 tools/grit/grit/node/structure_unittest.py    |  178 +
 tools/grit/grit/node/variant.py               |   41 +
 tools/grit/grit/pseudo.py                     |  129 +
 tools/grit/grit/pseudo_rtl.py                 |  104 +
 tools/grit/grit/pseudo_unittest.py            |   55 +
 tools/grit/grit/shortcuts.py                  |   93 +
 tools/grit/grit/shortcuts_unittest.py         |   79 +
 tools/grit/grit/tclib.py                      |  246 ++
 tools/grit/grit/tclib_unittest.py             |  180 +
 tools/grit/grit/test_suite_all.py             |   34 +
 tools/grit/grit/testdata/GoogleDesktop.adm    |  945 +++++
 tools/grit/grit/testdata/README.txt           |   87 +
 tools/grit/grit/testdata/about.html           |   45 +
 tools/grit/grit/testdata/android.xml          |   24 +
 tools/grit/grit/testdata/bad_browser.html     |   16 +
 tools/grit/grit/testdata/browser.html         |   42 +
 tools/grit/grit/testdata/buildinfo.grd        |   46 +
 tools/grit/grit/testdata/cache_prefix.html    |   24 +
 .../grit/grit/testdata/cache_prefix_file.html |   25 +
 tools/grit/grit/testdata/chat_result.html     |   24 +
 .../chrome/app/generated_resources.grd        |  199 ++
 tools/grit/grit/testdata/chrome_html.html     |    6 +
 .../grit/testdata/default_100_percent/a.png   |  Bin 0 -> 159 bytes
 .../grit/testdata/default_100_percent/b.png   |    1 +
 tools/grit/grit/testdata/del_footer.html      |    8 +
 tools/grit/grit/testdata/del_header.html      |   60 +
 tools/grit/grit/testdata/deleted.html         |   21 +
 tools/grit/grit/testdata/depfile.grd          |   18 +
 tools/grit/grit/testdata/details.html         |   10 +
 .../grit/testdata/duplicate-name-input.xml    |   26 +
 tools/grit/grit/testdata/email_result.html    |   34 +
 tools/grit/grit/testdata/email_thread.html    |   10 +
 tools/grit/grit/testdata/error.html           |    8 +
 tools/grit/grit/testdata/explicit_web.html    |   11 +
 tools/grit/grit/testdata/footer.html          |   14 +
 .../grit/testdata/generated_resources_fr.xtb  | 3079 +++++++++++++++++
 .../grit/testdata/generated_resources_iw.xtb  |    4 +
 .../grit/testdata/generated_resources_no.xtb  |    4 +
 tools/grit/grit/testdata/grit_part.grdp       |    5 +
 tools/grit/grit/testdata/header.html          |   39 +
 tools/grit/grit/testdata/homepage.html        |   37 +
 tools/grit/grit/testdata/hover.html           |  177 +
 tools/grit/grit/testdata/include_test.html    |   31 +
 tools/grit/grit/testdata/included_sample.html |    1 +
 tools/grit/grit/testdata/indexing_speed.html  |   58 +
 tools/grit/grit/testdata/install_prefs.html   |   92 +
 tools/grit/grit/testdata/install_prefs2.html  |   52 +
 .../grit/testdata/klonk-alternate-skeleton.rc |  Bin 0 -> 1088 bytes
 tools/grit/grit/testdata/klonk.ico            |  Bin 0 -> 766 bytes
 tools/grit/grit/testdata/klonk.rc             |  Bin 0 -> 9824 bytes
 .../grit/grit/testdata/ko_oem_enable_bug.html |    1 +
 .../grit/testdata/ko_oem_non_admin_bug.html   |    1 +
 tools/grit/grit/testdata/mini.html            |   36 +
 tools/grit/grit/testdata/oem_enable.html      |  106 +
 tools/grit/grit/testdata/oem_non_admin.html   |   39 +
 tools/grit/grit/testdata/onebox.html          |   21 +
 tools/grit/grit/testdata/oneclick.html        |   34 +
 tools/grit/grit/testdata/password.html        |   37 +
 tools/grit/grit/testdata/preferences.html     |  234 ++
 tools/grit/grit/testdata/preprocess_test.html |    7 +
 tools/grit/grit/testdata/privacy.html         |   35 +
 tools/grit/grit/testdata/quit_apps.html       |   49 +
 tools/grit/grit/testdata/recrawl.html         |   30 +
 tools/grit/grit/testdata/resource_ids         |   13 +
 tools/grit/grit/testdata/script.html          |   38 +
 tools/grit/grit/testdata/searchbox.html       |   22 +
 tools/grit/grit/testdata/sidebar_h.html       |   82 +
 tools/grit/grit/testdata/sidebar_v.html       |  267 ++
 tools/grit/grit/testdata/simple-input.xml     |   52 +
 tools/grit/grit/testdata/simple.html          |    3 +
 tools/grit/grit/testdata/source.rc            |   57 +
 .../grit/testdata/special_100_percent/a.png   |  Bin 0 -> 159 bytes
 tools/grit/grit/testdata/status.html          |   44 +
 .../grit/testdata/structure_variables.html    |    4 +
 tools/grit/grit/testdata/substitute.grd       |   31 +
 tools/grit/grit/testdata/substitute.xmb       |   10 +
 .../grit/grit/testdata/substitute_no_ids.grd  |   31 +
 tools/grit/grit/testdata/substitute_tmpl.grd  |   31 +
 tools/grit/grit/testdata/test_css.css         |    1 +
 tools/grit/grit/testdata/test_html.html       |    1 +
 tools/grit/grit/testdata/test_js.js           |    1 +
 tools/grit/grit/testdata/test_svg.svg         |    1 +
 tools/grit/grit/testdata/test_text.txt        |    1 +
 tools/grit/grit/testdata/time_related.html    |   11 +
 tools/grit/grit/testdata/toolbar_about.html   |  138 +
 .../grit/testdata/tools/grit/resource_ids     |  176 +
 tools/grit/grit/testdata/transl.rc            |   56 +
 tools/grit/grit/testdata/versions.html        |    7 +
 tools/grit/grit/testdata/whitelist.txt        |    4 +
 .../grit/testdata/whitelist_resources.grd     |   54 +
 .../grit/grit/testdata/whitelist_strings.grd  |   23 +
 tools/grit/grit/tool/__init__.py              |    8 +
 tools/grit/grit/tool/android2grd.py           |  484 +++
 tools/grit/grit/tool/android2grd_unittest.py  |  181 +
 tools/grit/grit/tool/build.py                 |  556 +++
 tools/grit/grit/tool/build_unittest.py        |  341 ++
 tools/grit/grit/tool/buildinfo.py             |   78 +
 tools/grit/grit/tool/buildinfo_unittest.py    |   90 +
 tools/grit/grit/tool/count.py                 |   52 +
 tools/grit/grit/tool/diff_structures.py       |  119 +
 .../grit/tool/diff_structures_unittest.py     |   46 +
 tools/grit/grit/tool/interface.py             |   62 +
 tools/grit/grit/tool/menu_from_parts.py       |   79 +
 tools/grit/grit/tool/newgrd.py                |   85 +
 tools/grit/grit/tool/newgrd_unittest.py       |   51 +
 tools/grit/grit/tool/postprocess_interface.py |   29 +
 tools/grit/grit/tool/postprocess_unittest.py  |   64 +
 tools/grit/grit/tool/preprocess_interface.py  |   25 +
 tools/grit/grit/tool/preprocess_unittest.py   |   50 +
 tools/grit/grit/tool/rc2grd.py                |  418 +++
 tools/grit/grit/tool/rc2grd_unittest.py       |  163 +
 tools/grit/grit/tool/resize.py                |  295 ++
 tools/grit/grit/tool/test.py                  |   24 +
 tools/grit/grit/tool/transl2tc.py             |  251 ++
 tools/grit/grit/tool/transl2tc_unittest.py    |  133 +
 tools/grit/grit/tool/unit.py                  |   43 +
 .../grit/tool/update_resource_ids/__init__.py |  305 ++
 .../grit/tool/update_resource_ids/assigner.py |  286 ++
 .../update_resource_ids/assigner_unittest.py  |  154 +
 .../grit/tool/update_resource_ids/common.py   |  101 +
 .../grit/tool/update_resource_ids/parser.py   |  231 ++
 .../grit/tool/update_resource_ids/reader.py   |   83 +
 tools/grit/grit/tool/xmb.py                   |  295 ++
 tools/grit/grit/tool/xmb_unittest.py          |  132 +
 tools/grit/grit/util.py                       |  691 ++++
 tools/grit/grit/util_unittest.py              |  118 +
 tools/grit/grit/xtb_reader.py                 |  140 +
 tools/grit/grit/xtb_reader_unittest.py        |  110 +
 tools/grit/grit_info.py                       |  173 +
 tools/grit/grit_rule.gni                      |  485 +++
 tools/grit/minify_with_uglify.py              |   44 +
 tools/grit/minimize_css.py                    |  105 +
 tools/grit/minimize_css_unittest.py           |   58 +
 tools/grit/pak_util.py                        |  223 ++
 tools/grit/repack.gni                         |  189 +
 tools/grit/setup.py                           |   46 +
 tools/grit/stamp_grit_sources.py              |   57 +
 tools/grit/third_party/six/LICENSE            |   18 +
 tools/grit/third_party/six/README             |   16 +
 tools/grit/third_party/six/README.chromium    |   13 +
 tools/grit/third_party/six/__init__.py        |  868 +++++
 226 files changed, 33440 insertions(+)
 create mode 100644 tools/grit/.gitignore
 create mode 100644 tools/grit/BUILD.gn
 create mode 100644 tools/grit/MANIFEST.in
 create mode 100644 tools/grit/OWNERS
 create mode 100644 tools/grit/PRESUBMIT.py
 create mode 100644 tools/grit/README.md
 create mode 100644 tools/grit/grit.py
 create mode 100644 tools/grit/grit/__init__.py
 create mode 100644 tools/grit/grit/clique.py
 create mode 100644 tools/grit/grit/clique_unittest.py
 create mode 100644 tools/grit/grit/constants.py
 create mode 100644 tools/grit/grit/exception.py
 create mode 100644 tools/grit/grit/extern/BogoFP.py
 create mode 100644 tools/grit/grit/extern/FP.py
 create mode 100644 tools/grit/grit/extern/__init__.py
 create mode 100644 tools/grit/grit/extern/tclib.py
 create mode 100644 tools/grit/grit/format/__init__.py
 create mode 100644 tools/grit/grit/format/android_xml.py
 create mode 100644 tools/grit/grit/format/android_xml_unittest.py
 create mode 100644 tools/grit/grit/format/c_format.py
 create mode 100644 tools/grit/grit/format/c_format_unittest.py
 create mode 100644 tools/grit/grit/format/chrome_messages_json.py
 create mode 100644 tools/grit/grit/format/chrome_messages_json_unittest.py
 create mode 100644 tools/grit/grit/format/data_pack.py
 create mode 100644 tools/grit/grit/format/data_pack_unittest.py
 create mode 100644 tools/grit/grit/format/gen_predetermined_ids.py
 create mode 100644 tools/grit/grit/format/gen_predetermined_ids_unittest.py
 create mode 100644 tools/grit/grit/format/gzip_string.py
 create mode 100644 tools/grit/grit/format/gzip_string_unittest.py
 create mode 100644 tools/grit/grit/format/html_inline.py
 create mode 100644 tools/grit/grit/format/html_inline_unittest.py
 create mode 100644 tools/grit/grit/format/minifier.py
 create mode 100644 tools/grit/grit/format/policy_templates_json.py
 create mode 100644 tools/grit/grit/format/policy_templates_json_unittest.py
 create mode 100644 tools/grit/grit/format/rc.py
 create mode 100644 tools/grit/grit/format/rc_header.py
 create mode 100644 tools/grit/grit/format/rc_header_unittest.py
 create mode 100644 tools/grit/grit/format/rc_unittest.py
 create mode 100644 tools/grit/grit/format/resource_map.py
 create mode 100644 tools/grit/grit/format/resource_map_unittest.py
 create mode 100644 tools/grit/grit/gather/__init__.py
 create mode 100644 tools/grit/grit/gather/admin_template.py
 create mode 100644 tools/grit/grit/gather/admin_template_unittest.py
 create mode 100644 tools/grit/grit/gather/chrome_html.py
 create mode 100644 tools/grit/grit/gather/chrome_html_unittest.py
 create mode 100644 tools/grit/grit/gather/chrome_scaled_image.py
 create mode 100644 tools/grit/grit/gather/chrome_scaled_image_unittest.py
 create mode 100644 tools/grit/grit/gather/interface.py
 create mode 100644 tools/grit/grit/gather/json_loader.py
 create mode 100644 tools/grit/grit/gather/policy_json.py
 create mode 100644 tools/grit/grit/gather/policy_json_unittest.py
 create mode 100644 tools/grit/grit/gather/rc.py
 create mode 100644 tools/grit/grit/gather/rc_unittest.py
 create mode 100644 tools/grit/grit/gather/regexp.py
 create mode 100644 tools/grit/grit/gather/skeleton_gatherer.py
 create mode 100644 tools/grit/grit/gather/tr_html.py
 create mode 100644 tools/grit/grit/gather/tr_html_unittest.py
 create mode 100644 tools/grit/grit/gather/txt.py
 create mode 100644 tools/grit/grit/gather/txt_unittest.py
 create mode 100644 tools/grit/grit/grd_reader.py
 create mode 100644 tools/grit/grit/grd_reader_unittest.py
 create mode 100644 tools/grit/grit/grit-todo.xml
 create mode 100644 tools/grit/grit/grit_runner.py
 create mode 100644 tools/grit/grit/grit_runner_unittest.py
 create mode 100644 tools/grit/grit/lazy_re.py
 create mode 100644 tools/grit/grit/lazy_re_unittest.py
 create mode 100644 tools/grit/grit/node/__init__.py
 create mode 100644 tools/grit/grit/node/base.py
 create mode 100644 tools/grit/grit/node/base_unittest.py
 create mode 100644 tools/grit/grit/node/brotli_util.py
 create mode 100644 tools/grit/grit/node/custom/__init__.py
 create mode 100644 tools/grit/grit/node/custom/filename.py
 create mode 100644 tools/grit/grit/node/custom/filename_unittest.py
 create mode 100644 tools/grit/grit/node/empty.py
 create mode 100644 tools/grit/grit/node/include.py
 create mode 100644 tools/grit/grit/node/include_unittest.py
 create mode 100644 tools/grit/grit/node/mapping.py
 create mode 100644 tools/grit/grit/node/message.py
 create mode 100644 tools/grit/grit/node/message_unittest.py
 create mode 100644 tools/grit/grit/node/misc.py
 create mode 100644 tools/grit/grit/node/misc_unittest.py
 create mode 100644 tools/grit/grit/node/mock_brotli.py
 create mode 100644 tools/grit/grit/node/node_io.py
 create mode 100644 tools/grit/grit/node/node_io_unittest.py
 create mode 100644 tools/grit/grit/node/structure.py
 create mode 100644 tools/grit/grit/node/structure_unittest.py
 create mode 100644 tools/grit/grit/node/variant.py
 create mode 100644 tools/grit/grit/pseudo.py
 create mode 100644 tools/grit/grit/pseudo_rtl.py
 create mode 100644 tools/grit/grit/pseudo_unittest.py
 create mode 100644 tools/grit/grit/shortcuts.py
 create mode 100644 tools/grit/grit/shortcuts_unittest.py
 create mode 100644 tools/grit/grit/tclib.py
 create mode 100644 tools/grit/grit/tclib_unittest.py
 create mode 100644 tools/grit/grit/test_suite_all.py
 create mode 100644 tools/grit/grit/testdata/GoogleDesktop.adm
 create mode 100644 tools/grit/grit/testdata/README.txt
 create mode 100644 tools/grit/grit/testdata/about.html
 create mode 100644 tools/grit/grit/testdata/android.xml
 create mode 100644 tools/grit/grit/testdata/bad_browser.html
 create mode 100644 tools/grit/grit/testdata/browser.html
 create mode 100644 tools/grit/grit/testdata/buildinfo.grd
 create mode 100644 tools/grit/grit/testdata/cache_prefix.html
 create mode 100644 tools/grit/grit/testdata/cache_prefix_file.html
 create mode 100644 tools/grit/grit/testdata/chat_result.html
 create mode 100644 tools/grit/grit/testdata/chrome/app/generated_resources.grd
 create mode 100644 tools/grit/grit/testdata/chrome_html.html
 create mode 100644 tools/grit/grit/testdata/default_100_percent/a.png
 create mode 100644 tools/grit/grit/testdata/default_100_percent/b.png
 create mode 100644 tools/grit/grit/testdata/del_footer.html
 create mode 100644 tools/grit/grit/testdata/del_header.html
 create mode 100644 tools/grit/grit/testdata/deleted.html
 create mode 100644 tools/grit/grit/testdata/depfile.grd
 create mode 100644 tools/grit/grit/testdata/details.html
 create mode 100644 tools/grit/grit/testdata/duplicate-name-input.xml
 create mode 100644 tools/grit/grit/testdata/email_result.html
 create mode 100644 tools/grit/grit/testdata/email_thread.html
 create mode 100644 tools/grit/grit/testdata/error.html
 create mode 100644 tools/grit/grit/testdata/explicit_web.html
 create mode 100644 tools/grit/grit/testdata/footer.html
 create mode 100644 tools/grit/grit/testdata/generated_resources_fr.xtb
 create mode 100644 tools/grit/grit/testdata/generated_resources_iw.xtb
 create mode 100644 tools/grit/grit/testdata/generated_resources_no.xtb
 create mode 100644 tools/grit/grit/testdata/grit_part.grdp
 create mode 100644 tools/grit/grit/testdata/header.html
 create mode 100644 tools/grit/grit/testdata/homepage.html
 create mode 100644 tools/grit/grit/testdata/hover.html
 create mode 100644 tools/grit/grit/testdata/include_test.html
 create mode 100644 tools/grit/grit/testdata/included_sample.html
 create mode 100644 tools/grit/grit/testdata/indexing_speed.html
 create mode 100644 tools/grit/grit/testdata/install_prefs.html
 create mode 100644 tools/grit/grit/testdata/install_prefs2.html
 create mode 100644 tools/grit/grit/testdata/klonk-alternate-skeleton.rc
 create mode 100644 tools/grit/grit/testdata/klonk.ico
 create mode 100644 tools/grit/grit/testdata/klonk.rc
 create mode 100644 tools/grit/grit/testdata/ko_oem_enable_bug.html
 create mode 100644 tools/grit/grit/testdata/ko_oem_non_admin_bug.html
 create mode 100644 tools/grit/grit/testdata/mini.html
 create mode 100644 tools/grit/grit/testdata/oem_enable.html
 create mode 100644 tools/grit/grit/testdata/oem_non_admin.html
 create mode 100644 tools/grit/grit/testdata/onebox.html
 create mode 100644 tools/grit/grit/testdata/oneclick.html
 create mode 100644 tools/grit/grit/testdata/password.html
 create mode 100644 tools/grit/grit/testdata/preferences.html
 create mode 100644 tools/grit/grit/testdata/preprocess_test.html
 create mode 100644 tools/grit/grit/testdata/privacy.html
 create mode 100644 tools/grit/grit/testdata/quit_apps.html
 create mode 100644 tools/grit/grit/testdata/recrawl.html
 create mode 100644 tools/grit/grit/testdata/resource_ids
 create mode 100644 tools/grit/grit/testdata/script.html
 create mode 100644 tools/grit/grit/testdata/searchbox.html
 create mode 100644 tools/grit/grit/testdata/sidebar_h.html
 create mode 100644 tools/grit/grit/testdata/sidebar_v.html
 create mode 100644 tools/grit/grit/testdata/simple-input.xml
 create mode 100644 tools/grit/grit/testdata/simple.html
 create mode 100644 tools/grit/grit/testdata/source.rc
 create mode 100644 tools/grit/grit/testdata/special_100_percent/a.png
 create mode 100644 tools/grit/grit/testdata/status.html
 create mode 100644 tools/grit/grit/testdata/structure_variables.html
 create mode 100644 tools/grit/grit/testdata/substitute.grd
 create mode 100644 tools/grit/grit/testdata/substitute.xmb
 create mode 100644 tools/grit/grit/testdata/substitute_no_ids.grd
 create mode 100644 tools/grit/grit/testdata/substitute_tmpl.grd
 create mode 100644 tools/grit/grit/testdata/test_css.css
 create mode 100644 tools/grit/grit/testdata/test_html.html
 create mode 100644 tools/grit/grit/testdata/test_js.js
 create mode 100644 tools/grit/grit/testdata/test_svg.svg
 create mode 100644 tools/grit/grit/testdata/test_text.txt
 create mode 100644 tools/grit/grit/testdata/time_related.html
 create mode 100644 tools/grit/grit/testdata/toolbar_about.html
 create mode 100644 tools/grit/grit/testdata/tools/grit/resource_ids
 create mode 100644 tools/grit/grit/testdata/transl.rc
 create mode 100644 tools/grit/grit/testdata/versions.html
 create mode 100644 tools/grit/grit/testdata/whitelist.txt
 create mode 100644 tools/grit/grit/testdata/whitelist_resources.grd
 create mode 100644 tools/grit/grit/testdata/whitelist_strings.grd
 create mode 100644 tools/grit/grit/tool/__init__.py
 create mode 100644 tools/grit/grit/tool/android2grd.py
 create mode 100644 tools/grit/grit/tool/android2grd_unittest.py
 create mode 100644 tools/grit/grit/tool/build.py
 create mode 100644 tools/grit/grit/tool/build_unittest.py
 create mode 100644 tools/grit/grit/tool/buildinfo.py
 create mode 100644 tools/grit/grit/tool/buildinfo_unittest.py
 create mode 100644 tools/grit/grit/tool/count.py
 create mode 100644 tools/grit/grit/tool/diff_structures.py
 create mode 100644 tools/grit/grit/tool/diff_structures_unittest.py
 create mode 100644 tools/grit/grit/tool/interface.py
 create mode 100644 tools/grit/grit/tool/menu_from_parts.py
 create mode 100644 tools/grit/grit/tool/newgrd.py
 create mode 100644 tools/grit/grit/tool/newgrd_unittest.py
 create mode 100644 tools/grit/grit/tool/postprocess_interface.py
 create mode 100644 tools/grit/grit/tool/postprocess_unittest.py
 create mode 100644 tools/grit/grit/tool/preprocess_interface.py
 create mode 100644 tools/grit/grit/tool/preprocess_unittest.py
 create mode 100644 tools/grit/grit/tool/rc2grd.py
 create mode 100644 tools/grit/grit/tool/rc2grd_unittest.py
 create mode 100644 tools/grit/grit/tool/resize.py
 create mode 100644 tools/grit/grit/tool/test.py
 create mode 100644 tools/grit/grit/tool/transl2tc.py
 create mode 100644 tools/grit/grit/tool/transl2tc_unittest.py
 create mode 100644 tools/grit/grit/tool/unit.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/__init__.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/common.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/parser.py
 create mode 100644 tools/grit/grit/tool/update_resource_ids/reader.py
 create mode 100644 tools/grit/grit/tool/xmb.py
 create mode 100644 tools/grit/grit/tool/xmb_unittest.py
 create mode 100644 tools/grit/grit/util.py
 create mode 100644 tools/grit/grit/util_unittest.py
 create mode 100644 tools/grit/grit/xtb_reader.py
 create mode 100644 tools/grit/grit/xtb_reader_unittest.py
 create mode 100644 tools/grit/grit_info.py
 create mode 100644 tools/grit/grit_rule.gni
 create mode 100644 tools/grit/minify_with_uglify.py
 create mode 100644 tools/grit/minimize_css.py
 create mode 100644 tools/grit/minimize_css_unittest.py
 create mode 100644 tools/grit/pak_util.py
 create mode 100644 tools/grit/repack.gni
 create mode 100644 tools/grit/setup.py
 create mode 100644 tools/grit/stamp_grit_sources.py
 create mode 100644 tools/grit/third_party/six/LICENSE
 create mode 100644 tools/grit/third_party/six/README
 create mode 100644 tools/grit/third_party/six/README.chromium
 create mode 100644 tools/grit/third_party/six/__init__.py

diff --git a/tools/grit/.gitignore b/tools/grit/.gitignore
new file mode 100644
index 0000000000..0d20b6487c
--- /dev/null
+++ b/tools/grit/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/tools/grit/BUILD.gn b/tools/grit/BUILD.gn
new file mode 100644
index 0000000000..1cd3c75b55
--- /dev/null
+++ b/tools/grit/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This target creates a stamp file that depends on all the sources in the grit
+# directory. By depending on this, a target can force itself to be rebuilt if
+# grit itself changes.
+
+import("//build/config/sanitizers/sanitizers.gni")
+
+action("grit_sources") {
+  depfile = "$target_out_dir/grit_sources.d"
+  script = "stamp_grit_sources.py"
+
+  inputs = [ "grit.py" ]
+
+  # Note that we can't call this "grit_sources.stamp" because that file is
+  # implicitly created by GN for script actions.
+  outputs = [ "$target_out_dir/grit_sources.script.stamp" ]
+
+  args = [
+    rebase_path("//tools/grit", root_build_dir),
+    rebase_path(outputs[0], root_build_dir),
+    rebase_path(depfile, root_build_dir),
+  ]
+}
+
+group("grit_python_unittests") {
+  testonly = true
+
+  data = [
+    "//testing/scripts/common.py",
+    "//testing/scripts/run_isolated_script_test.py",
+    "//testing/xvfb.py",
+    "//tools/grit/",
+    "//third_party/catapult/third_party/typ/",
+  ]
+}
+
+# See https://crbug.com/983200
+if (is_mac && is_asan) {
+  create_bundle("brotli_mac_asan_workaround") {
+    bundle_root_dir = "$target_out_dir/$target_name"
+    bundle_executable_dir = bundle_root_dir
+
+    public_deps = [ "//third_party/brotli:brotli($host_toolchain)" ]
+  }
+}
diff --git a/tools/grit/MANIFEST.in b/tools/grit/MANIFEST.in
new file mode 100644
index 0000000000..1cbff42400
--- /dev/null
+++ b/tools/grit/MANIFEST.in
@@ -0,0 +1,3 @@
+exclude grit/test_suite_all.py
+exclude grit/tool/test.py
+global-exclude *_unittest.py
diff --git a/tools/grit/OWNERS b/tools/grit/OWNERS
new file mode 100644
index 0000000000..6a8f447b82
--- /dev/null
+++ b/tools/grit/OWNERS
@@ -0,0 +1,8 @@
+agrieve@chromium.org
+flackr@chromium.org
+thakis@chromium.org
+thestig@chromium.org
+
+# Admin policy related grit tools.
+per-file *policy*=file://components/policy/tools/OWNERS
+per-file *admin_template*=file://components/policy/tools/OWNERS
diff --git a/tools/grit/PRESUBMIT.py b/tools/grit/PRESUBMIT.py
new file mode 100644
index 0000000000..03b7188551
--- /dev/null
+++ b/tools/grit/PRESUBMIT.py
@@ -0,0 +1,22 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""grit unittests presubmit script.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into gcl.
+"""
+
+
+def RunUnittests(input_api, output_api):
+  return input_api.canned_checks.RunUnitTests(input_api, output_api,
+      [input_api.os_path.join('grit', 'test_suite_all.py')])
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return RunUnittests(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return RunUnittests(input_api, output_api)
diff --git a/tools/grit/README.md b/tools/grit/README.md
new file mode 100644
index 0000000000..b5c3f4b51b
--- /dev/null
+++ b/tools/grit/README.md
@@ -0,0 +1,19 @@
+# GRIT (Google Resource and Internationalization Tool)
+
+This is a tool for projects to manage resources and simplify the localization
+workflow.
+
+See the user guide for more details on using this project:
+https://dev.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide
+
+## History
+
+This code previously used to live at
+https://code.google.com/p/grit-i18n/source/checkout which still contains the
+project's history.  https://chromium.googlesource.com/external/grit-i18n/ is
+a git mirror of the SVN repository that's identical except for the last two
+commits.  The project is now developed in the Chromium project directly.
+
+There is a read-only mirror of just this directory at
+https://chromium.googlesource.com/chromium/src/tools/grit/ if you don't want to
+check out all of Chromium.
diff --git a/tools/grit/grit.py b/tools/grit/grit.py
new file mode 100644
index 0000000000..abd1ab6449
--- /dev/null
+++ b/tools/grit/grit.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Bootstrapping for GRIT.
+'''
+
+from __future__ import print_function
+
+import os
+import sys
+
+import grit.grit_runner
+
+sys.path.append(
+    os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+        'diagnosis'))
+try:
+  import crbug_1001171
+except ImportError:
+  crbug_1001171 = None
+
+
+if __name__ == '__main__':
+  if crbug_1001171:
+    with crbug_1001171.DumpStateOnLookupError():
+      sys.exit(grit.grit_runner.Main(sys.argv[1:]))
+  else:
+    sys.exit(grit.grit_runner.Main(sys.argv[1:]))
diff --git a/tools/grit/grit/__init__.py b/tools/grit/grit/__init__.py
new file mode 100644
index 0000000000..91ac9ee896
--- /dev/null
+++ b/tools/grit/grit/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Package 'grit'
+'''
+
+from __future__ import print_function
+
+import os
+import sys
+
+
+_CUR_DIR = os.path.abspath(os.path.dirname(__file__))
+_GRIT_DIR = os.path.dirname(_CUR_DIR)
+_THIRD_PARTY_DIR = os.path.join(_GRIT_DIR, 'third_party')
+
+if _THIRD_PARTY_DIR not in sys.path:
+  sys.path.insert(0, _THIRD_PARTY_DIR)
diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py
new file mode 100644
index 0000000000..e7be3ec164
--- /dev/null
+++ b/tools/grit/grit/clique.py
@@ -0,0 +1,491 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Collections of messages and their translations, called cliques.  Also
+collections of cliques (uber-cliques).
+'''
+
+from __future__ import print_function
+
+import re
+
+import six
+
+from grit import constants
+from grit import exception
+from grit import lazy_re
+from grit import pseudo
+from grit import pseudo_rtl
+from grit import tclib
+
+
+class UberClique(object):
+  '''A factory (NOT a singleton factory) for making cliques.  It has several
+  methods for working with the cliques created using the factory.
+  '''
+
+  def __init__(self):
+    # A map from message ID to list of cliques whose source messages have
+    # that ID.  This will contain all cliques created using this factory.
+    # Different messages can have the same ID because they have the
+    # same translateable portion and placeholder names, but occur in different
+    # places in the resource tree.
+    #
+    # Each list of cliques is kept sorted by description, to achieve
+    # stable results from the BestClique method, see below.
+    self.cliques_ = {}
+
+    # A map of clique IDs to list of languages to indicate translations where we
+    # fell back to English.
+    self.fallback_translations_ = {}
+
+    # A map of clique IDs to list of languages to indicate missing translations.
+    self.missing_translations_ = {}
+
+  def _AddMissingTranslation(self, lang, clique, is_error):
+    tl = self.fallback_translations_
+    if is_error:
+      tl = self.missing_translations_
+    id = clique.GetId()
+    if id not in tl:
+      tl[id] = {}
+    if lang not in tl[id]:
+      tl[id][lang] = 1
+
+  def HasMissingTranslations(self):
+    return len(self.missing_translations_) > 0
+
+  def MissingTranslationsReport(self):
+    '''Returns a string suitable for printing to report missing
+    and fallback translations to the user.
+    '''
+    def ReportTranslation(clique, langs):
+      text = clique.GetMessage().GetPresentableContent()
+      # The text 'error' (usually 'Error:' but we are conservative)
+      # can trigger some build environments (Visual Studio, we're
+      # looking at you) to consider invocation of grit to have failed,
+      # so we make sure never to output that word.
+      extract = re.sub(r'(?i)error', 'REDACTED', text[0:40])[0:40]
+      ellipsis = ''
+      if len(text) > 40:
+        ellipsis = '...'
+      langs_extract = langs[0:6]
+      describe_langs = ','.join(langs_extract)
+      if len(langs) > 6:
+        describe_langs += " and %d more" % (len(langs) - 6)
+      return "  %s \"%s%s\" %s" % (clique.GetId(), extract, ellipsis,
+                                   describe_langs)
+    lines = []
+    if len(self.fallback_translations_):
+      lines.append(
+        "WARNING: Fell back to English for the following translations:")
+      for (id, langs) in self.fallback_translations_.items():
+        lines.append(
+            ReportTranslation(self.cliques_[id][0], list(langs.keys())))
+    if len(self.missing_translations_):
+      lines.append("ERROR: The following translations are MISSING:")
+      for (id, langs) in self.missing_translations_.items():
+        lines.append(
+            ReportTranslation(self.cliques_[id][0], list(langs.keys())))
+    return '\n'.join(lines)
+
+  def MakeClique(self, message, translateable=True):
+    '''Create a new clique initialized  with a message.
+
+    Args:
+      message: tclib.Message()
+      translateable: True | False
+    '''
+    clique = MessageClique(self, message, translateable)
+
+    # Enable others to find this clique by its message ID
+    if message.GetId() in self.cliques_:
+      presentable_text = clique.GetMessage().GetPresentableContent()
+      if not message.HasAssignedId():
+        for c in self.cliques_[message.GetId()]:
+          assert c.GetMessage().GetPresentableContent() == presentable_text
+      self.cliques_[message.GetId()].append(clique)
+      # We need to keep each list of cliques sorted by description, to
+      # achieve stable results from the BestClique method, see below.
+      self.cliques_[message.GetId()].sort(
+          key=lambda c:c.GetMessage().GetDescription())
+    else:
+      self.cliques_[message.GetId()] = [clique]
+
+    return clique
+
+  def FindCliqueAndAddTranslation(self, translation, language):
+    '''Adds the specified translation to the clique with the source message
+    it is a translation of.
+
+    Args:
+      translation: tclib.Translation()
+      language: 'en' | 'fr' ...
+
+    Return:
+      True if the source message was found, otherwise false.
+    '''
+    if translation.GetId() in self.cliques_:
+      for clique in self.cliques_[translation.GetId()]:
+        clique.AddTranslation(translation, language)
+      return True
+    else:
+      return False
+
+  def BestClique(self, id):
+    '''Returns the "best" clique from a list of cliques.  All the cliques
+    must have the same ID.  The "best" clique is chosen in the following
+    order of preference:
+    - The first clique that has a non-ID-based description.
+    - If no such clique found, the first clique with an ID-based description.
+    - Otherwise the first clique.
+
+    This method is stable in terms of always returning a clique with
+    an identical description (on different runs of GRIT on the same
+    data) because self.cliques_ is sorted by description.
+    '''
+    clique_list = self.cliques_[id]
+    clique_with_id = None
+    clique_default = None
+    for clique in clique_list:
+      if not clique_default:
+        clique_default = clique
+
+      description = clique.GetMessage().GetDescription()
+      if description and len(description) > 0:
+        if not description.startswith('ID:'):
+          # this is the preferred case so we exit right away
+          return clique
+        elif not clique_with_id:
+          clique_with_id = clique
+    if clique_with_id:
+      return clique_with_id
+    else:
+      return clique_default
+
+  def BestCliquePerId(self):
+    '''Iterates over the list of all cliques and returns the best clique for
+    each ID.  This will be the first clique with a source message that has a
+    non-empty description, or an arbitrary clique if none of them has a
+    description.
+    '''
+    for id in self.cliques_:
+      yield self.BestClique(id)
+
+  def BestCliqueByOriginalText(self, text, meaning):
+    '''Finds the "best" (as in BestClique()) clique that has original text
+    'text' and meaning 'meaning'.  Returns None if there is no such clique.
+    '''
+    # If needed, this can be optimized by maintaining a map of
+    # fingerprints of original text+meaning to cliques.
+    for c in self.BestCliquePerId():
+      msg = c.GetMessage()
+      if msg.GetRealContent() == text and msg.GetMeaning() == meaning:
+        return msg
+    return None
+
+  def AllMessageIds(self):
+    '''Returns a list of all defined message IDs.
+    '''
+    return list(self.cliques_.keys())
+
+  def AllCliques(self):
+    '''Iterates over all cliques.  Note that this can return multiple cliques
+    with the same ID.
+    '''
+    for cliques in self.cliques_.values():
+      for c in cliques:
+        yield c
+
+  def GenerateXtbParserCallback(self, lang, debug=False):
+    '''Creates a callback function as required by grit.xtb_reader.Parse().
+    This callback will create Translation objects for each message from
+    the XTB that exists in this uberclique, and add them as translations for
+    the relevant cliques.  The callback will add translations to the language
+    specified by 'lang'
+
+    Args:
+      lang: 'fr'
+      debug: True | False
+    '''
+    def Callback(id, structure):
+      if id not in self.cliques_:
+        if debug:
+          print("Ignoring translation #%s" % id)
+        return
+
+      if debug:
+        print("Adding translation #%s" % id)
+
+      # We fetch placeholder information from the original message (the XTB file
+      # only contains placeholder names).
+      original_msg = self.BestClique(id).GetMessage()
+
+      translation = tclib.Translation(id=id)
+      for is_ph,text in structure:
+        if not is_ph:
+          translation.AppendText(text)
+        else:
+          found_placeholder = False
+          for ph in original_msg.GetPlaceholders():
+            if ph.GetPresentation() == text:
+              translation.AppendPlaceholder(tclib.Placeholder(
+                ph.GetPresentation(), ph.GetOriginal(), ph.GetExample()))
+              found_placeholder = True
+              break
+          if not found_placeholder:
+            raise exception.MismatchingPlaceholders(
+              'Translation for message ID %s had <ph name="%s"/>, no match\n'
+              'in original message' % (id, text))
+      self.FindCliqueAndAddTranslation(translation, lang)
+    return Callback
+
+
+class CustomType(object):
+  '''A base class you should implement if you wish to specify a custom type
+  for a message clique (i.e. custom validation and optional modification of
+  translations).'''
+
+  def Validate(self, message):
+    '''Returns true if the message (a tclib.Message object) is valid,
+    otherwise false.
+    '''
+    raise NotImplementedError()
+
+  def ValidateAndModify(self, lang, translation):
+    '''Returns true if the translation (a tclib.Translation object) is valid,
+    otherwise false.  The language is also passed in.  This method may modify
+    the translation that is passed in, if it so wishes.
+    '''
+    raise NotImplementedError()
+
+  def ModifyTextPart(self, lang, text):
+    '''If you call ModifyEachTextPart, it will turn around and call this method
+    for each text part of the translation.  You should return the modified
+    version of the text, or just the original text to not change anything.
+    '''
+    raise NotImplementedError()
+
+  def ModifyEachTextPart(self, lang, translation):
+    '''Call this to easily modify one or more of the textual parts of a
+    translation.  It will call ModifyTextPart for each part of the
+    translation.
+    '''
+    contents = translation.GetContent()
+    for ix in range(len(contents)):
+      if (isinstance(contents[ix], six.string_types)):
+        contents[ix] = self.ModifyTextPart(lang, contents[ix])
+
+
+class OneOffCustomType(CustomType):
+  '''A very simple custom type that performs the validation expressed by
+  the input expression on all languages including the source language.
+  The expression can access the variables 'lang', 'msg' and 'text()' where
+  'lang' is the language of 'msg', 'msg' is the message or translation being
+  validated and 'text()' returns the real contents of 'msg' (for shorthand).
+  '''
+  def __init__(self, expression):
+    self.expr = expression
+  def Validate(self, message):
+    return self.ValidateAndModify(MessageClique.source_language, message)
+  def ValidateAndModify(self, lang, msg):
+    def text():
+      return msg.GetRealContent()
+    return eval(self.expr, {},
+            {'lang' : lang,
+             'text' : text,
+             'msg' : msg,
+             })
+
+
+class MessageClique(object):
+  '''A message along with all of its translations.  Also code to bring
+  translations together with their original message.'''
+
+  # change this to the language code of Messages you add to cliques_.
+  # TODO(joi) Actually change this based on the <grit> node's source language
+  source_language = 'en'
+
+  # A constant translation we use when asked for a translation into the
+  # special language constants.CONSTANT_LANGUAGE.
+  CONSTANT_TRANSLATION = tclib.Translation(text='TTTTTT')
+
+  # A pattern to match messages that are empty or whitespace only.
+  WHITESPACE_MESSAGE = lazy_re.compile(r'^\s*$')
+
+  def __init__(self, uber_clique, message, translateable=True,
+               custom_type=None):
+    '''Create a new clique initialized with just a message.
+
+    Note that messages with a body comprised only of whitespace will implicitly
+    be marked non-translatable.
+
+    Args:
+      uber_clique: Our uber-clique (collection of cliques)
+      message: tclib.Message()
+      translateable: True | False
+      custom_type: instance of clique.CustomType interface
+    '''
+    # Our parent
+    self.uber_clique = uber_clique
+    # If not translateable, we only store the original message.
+    self.translateable = translateable
+
+    # We implicitly mark messages that have a whitespace-only body as
+    # non-translateable.
+    if MessageClique.WHITESPACE_MESSAGE.match(message.GetRealContent()):
+      self.translateable = False
+
+    # A mapping of language identifiers to tclib.BaseMessage and its
+    # subclasses (i.e. tclib.Message and tclib.Translation).
+    self.clique = { MessageClique.source_language : message }
+    # A list of the "shortcut groups" this clique is
+    # part of.  Within any given shortcut group, no shortcut key (e.g. &J)
+    # must appear more than once in each language for all cliques that
+    # belong to the group.
+    self.shortcut_groups = []
+    # An instance of the CustomType interface, or None.  If this is set, it will
+    # be used to validate the original message and translations thereof, and
+    # will also get a chance to modify translations of the message.
+    self.SetCustomType(custom_type)
+
+  def GetMessage(self):
+    '''Retrieves the tclib.Message that is the source for this clique.'''
+    return self.clique[MessageClique.source_language]
+
+  def GetId(self):
+    '''Retrieves the message ID of the messages in this clique.'''
+    return self.GetMessage().GetId()
+
+  def IsTranslateable(self):
+    return self.translateable
+
+  def AddToShortcutGroup(self, group):
+    self.shortcut_groups.append(group)
+
+  def SetCustomType(self, custom_type):
+    '''Makes this clique use custom_type for validating messages and
+    translations, and optionally modifying translations.
+    '''
+    self.custom_type = custom_type
+    if custom_type and not custom_type.Validate(self.GetMessage()):
+      raise exception.InvalidMessage(self.GetMessage().GetRealContent())
+
+  def MessageForLanguage(self, lang, pseudo_if_no_match=True,
+                         fallback_to_english=False):
+    '''Returns the message/translation for the specified language, providing
+    a pseudotranslation if there is no available translation and a pseudo-
+    translation is requested.
+
+    The translation of any message whatsoever in the special language
+    'x_constant' is the message "TTTTTT".
+
+    Args:
+      lang: 'en'
+      pseudo_if_no_match: True
+      fallback_to_english: False
+
+    Return:
+      tclib.BaseMessage
+    '''
+    if not self.translateable:
+      return self.GetMessage()
+
+    if lang == constants.CONSTANT_LANGUAGE:
+      return self.CONSTANT_TRANSLATION
+
+    for msglang in self.clique:
+      if lang == msglang:
+        return self.clique[msglang]
+
+    if lang == constants.FAKE_BIDI:
+      return pseudo_rtl.PseudoRTLMessage(self.GetMessage())
+
+    if fallback_to_english:
+      self.uber_clique._AddMissingTranslation(lang, self, is_error=False)
+      return self.GetMessage()
+
+    # If we're not supposed to generate pseudotranslations, we add an error
+    # report to a list of errors, then fail at a higher level, so that we
+    # get a list of all messages that are missing translations.
+    if not pseudo_if_no_match:
+      self.uber_clique._AddMissingTranslation(lang, self, is_error=True)
+
+    return pseudo.PseudoMessage(self.GetMessage())
+
+  def AllMessagesThatMatch(self, lang_re, include_pseudo = True):
+    '''Returns a map of all messages that match 'lang', including the pseudo
+    translation if requested.
+
+    Args:
+      lang_re: re.compile(r'fr|en')
+      include_pseudo: True
+
+    Return:
+      { 'en' : tclib.Message,
+        'fr' : tclib.Translation,
+        pseudo.PSEUDO_LANG : tclib.Translation }
+    '''
+    if not self.translateable:
+      return [self.GetMessage()]
+
+    matches = {}
+    for msglang in self.clique:
+      if lang_re.match(msglang):
+        matches[msglang] = self.clique[msglang]
+
+    if include_pseudo:
+      matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage())
+
+    return matches
+
+  def AddTranslation(self, translation, language):
+    '''Add a translation to this clique.  The translation must have the same
+    ID as the message that is the source for this clique.
+
+    If this clique is not translateable, the function just returns.
+
+    Args:
+      translation: tclib.Translation()
+      language: 'en'
+
+    Throws:
+      grit.exception.InvalidTranslation if the translation you're trying to add
+      doesn't have the same message ID as the source message of this clique.
+    '''
+    if not self.translateable:
+      return
+    if translation.GetId() != self.GetId():
+      raise exception.InvalidTranslation(
+        'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId()))
+
+    assert not language in self.clique
+
+    # Because two messages can differ in the original content of their
+    # placeholders yet share the same ID (because they are otherwise the
+    # same), the translation we are getting may have different original
+    # content for placeholders than our message, yet it is still the right
+    # translation for our message (because it is for the same ID).  We must
+    # therefore fetch the original content of placeholders from our original
+    # English message.
+    #
+    # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques
+    # for a concrete explanation of why this is necessary.
+
+    original = self.MessageForLanguage(self.source_language, False)
+    if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()):
+      print("ERROR: '%s' translation of message id %s does not match" %
+            (language, translation.GetId()))
+      assert False
+
+    transl_msg = tclib.Translation(id=self.GetId(),
+                                   text=translation.GetPresentableContent(),
+                                   placeholders=original.GetPlaceholders())
+
+    if (self.custom_type and
+        not self.custom_type.ValidateAndModify(language, transl_msg)):
+      print("WARNING: %s translation failed validation: %s" %
+            (language, transl_msg.GetId()))
+
+    self.clique[language] = transl_msg
diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
new file mode 100644
index 0000000000..7d2d7318ba
--- /dev/null
+++ b/tools/grit/grit/clique_unittest.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Unit tests for grit.clique'''
+
+from __future__ import print_function
+
+import os
+import sys
+if __name__ == '__main__':
+  sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+import re
+import unittest
+
+from six import StringIO
+
+from grit import clique
+from grit import exception
+from grit import pseudo
+from grit import tclib
+from grit import grd_reader
+from grit import util
+
+class MessageCliqueUnittest(unittest.TestCase):
+  def testClique(self):
+    factory = clique.UberClique()
+    msg = tclib.Message(text='Hello USERNAME, how are you?',
+                        placeholders=[
+                          tclib.Placeholder('USERNAME', '%s', 'Joi')])
+    c = factory.MakeClique(msg)
+
+    self.failUnless(c.GetMessage() == msg)
+    self.failUnless(c.GetId() == msg.GetId())
+
+    msg_fr = tclib.Translation(text='Bonjour USERNAME, comment ca va?',
+                               id=msg.GetId(), placeholders=[
+                                tclib.Placeholder('USERNAME', '%s', 'Joi')])
+    msg_de = tclib.Translation(text='Guten tag USERNAME, wie geht es dir?',
+                               id=msg.GetId(), placeholders=[
+                                tclib.Placeholder('USERNAME', '%s', 'Joi')])
+
+    c.AddTranslation(msg_fr, 'fr')
+    factory.FindCliqueAndAddTranslation(msg_de, 'de')
+
+    # sort() sorts lists in-place and does not return them
+    for lang in ('en', 'fr', 'de'):
+      self.failUnless(lang in c.clique)
+
+    self.failUnless(c.MessageForLanguage('fr').GetRealContent() ==
+                    msg_fr.GetRealContent())
+
+    try:
+      c.MessageForLanguage('zh-CN', False)
+      self.fail('Should have gotten exception')
+    except:
+      pass
+
+    self.failUnless(c.MessageForLanguage('zh-CN', True) != None)
+
+    rex = re.compile('fr|de|bingo')
+    self.failUnless(len(c.AllMessagesThatMatch(rex, False)) == 2)
+    self.failUnless(
+        c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
+
+  def testBestClique(self):
+    factory = clique.UberClique()
+    factory.MakeClique(tclib.Message(text='Alfur', description='alfaholl'))
+    factory.MakeClique(tclib.Message(text='Alfur', description=''))
+    factory.MakeClique(tclib.Message(text='Vaettur', description=''))
+    factory.MakeClique(tclib.Message(text='Vaettur', description=''))
+    factory.MakeClique(tclib.Message(text='Troll', description=''))
+    factory.MakeClique(tclib.Message(text='Gryla', description='ID: IDS_GRYLA'))
+    factory.MakeClique(tclib.Message(text='Gryla', description='vondakerling'))
+    factory.MakeClique(tclib.Message(text='Leppaludi', description='ID: IDS_LL'))
+    factory.MakeClique(tclib.Message(text='Leppaludi', description=''))
+
+    count_best_cliques = 0
+    for c in factory.BestCliquePerId():
+      count_best_cliques += 1
+      msg = c.GetMessage()
+      text = msg.GetRealContent()
+      description = msg.GetDescription()
+      if text == 'Alfur':
+        self.failUnless(description == 'alfaholl')
+      elif text == 'Gryla':
+        self.failUnless(description == 'vondakerling')
+      elif text == 'Leppaludi':
+        self.failUnless(description == 'ID: IDS_LL')
+    self.failUnless(count_best_cliques == 5)
+
+  def testAllInUberClique(self):
+    resources = grd_reader.Parse(
+        StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
+  <release seq="3">
+    <messages>
+      <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
+        Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
+      </message>
+    </messages>
+    <structures>
+      <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
+      <structure type="tr_html" name="ID_HTML" file="grit/testdata/simple.html" />
+    </structures>
+  </release>
+</grit>'''), util.PathFromRoot('.'))
+    resources.SetOutputLanguage('en')
+    resources.RunGatherers()
+    content_list = []
+    for clique_list in resources.UberClique().cliques_.values():
+      for clique in clique_list:
+        content_list.append(clique.GetMessage().GetRealContent())
+    self.failUnless('Hello %s, how are you doing today?' in content_list)
+    self.failUnless('Jack "Black" Daniels' in content_list)
+    self.failUnless('Hello!' in content_list)
+
+  def testCorrectExceptionIfWrongEncodingOnResourceFile(self):
+    '''This doesn't really belong in this unittest file, but what the heck.'''
+    resources = grd_reader.Parse(
+        StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
+  <release seq="3">
+    <structures>
+      <structure type="dialog" name="IDD_ABOUTBOX" file="grit/testdata/klonk.rc" />
+    </structures>
+  </release>
+</grit>'''), util.PathFromRoot('.'))
+    self.assertRaises(exception.SectionNotFound, resources.RunGatherers)
+
+  def testSemiIdenticalCliques(self):
+    messages = [
+      tclib.Message(text='Hello USERNAME',
+                    placeholders=[tclib.Placeholder('USERNAME', '$1', 'Joi')]),
+      tclib.Message(text='Hello USERNAME',
+                    placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]),
+    ]
+    self.failUnless(messages[0].GetId() == messages[1].GetId())
+
+    # Both of the above would share a translation.
+    translation = tclib.Translation(id=messages[0].GetId(),
+                                    text='Bonjour USERNAME',
+                                    placeholders=[tclib.Placeholder(
+                                      'USERNAME', '$1', 'Joi')])
+
+    factory = clique.UberClique()
+    cliques = [factory.MakeClique(msg) for msg in messages]
+
+    for clq in cliques:
+      clq.AddTranslation(translation, 'fr')
+
+    self.failUnless(cliques[0].MessageForLanguage('fr').GetRealContent() ==
+                    'Bonjour $1')
+    self.failUnless(cliques[1].MessageForLanguage('fr').GetRealContent() ==
+                    'Bonjour %s')
+
+  def testMissingTranslations(self):
+    messages = [ tclib.Message(text='Hello'), tclib.Message(text='Goodbye') ]
+    factory = clique.UberClique()
+    cliques = [factory.MakeClique(msg) for msg in messages]
+
+    cliques[1].MessageForLanguage('fr', False, True)
+
+    self.failUnless(not factory.HasMissingTranslations())
+
+    cliques[0].MessageForLanguage('de', False, False)
+
+    self.failUnless(factory.HasMissingTranslations())
+
+    report = factory.MissingTranslationsReport()
+    self.failUnless(report.count('WARNING') == 1)
+    self.failUnless(report.count('8053599568341804890 "Goodbye" fr') == 1)
+    self.failUnless(report.count('ERROR') == 1)
+    self.failUnless(report.count('800120468867715734 "Hello" de') == 1)
+
+  def testCustomTypes(self):
+    factory = clique.UberClique()
+    message = tclib.Message(text='Bingo bongo')
+    c = factory.MakeClique(message)
+    try:
+      c.SetCustomType(DummyCustomType())
+      self.fail()
+    except:
+      pass  # expected case - 'Bingo bongo' does not start with 'jjj'
+
+    message = tclib.Message(text='jjjBingo bongo')
+    c = factory.MakeClique(message)
+    c.SetCustomType(util.NewClassInstance(
+      'grit.clique_unittest.DummyCustomType', clique.CustomType))
+    translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo')
+    c.AddTranslation(translation, 'fr')
+    self.failUnless(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
+
+  def testWhitespaceMessagesAreNontranslateable(self):
+    factory = clique.UberClique()
+
+    message = tclib.Message(text=' \t')
+    c = factory.MakeClique(message, translateable=True)
+    self.failIf(c.IsTranslateable())
+
+    message = tclib.Message(text='\n \n ')
+    c = factory.MakeClique(message, translateable=True)
+    self.failIf(c.IsTranslateable())
+
+    message = tclib.Message(text='\n hello')
+    c = factory.MakeClique(message, translateable=True)
+    self.failUnless(c.IsTranslateable())
+
+  def testEachCliqueKeptSorted(self):
+    factory = clique.UberClique()
+    msg_a = tclib.Message(text='hello', description='a')
+    msg_b = tclib.Message(text='hello', description='b')
+    msg_c = tclib.Message(text='hello', description='c')
+    # Insert out of order
+    clique_b = factory.MakeClique(msg_b, translateable=True)
+    clique_a = factory.MakeClique(msg_a, translateable=True)
+    clique_c = factory.MakeClique(msg_c, translateable=True)
+    clique_list = factory.cliques_[clique_a.GetId()]
+    self.failUnless(len(clique_list) == 3)
+    self.failUnless(clique_list[0] == clique_a)
+    self.failUnless(clique_list[1] == clique_b)
+    self.failUnless(clique_list[2] == clique_c)
+
+  def testBestCliqueSortIsStable(self):
+    factory = clique.UberClique()
+    text = 'hello'
+    msg_no_description = tclib.Message(text=text)
+    msg_id_description_a = tclib.Message(text=text, description='ID: a')
+    msg_id_description_b = tclib.Message(text=text, description='ID: b')
+    msg_description_x = tclib.Message(text=text, description='x')
+    msg_description_y = tclib.Message(text=text, description='y')
+    clique_id = msg_no_description.GetId()
+
+    # Insert in an order that tests all outcomes.
+    clique_no_description = factory.MakeClique(msg_no_description,
+                                               translateable=True)
+    self.failUnless(factory.BestClique(clique_id) == clique_no_description)
+    clique_id_description_b = factory.MakeClique(msg_id_description_b,
+                                                 translateable=True)
+    self.failUnless(factory.BestClique(clique_id) == clique_id_description_b)
+    clique_id_description_a = factory.MakeClique(msg_id_description_a,
+                                                 translateable=True)
+    self.failUnless(factory.BestClique(clique_id) == clique_id_description_a)
+    clique_description_y = factory.MakeClique(msg_description_y,
+                                              translateable=True)
+    self.failUnless(factory.BestClique(clique_id) == clique_description_y)
+    clique_description_x = factory.MakeClique(msg_description_x,
+                                              translateable=True)
+    self.failUnless(factory.BestClique(clique_id) == clique_description_x)
+
+
+class DummyCustomType(clique.CustomType):
+  def Validate(self, message):
+    return message.GetRealContent().startswith('jjj')
+  def ValidateAndModify(self, lang, translation):
+    is_ok = self.Validate(translation)
+    self.ModifyEachTextPart(lang, translation)
+  def ModifyTextPart(self, lang, text):
+    return 'jjj%s' % text
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/grit/grit/constants.py b/tools/grit/grit/constants.py
new file mode 100644
index 0000000000..8229c94b09
--- /dev/null
+++ b/tools/grit/grit/constants.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Constant definitions for GRIT.
+'''
+
+from __future__ import print_function
+
+# This is the Icelandic noun meaning "grit" and is used to check that our
+# input files are in the correct encoding.  The middle character gets encoded
+# as two bytes in UTF-8, so this is sufficient to detect incorrect encoding.
+ENCODING_CHECK = u'm\u00f6l'
+
+# A special language, translations into which are always "TTTTTT".
+CONSTANT_LANGUAGE = 'x_constant'
+
+FAKE_BIDI = 'fake-bidi'
+
+# Magic number added to the header of resources brotli compressed by grit. Used
+# to easily identify resources as being brotli compressed. See
+# ui/base/resource/resource_bundle.h for decompression usage.
+BROTLI_CONST = b'\x1e\x9b'
diff --git a/tools/grit/grit/exception.py b/tools/grit/grit/exception.py
new file mode 100644
index 0000000000..2a363fb077
--- /dev/null
+++ b/tools/grit/grit/exception.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Exception types for GRIT.
+'''
+
+from __future__ import print_function
+
+class Base(Exception):
+  '''A base exception that uses the class's docstring in addition to any
+  user-provided message as the body of the Base.
+  '''
+  def __init__(self, msg=''):
+    if len(msg):
+      if self.__doc__:
+        msg = self.__doc__ + ': ' + msg
+    else:
+      msg = self.__doc__
+    super(Base, self).__init__(msg)
+
+
+class Parsing(Base):
+  '''An error occurred parsing a GRD or XTB file.'''
+  pass
+
+
+class UnknownElement(Parsing):
+  '''An unknown node type was encountered.'''
+  pass
+
+
+class MissingElement(Parsing):
+  '''An expected element was missing.'''
+  pass
+
+
+class UnexpectedChild(Parsing):
+  '''An unexpected child element was encountered (on a leaf node).'''
+  pass
+
+
+class UnexpectedAttribute(Parsing):
+  '''The attribute was not expected'''
+  pass
+
+
+class UnexpectedContent(Parsing):
+  '''This element should not have content'''
+  pass
+
+class MissingMandatoryAttribute(Parsing):
+  '''This element is missing a mandatory attribute'''
+  pass
+
+
+class MutuallyExclusiveMandatoryAttribute(Parsing):
+  '''This element has 2 mutually exclusive mandatory attributes'''
+  pass
+
+
+class DuplicateKey(Parsing):
+  '''A duplicate key attribute was found.'''
+  pass
+
+
+class TooManyExamples(Parsing):
+  '''Only one <ex> element is allowed for each <ph> element.'''
+  pass
+
+
+class FileNotFound(Parsing):
+  '''The resource file was not found.'''
+  pass
+
+
+class InvalidMessage(Base):
+  '''The specified message failed validation.'''
+  pass
+
+
+class InvalidTranslation(Base):
+  '''Attempt to add an invalid translation to a clique.'''
+  pass
+
+
+class NoSuchTranslation(Base):
+  '''Requested translation not available'''
+  pass
+
+
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.233 Sekunden  ]