Merge from Chromium at DEPS revision db3f05efe0f9

This commit was generated by merge_to_master.py.

Change-Id: I25cedbc80c8f52b759d350181e5e8ddb18a942cb
diff --git a/grit/format/policy_templates/writer_configuration.py b/grit/format/policy_templates/writer_configuration.py
index 88de6b5..db9613b 100644
--- a/grit/format/policy_templates/writer_configuration.py
+++ b/grit/format/policy_templates/writer_configuration.py
@@ -52,6 +52,8 @@
     }
   else:
     raise Exception('Unknown build')
+  if 'version' in defines:
+    config['version'] = defines['version']
   config['win_group_policy_class'] = 'Both'
   config['win_supported_os'] = 'SUPPORTED_WINXPSP2'
   if 'mac_bundle_id' in defines:
diff --git a/grit/format/policy_templates/writers/adm_writer.py b/grit/format/policy_templates/writers/adm_writer.py
index bffbeb5..91aeef6 100644
--- a/grit/format/policy_templates/writers/adm_writer.py
+++ b/grit/format/policy_templates/writers/adm_writer.py
@@ -75,7 +75,12 @@
     'dict': 'EDITTEXT'
   }
 
+  def _Escape(self, string):
+    return string.replace('.', '_')
+
   def _AddGuiString(self, name, value):
+    # The |name| must be escaped.
+    assert name == self._Escape(name)
     # Escape newlines in the value.
     value = value.replace('\n', '\\n')
     if name in self.strings_seen:
@@ -100,7 +105,7 @@
       key_name: The registry key backing the policy.
       builder: Builder to append lines to.
     '''
-    policy_part_name = policy['name'] + '_Part'
+    policy_part_name = self._Escape(policy['name'] + '_Part')
     self._AddGuiString(policy_part_name, policy['label'])
 
     # Print the PART ... END PART section:
@@ -117,6 +122,10 @@
     if policy['type'] == 'int':
       # The default max for NUMERIC values is 9999 which is too small for us.
       builder.AddLine('MIN 0 MAX 2000000000')
+    if policy['type'] in ('string', 'dict'):
+      # The default max for EDITTEXT values is 1023, which is too small for
+      # big JSON blobs and other string policies.
+      builder.AddLine('MAXLEN 1000000')
     if policy['type'] in ('int-enum', 'string-enum'):
       builder.AddLine('ITEMLIST', 1)
       for item in policy['items']:
@@ -124,9 +133,9 @@
           value_text = 'NUMERIC ' + str(item['value'])
         else:
           value_text = '"' + item['value'] + '"'
-        builder.AddLine('NAME !!%s_DropDown VALUE %s' %
-            (item['name'], value_text))
-        self._AddGuiString(item['name'] + '_DropDown', item['caption'])
+        string_id = self._Escape(item['name'] + '_DropDown')
+        builder.AddLine('NAME !!%s VALUE %s' % (string_id, value_text))
+        self._AddGuiString(string_id, item['caption'])
       builder.AddLine('END ITEMLIST', -1)
     builder.AddLine('END PART', -1)
 
@@ -135,10 +144,11 @@
       # This type can only be set through cloud policy.
       return
 
-    self._AddGuiString(policy['name'] + '_Policy', policy['caption'])
-    builder.AddLine('POLICY !!%s_Policy' % policy['name'], 1)
+    policy_name = self._Escape(policy['name'] + '_Policy')
+    self._AddGuiString(policy_name, policy['caption'])
+    builder.AddLine('POLICY !!%s' % policy_name, 1)
     self._WriteSupported(builder)
-    policy_explain_name = policy['name'] + '_Explain'
+    policy_explain_name = self._Escape(policy['name'] + '_Explain')
     self._AddGuiString(policy_explain_name, policy['desc'])
     builder.AddLine('EXPLAIN !!' + policy_explain_name)
 
@@ -152,6 +162,9 @@
     builder.AddLine('END POLICY', -1)
     builder.AddLine()
 
+  def WriteComment(self, comment):
+    self.lines.AddLine('; ' + comment)
+
   def WritePolicy(self, policy):
     if self.CanBeMandatory(policy):
       self._WritePolicy(policy,
@@ -164,7 +177,7 @@
                       self.recommended_policies)
 
   def BeginPolicyGroup(self, group):
-    category_name = group['name'] + '_Category'
+    category_name = self._Escape(group['name'] + '_Category')
     self._AddGuiString(category_name, group['caption'])
     self.policies.AddLine('CATEGORY !!' + category_name, 1)
 
@@ -173,7 +186,7 @@
     self.policies.AddLine('')
 
   def BeginRecommendedPolicyGroup(self, group):
-    category_name = group['name'] + '_Category'
+    category_name = self._Escape(group['name'] + '_Category')
     self._AddGuiString(category_name, group['caption'])
     self.recommended_policies.AddLine('CATEGORY !!' + category_name, 1)
 
@@ -205,6 +218,9 @@
     return lines
 
   def BeginTemplate(self):
+    if self._GetChromiumVersionString() is not None:
+      self.WriteComment(self.config['build'] + ' version: ' + \
+          self._GetChromiumVersionString())
     self._AddGuiString(self.config['win_supported_os'],
                        self.messages['win_supported_winxpsp2']['text'])
     category_path = self.config['win_mandatory_category_path']
diff --git a/grit/format/policy_templates/writers/adm_writer_unittest.py b/grit/format/policy_templates/writers/adm_writer_unittest.py
index 82374bb..c5c64f7 100644
--- a/grit/format/policy_templates/writers/adm_writer_unittest.py
+++ b/grit/format/policy_templates/writers/adm_writer_unittest.py
@@ -76,6 +76,42 @@
 chromium_recommended="Chromium - Recommended"''')
     self.CompareOutputs(output, expected_output)
 
+  def testVersionAnnotation(self):
+    # Test PListWriter in case of empty polices.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [],
+        'placeholders': [],
+        'messages': {
+          'win_supported_winxpsp2': {
+            'text': 'At least "Windows 3.11', 'desc': 'blah'
+          },
+          'doc_recommended': {
+            'text': 'Recommended', 'desc': 'bleh'
+          }
+        }
+      }''')
+    output = self.GetOutput(
+        grd, 'fr', {'_chromium': '1', 'version':'39.0.0.0'}, 'adm', 'en')
+    expected_output = '; chromium version: 39.0.0.0\n' + \
+        self.ConstructOutput(['MACHINE', 'USER'], '''
+  CATEGORY !!chromium
+    KEYNAME "Software\\Policies\\Chromium"
+
+  END CATEGORY
+
+  CATEGORY !!chromium_recommended
+    KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WINXPSP2="At least "Windows 3.11"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"''')
+    self.CompareOutputs(output, expected_output)
+
   def testMainPolicy(self):
     # Tests a policy group with a single policy of type 'main'.
     grd = self.PrepareTest('''
@@ -250,6 +286,7 @@
 
       PART !!StringPolicy_Part  EDITTEXT
         VALUENAME "StringPolicy"
+        MAXLEN 1000000
       END PART
     END POLICY
 
@@ -266,6 +303,7 @@
 
       PART !!StringPolicy_Part  EDITTEXT
         VALUENAME "StringPolicy"
+        MAXLEN 1000000
       END PART
     END POLICY
 
@@ -734,6 +772,7 @@
 
       PART !!DictionaryPolicy_Part  EDITTEXT
         VALUENAME "DictionaryPolicy"
+        MAXLEN 1000000
       END PART
     END POLICY
 
@@ -750,6 +789,7 @@
 
       PART !!DictionaryPolicy_Part  EDITTEXT
         VALUENAME "DictionaryPolicy"
+        MAXLEN 1000000
       END PART
     END POLICY
 
@@ -942,6 +982,7 @@
 
         PART !!Policy2_Part  EDITTEXT
           VALUENAME "Policy2"
+          MAXLEN 1000000
         END PART
       END POLICY
 
@@ -984,5 +1025,103 @@
 ''')
     self.CompareOutputs(output, expected_output)
 
+  def testDuplicatedStringEnumPolicy(self):
+    # Verifies that duplicated enum constants get merged, and that
+    # string constants get escaped.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'EnumPolicy.A',
+            'type': 'string-enum',
+            'caption': 'Caption of policy A.',
+            'desc': 'Description of policy A.',
+            'items': [
+              {'name': 'tls1.2', 'value': 'tls1.2', 'caption': 'tls1.2' },
+            ],
+            'supported_on': ['chrome.win:39-'],
+          },
+          {
+            'name': 'EnumPolicy.B',
+            'type': 'string-enum',
+            'caption': 'Caption of policy B.',
+            'desc': 'Description of policy B.',
+            'items': [
+              {'name': 'tls1.2', 'value': 'tls1.2', 'caption': 'tls1.2' },
+            ],
+            'supported_on': ['chrome.win:39-'],
+          },
+        ],
+        'placeholders': [],
+        'messages': {
+          'win_supported_winxpsp2': {
+            'text': 'At least Windows 3.14', 'desc': 'blah'
+          },
+          'doc_recommended': {
+            'text': 'Recommended', 'desc': 'bleh'
+          }
+        }
+      }''')
+    output = self.GetOutput(grd, 'fr', {'_google_chrome': '1'}, 'adm', 'en')
+    expected_output = self.ConstructOutput(
+        ['MACHINE', 'USER'], '''
+  CATEGORY !!google
+    CATEGORY !!googlechrome
+      KEYNAME "Software\\Policies\\Google\\Chrome"
+
+      POLICY !!EnumPolicy_A_Policy
+        #if version >= 4
+          SUPPORTED !!SUPPORTED_WINXPSP2
+        #endif
+        EXPLAIN !!EnumPolicy_A_Explain
+
+        PART !!EnumPolicy_A_Part  DROPDOWNLIST
+          VALUENAME "EnumPolicy.A"
+          ITEMLIST
+            NAME !!tls1_2_DropDown VALUE "tls1.2"
+          END ITEMLIST
+        END PART
+      END POLICY
+
+      POLICY !!EnumPolicy_B_Policy
+        #if version >= 4
+          SUPPORTED !!SUPPORTED_WINXPSP2
+        #endif
+        EXPLAIN !!EnumPolicy_B_Explain
+
+        PART !!EnumPolicy_B_Part  DROPDOWNLIST
+          VALUENAME "EnumPolicy.B"
+          ITEMLIST
+            NAME !!tls1_2_DropDown VALUE "tls1.2"
+          END ITEMLIST
+        END PART
+      END POLICY
+
+    END CATEGORY
+  END CATEGORY
+
+  CATEGORY !!google
+    CATEGORY !!googlechrome_recommended
+      KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+    END CATEGORY
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WINXPSP2="At least Windows 3.14"
+google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+EnumPolicy_A_Policy="Caption of policy A."
+EnumPolicy_A_Explain="Description of policy A."
+EnumPolicy_A_Part="Caption of policy A."
+tls1_2_DropDown="tls1.2"
+EnumPolicy_B_Policy="Caption of policy B."
+EnumPolicy_B_Explain="Description of policy B."
+EnumPolicy_B_Part="Caption of policy B."
+''')
+    self.CompareOutputs(output, expected_output)
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/grit/format/policy_templates/writers/adml_writer.py b/grit/format/policy_templates/writers/adml_writer.py
index c525602..64f8562 100644
--- a/grit/format/policy_templates/writers/adml_writer.py
+++ b/grit/format/policy_templates/writers/adml_writer.py
@@ -30,19 +30,24 @@
   # describe the presentation of Policy-Groups and Policies.
   _presentation_table_elem = None
 
-  def _AddString(self, parent, id, text):
-    ''' Adds an ADML "string" element to the passed parent. The following
+  def _AddString(self, id, text):
+    ''' Adds an ADML "string" element to _string_table_elem. The following
     ADML snippet contains an example:
 
     <string id="$(id)">$(text)</string>
 
     Args:
-      parent: Parent element to which the new "string" element is added.
       id: ID of the newly created "string" element.
       text: Value of the newly created "string" element.
     '''
-    string_elem = self.AddElement(parent, 'string', {'id': id})
-    string_elem.appendChild(self._doc.createTextNode(text))
+    id = id.replace('.', '_')
+    if id in self.strings_seen:
+      assert text == self.strings_seen[id]
+    else:
+      self.strings_seen[id] = text
+      string_elem = self.AddElement(
+          self._string_table_elem, 'string', {'id': id})
+      string_elem.appendChild(self._doc.createTextNode(text))
 
   def WritePolicy(self, policy):
     '''Generates the ADML elements for a Policy.
@@ -75,9 +80,8 @@
     else:
       policy_label = policy_name
 
-    self._AddString(self._string_table_elem, policy_name, policy_caption)
-    self._AddString(self._string_table_elem, policy_name + '_Explain',
-                    policy_description)
+    self._AddString(policy_name, policy_caption)
+    self._AddString(policy_name + '_Explain', policy_description)
     presentation_elem = self.AddElement(
         self._presentation_table_elem, 'presentation', {'id': policy_name})
 
@@ -95,14 +99,12 @@
       textbox_elem.appendChild(self._doc.createTextNode(policy_label + ':'))
     elif policy_type in ('int-enum', 'string-enum'):
       for item in policy['items']:
-        self._AddString(self._string_table_elem, item['name'], item['caption'])
+        self._AddString(item['name'], item['caption'])
       dropdownlist_elem = self.AddElement(presentation_elem, 'dropdownList',
                                           {'refId': policy_name})
       dropdownlist_elem.appendChild(self._doc.createTextNode(policy_label))
     elif policy_type in ('list', 'string-enum-list'):
-      self._AddString(self._string_table_elem,
-                      policy_name + 'Desc',
-                      policy_caption)
+      self._AddString(policy_name + 'Desc', policy_caption)
       listbox_elem = self.AddElement(presentation_elem, 'listBox',
                                      {'refId': policy_name + 'Desc'})
       listbox_elem.appendChild(self._doc.createTextNode(policy_label))
@@ -128,39 +130,36 @@
     '''
     # Add ADML "string" elements to the string-table that are required by a
     # Policy-Group.
-    self._AddString(self._string_table_elem, group['name'] + '_group',
-                    group['caption'])
+    self._AddString(group['name'] + '_group', group['caption'])
 
-  def _AddBaseStrings(self, string_table_elem, build):
+  def _AddBaseStrings(self, build):
     ''' Adds ADML "string" elements to the string-table that are referenced by
     the ADMX file but not related to any specific Policy-Group or Policy.
     '''
-    self._AddString(string_table_elem, self.config['win_supported_os'],
+    self._AddString(self.config['win_supported_os'],
                     self.messages['win_supported_winxpsp2']['text'])
     recommended_name = '%s - %s' % \
         (self.config['app_name'], self.messages['doc_recommended']['text'])
     if build == 'chrome':
-      self._AddString(string_table_elem,
-                      self.config['win_mandatory_category_path'][0],
+      self._AddString(self.config['win_mandatory_category_path'][0],
                       'Google')
-      self._AddString(string_table_elem,
-                      self.config['win_mandatory_category_path'][1],
+      self._AddString(self.config['win_mandatory_category_path'][1],
                       self.config['app_name'])
-      self._AddString(string_table_elem,
-                      self.config['win_recommended_category_path'][1],
+      self._AddString(self.config['win_recommended_category_path'][1],
                       recommended_name)
     elif build == 'chromium':
-      self._AddString(string_table_elem,
-                      self.config['win_mandatory_category_path'][0],
+      self._AddString(self.config['win_mandatory_category_path'][0],
                       self.config['app_name'])
-      self._AddString(string_table_elem,
-                      self.config['win_recommended_category_path'][0],
+      self._AddString(self.config['win_recommended_category_path'][0],
                       recommended_name)
 
   def BeginTemplate(self):
     dom_impl = minidom.getDOMImplementation('')
     self._doc = dom_impl.createDocument(None, 'policyDefinitionResources',
                                         None)
+    if self._GetChromiumVersionString() is not None:
+      self.AddComment(self._doc.documentElement, self.config['build'] + \
+          ' version: ' + self._GetChromiumVersionString())
     policy_definitions_resources_elem = self._doc.documentElement
     policy_definitions_resources_elem.attributes['revision'] = '1.0'
     policy_definitions_resources_elem.attributes['schemaVersion'] = '1.0'
@@ -170,10 +169,14 @@
     resources_elem = self.AddElement(policy_definitions_resources_elem,
                                      'resources')
     self._string_table_elem = self.AddElement(resources_elem, 'stringTable')
-    self._AddBaseStrings(self._string_table_elem, self.config['build'])
+    self._AddBaseStrings(self.config['build'])
     self._presentation_table_elem = self.AddElement(resources_elem,
                                                    'presentationTable')
 
+  def Init(self):
+    # Map of all strings seen.
+    self.strings_seen = {}
+
   def GetTemplateText(self):
     # Using "toprettyxml()" confuses the Windows Group Policy Editor
     # (gpedit.msc) because it interprets whitespace characters in text between
diff --git a/grit/format/policy_templates/writers/adml_writer_unittest.py b/grit/format/policy_templates/writers/adml_writer_unittest.py
index 8a8f4f7..08bec58 100644
--- a/grit/format/policy_templates/writers/adml_writer_unittest.py
+++ b/grit/format/policy_templates/writers/adml_writer_unittest.py
@@ -77,6 +77,20 @@
         '</resources></policyDefinitionResources>')
     self.AssertXMLEquals(output, expected_output)
 
+  def testVersionAnnotation(self):
+    self.writer.config['version'] = '39.0.0.0'
+    self.writer.BeginTemplate()
+    self.writer.EndTemplate()
+    output = self.writer.GetTemplateText()
+    expected_output = (
+        '<?xml version="1.0" ?><policyDefinitionResources'
+        ' revision="1.0" schemaVersion="1.0"><!--test version: 39.0.0.0-->'
+        '<displayName/><description/><resources><stringTable>'
+        '<string id="SUPPORTED_TESTOS">Supported on'
+        ' Test OS or higher</string></stringTable><presentationTable/>'
+        '</resources></policyDefinitionResources>')
+    self.AssertXMLEquals(output, expected_output)
+
   def testPolicyGroup(self):
     empty_policy_group = {
       'name': 'PolicyGroup',
@@ -362,6 +376,62 @@
       ]
     }))
 
+  def testStringEncodings(self):
+    enum_policy_a = {
+      'name': 'EnumPolicy.A',
+      'type': 'string-enum',
+      'caption': 'Enum policy A caption',
+      'label': 'Enum policy A label',
+      'desc': 'This is a test description.',
+      'items': [
+          {
+           'name': 'tls1.2',
+           'value': 'tls1.2',
+           'caption': 'tls1.2',
+          }
+      ],
+    }
+    enum_policy_b = {
+      'name': 'EnumPolicy.B',
+      'type': 'string-enum',
+      'caption': 'Enum policy B caption',
+      'label': 'Enum policy B label',
+      'desc': 'This is a test description.',
+      'items': [
+          {
+           'name': 'tls1.2',
+           'value': 'tls1.2',
+           'caption': 'tls1.2',
+          }
+      ],
+    }
+    self. _InitWriterForAddingPolicies(self.writer, enum_policy_a)
+    self.writer.WritePolicy(enum_policy_a)
+    self.writer.WritePolicy(enum_policy_b)
+    # Assert generated string elements.
+    output = self.GetXMLOfChildren(self.writer._string_table_elem)
+    expected_output = (
+        '<string id="EnumPolicy_A">Enum policy A caption</string>\n'
+        '<string id="EnumPolicy_A_Explain">'
+        'This is a test description.</string>\n'
+        '<string id="tls1_2">tls1.2</string>\n'
+        '<string id="EnumPolicy_B">Enum policy B caption</string>\n'
+        '<string id="EnumPolicy_B_Explain">'
+        'This is a test description.</string>\n')
+    self.AssertXMLEquals(output, expected_output)
+    # Assert generated presentation elements.
+    output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+    expected_output = (
+        '<presentation id="EnumPolicy.A">\n'
+        '  <dropdownList refId="EnumPolicy.A">'
+        'Enum policy A label</dropdownList>\n'
+        '</presentation>\n'
+        '<presentation id="EnumPolicy.B">\n'
+        '  <dropdownList refId="EnumPolicy.B">'
+        'Enum policy B label</dropdownList>\n'
+        '</presentation>')
+    self.AssertXMLEquals(output, expected_output)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/grit/format/policy_templates/writers/admx_writer.py b/grit/format/policy_templates/writers/admx_writer.py
index 0d4394b..d657834 100644
--- a/grit/format/policy_templates/writers/admx_writer.py
+++ b/grit/format/policy_templates/writers/admx_writer.py
@@ -32,6 +32,7 @@
     Args:
       name: Name of the referenced ADML string.
     '''
+    name = name.replace('.', '_')
     return '$(string.' + name + ')'
 
   def _AdmlStringExplain(self, name):
@@ -39,6 +40,7 @@
     Args:
       name: Name of the referenced ADML explanation.
     '''
+    name = name.replace('.', '_')
     return '$(string.' + name + '_Explain)'
 
   def _AdmlPresentation(self, name):
@@ -166,6 +168,7 @@
     attributes = {
       'id': name,
       'valueName': name,
+      'maxLength': '1000000',
     }
     self.AddElement(parent, 'text', attributes)
 
@@ -349,6 +352,9 @@
     '''
     dom_impl = minidom.getDOMImplementation('')
     self._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
+    if self._GetChromiumVersionString() is not None:
+      self.AddComment(self._doc.documentElement, self.config['build'] + \
+          ' version: ' + self._GetChromiumVersionString())
     policy_definitions_elem = self._doc.documentElement
 
     policy_definitions_elem.attributes['revision'] = '1.0'
diff --git a/grit/format/policy_templates/writers/admx_writer_unittest.py b/grit/format/policy_templates/writers/admx_writer_unittest.py
index cb3d39e..8bdb7a4 100644
--- a/grit/format/policy_templates/writers/admx_writer_unittest.py
+++ b/grit/format/policy_templates/writers/admx_writer_unittest.py
@@ -37,7 +37,8 @@
       'win_mandatory_category_path': ['test_category'],
       'win_recommended_category_path': ['test_recommended_category'],
       'admx_namespace': 'ADMXWriter.Test.Namespace',
-      'admx_prefix': 'test_prefix'
+      'admx_prefix': 'test_prefix',
+      'build': 'test_product',
     }
     self.writer = admx_writer.GetWriter(config)
     self.writer.Init()
@@ -82,6 +83,38 @@
         '</policyDefinitions>')
     self.AssertXMLEquals(output, expected_output)
 
+  def testEmptyVersion(self):
+    self.writer.config['version'] = '39.0.0.0'
+    self.writer.BeginTemplate()
+    self.writer.EndTemplate()
+
+    output = self.writer.GetTemplateText()
+    expected_output = (
+        '<?xml version="1.0" ?>\n'
+        '<policyDefinitions revision="1.0" schemaVersion="1.0">\n'
+        '  <!--test_product version: 39.0.0.0-->\n'
+        '  <policyNamespaces>\n'
+        '    <target namespace="ADMXWriter.Test.Namespace"'
+        ' prefix="test_prefix"/>\n'
+        '    <using namespace="Microsoft.Policies.Windows" prefix="windows"/>\n'
+        '  </policyNamespaces>\n'
+        '  <resources minRequiredRevision="1.0"/>\n'
+        '  <supportedOn>\n'
+        '    <definitions>\n'
+        '      <definition displayName="'
+        '$(string.SUPPORTED_TESTOS)" name="SUPPORTED_TESTOS"/>\n'
+        '    </definitions>\n'
+        '  </supportedOn>\n'
+        '  <categories>\n'
+        '    <category displayName="$(string.test_category)"'
+        ' name="test_category"/>\n'
+        '    <category displayName="$(string.test_recommended_category)"'
+        ' name="test_recommended_category"/>\n'
+        '  </categories>\n'
+        '  <policies/>\n'
+        '</policyDefinitions>')
+    self.AssertXMLEquals(output, expected_output)
+
   def testEmptyPolicyGroup(self):
     empty_policy_group = {
       'name': 'PolicyGroup',
@@ -276,7 +309,8 @@
         '  <parentCategory ref="PolicyGroup"/>\n'
         '  <supportedOn ref="SUPPORTED_TESTOS"/>\n'
         '  <elements>\n'
-        '    <text id="SampleStringPolicy" valueName="SampleStringPolicy"/>\n'
+        '    <text id="SampleStringPolicy" maxLength="1000000"'
+            ' valueName="SampleStringPolicy"/>\n'
         '  </elements>\n'
         '</policy>')
     self.AssertXMLEquals(output, expected_output)
@@ -454,8 +488,8 @@
         '  <parentCategory ref="PolicyGroup"/>\n'
         '  <supportedOn ref="SUPPORTED_TESTOS"/>\n'
         '  <elements>\n'
-        '    <text id="SampleDictionaryPolicy" '
-            'valueName="SampleDictionaryPolicy"/>\n'
+        '    <text id="SampleDictionaryPolicy" maxLength="1000000"'
+            ' valueName="SampleDictionaryPolicy"/>\n'
         '  </elements>\n'
         '</policy>')
     self.AssertXMLEquals(output, expected_output)
@@ -473,6 +507,67 @@
       ]
     }))
 
+  def testStringEncodings(self):
+    enum_policy_a = {
+      'name': 'SampleEnumPolicy.A',
+      'type': 'string-enum',
+        'items': [
+          {'name': 'tls1.2', 'value': 'tls1.2'}
+        ]
+    }
+    enum_policy_b = {
+      'name': 'SampleEnumPolicy.B',
+      'type': 'string-enum',
+        'items': [
+          {'name': 'tls1.2', 'value': 'tls1.2'}
+        ]
+    }
+
+    dom_impl = minidom.getDOMImplementation('')
+    self.writer._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
+    self.writer._active_policies_elem = self.writer._doc.documentElement
+    self.writer._active_mandatory_policy_group_name = 'PolicyGroup'
+    self.writer.WritePolicy(enum_policy_a)
+    self.writer.WritePolicy(enum_policy_b)
+    output = self.writer.GetTemplateText()
+    expected_output = (
+        '<?xml version="1.0" ?>\n'
+        '<policyDefinitions>\n'
+        '  <policy class="TestClass" displayName="$(string.SampleEnumPolicy_A)"'
+          ' explainText="$(string.SampleEnumPolicy_A_Explain)"'
+          ' key="Software\\Policies\\Test" name="SampleEnumPolicy.A"'
+          ' presentation="$(presentation.SampleEnumPolicy.A)">\n'
+        '    <parentCategory ref="PolicyGroup"/>\n'
+        '    <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+        '    <elements>\n'
+        '      <enum id="SampleEnumPolicy.A" valueName="SampleEnumPolicy.A">\n'
+        '        <item displayName="$(string.tls1_2)">\n'
+        '          <value>\n'
+        '            <string>tls1.2</string>\n'
+        '          </value>\n'
+        '        </item>\n'
+        '      </enum>\n'
+        '    </elements>\n'
+        '  </policy>\n'
+        '  <policy class="TestClass" displayName="$(string.SampleEnumPolicy_B)"'
+          ' explainText="$(string.SampleEnumPolicy_B_Explain)"'
+          ' key="Software\\Policies\\Test" name="SampleEnumPolicy.B"'
+          ' presentation="$(presentation.SampleEnumPolicy.B)">\n'
+        '    <parentCategory ref="PolicyGroup"/>\n'
+        '    <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+        '    <elements>\n'
+        '      <enum id="SampleEnumPolicy.B" valueName="SampleEnumPolicy.B">\n'
+        '        <item displayName="$(string.tls1_2)">\n'
+        '          <value>\n'
+        '            <string>tls1.2</string>\n'
+        '          </value>\n'
+        '        </item>\n'
+        '      </enum>\n'
+        '    </elements>\n'
+        '  </policy>\n'
+        '</policyDefinitions>')
+    self.AssertXMLEquals(output, expected_output)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/grit/format/policy_templates/writers/doc_writer.py b/grit/format/policy_templates/writers/doc_writer.py
index d8f108d..e90e16c 100644
--- a/grit/format/policy_templates/writers/doc_writer.py
+++ b/grit/format/policy_templates/writers/doc_writer.py
@@ -590,6 +590,10 @@
 
   def BeginTemplate(self):
     # Add a <div> for the summary section.
+    if self._GetChromiumVersionString() is not None:
+      self.AddComment(self._main_div, self.config['build'] + \
+          ' version: ' + self._GetChromiumVersionString())
+
     summary_div = self.AddElement(self._main_div, 'div')
     self.AddElement(summary_div, 'a', {'name': 'top'})
     self.AddElement(summary_div, 'br')
diff --git a/grit/format/policy_templates/writers/doc_writer_unittest.py b/grit/format/policy_templates/writers/doc_writer_unittest.py
index 15d8b85..db03808 100644
--- a/grit/format/policy_templates/writers/doc_writer_unittest.py
+++ b/grit/format/policy_templates/writers/doc_writer_unittest.py
@@ -39,6 +39,7 @@
         'os_name': 'Chrome OS',
         'win_reg_mandatory_key_name': 'MockKey',
         'win_reg_recommended_key_name': 'MockKeyRec',
+        'build': 'test_product',
       })
     self.writer.messages = {
       'doc_back_to_top': {'text': '_test_back_to_top'},
@@ -103,6 +104,31 @@
           '<div/>'
         '</div>')
 
+  def testVersionAnnotation(self):
+    # Test if DocWriter creates the skeleton of the document correctly.
+    self.writer.config['version'] = '39.0.0.0'
+    self.writer.BeginTemplate()
+    self.assertEquals(
+        self.writer._main_div.toxml(),
+        '<div>'
+          '<!--test_product version: 39.0.0.0-->'
+          '<div>'
+            '<a name="top"/><br/>_test_intro<br/><br/><br/>'
+            '<table style="style_table;">'
+              '<thead><tr style="style_tr;">'
+                '<td style="style_td;style_td.left;style_thead td;">'
+                  '_test_name_column_title'
+                '</td>'
+                '<td style="style_td;style_td.right;style_thead td;">'
+                  '_test_description_column_title'
+                '</td>'
+              '</tr></thead>'
+              '<tbody/>'
+            '</table>'
+          '</div>'
+          '<div/>'
+        '</div>')
+
   def testGetLocalizedMessage(self):
     # Test if localized messages are retrieved correctly.
     self.writer.messages = {
diff --git a/grit/format/policy_templates/writers/ios_plist_writer.py b/grit/format/policy_templates/writers/ios_plist_writer.py
index 1da64aa..b726ffa 100644
--- a/grit/format/policy_templates/writers/ios_plist_writer.py
+++ b/grit/format/policy_templates/writers/ios_plist_writer.py
@@ -97,6 +97,9 @@
     self._plist.attributes['version'] = '1.0'
     self._root_dict = self.AddElement(self._plist, 'dict')
     self.AddComment(self._root_dict, CHROME_POLICY_COMMENT)
+    if self._GetChromiumVersionString() is not None:
+      self.AddComment(self._root_dict, ' ' + self.config['build'] + \
+          ' version: ' + self._GetChromiumVersionString() + ' ')
     self._dict = self._AddKeyValuePair(self._root_dict, 'ChromePolicy', 'dict')
 
     self._encoded_plist.attributes['version'] = '1.0'
diff --git a/grit/format/policy_templates/writers/ios_plist_writer_unittest.py b/grit/format/policy_templates/writers/ios_plist_writer_unittest.py
index 4743e4e..0fdecb1 100644
--- a/grit/format/policy_templates/writers/ios_plist_writer_unittest.py
+++ b/grit/format/policy_templates/writers/ios_plist_writer_unittest.py
@@ -55,11 +55,17 @@
                                         expected_output,
                                         parse,
                                         decode_and_parse):
+
+
+    _defines = { '_chromium': '1',
+                 'mac_bundle_id': 'com.example.Test',
+                 'version': '39.0.0.0' }
+
     # Generate the grit output for |templates|.
     output = self.GetOutput(
         self.PrepareTest(templates),
         'fr',
-        { '_chromium': '1', 'mac_bundle_id': 'com.example.Test' },
+        _defines,
         'ios_plist',
         'en')
 
@@ -124,6 +130,17 @@
     expected = {}
     self._VerifyGeneratedOutput(templates, expected)
 
+  def testEmptyVersion(self):
+    templates = '''
+    {
+      'policy_definitions': [],
+      'placeholders': [],
+      'messages': {},
+    }
+    '''
+    expected = {}
+    self._VerifyGeneratedOutput(templates, expected)
+
   def testBoolean(self):
     templates = self._MakeTemplate('BooleanPolicy', 'main', 'True')
     expected = {
diff --git a/grit/format/policy_templates/writers/json_writer.py b/grit/format/policy_templates/writers/json_writer.py
index f5af8c1..4dfd282 100644
--- a/grit/format/policy_templates/writers/json_writer.py
+++ b/grit/format/policy_templates/writers/json_writer.py
@@ -39,6 +39,9 @@
   def PreprocessPolicies(self, policy_list):
     return self.FlattenGroupsAndSortPolicies(policy_list)
 
+  def WriteComment(self, comment):
+    self._out.append('// ' + comment)
+
   def WritePolicy(self, policy):
     if policy['type'] == 'external':
       # This type can only be set through cloud policy.
@@ -69,6 +72,9 @@
     self._first_written = False
 
   def BeginTemplate(self):
+    if self._GetChromiumVersionString() is not None:
+      self.WriteComment(self.config['build'] + ''' version: ''' + \
+          self._GetChromiumVersionString())
     self._out.append(TEMPLATE_HEADER)
 
   def EndTemplate(self):
diff --git a/grit/format/policy_templates/writers/json_writer_unittest.py b/grit/format/policy_templates/writers/json_writer_unittest.py
index b2ed1ef..8f3c745 100644
--- a/grit/format/policy_templates/writers/json_writer_unittest.py
+++ b/grit/format/policy_templates/writers/json_writer_unittest.py
@@ -24,6 +24,15 @@
 {
 """
 
+TEMPLATE_HEADER_WITH_VERSION="""\
+// chromium version: 39.0.0.0
+// Policy template for Linux.
+// Uncomment the policies you wish to activate and change their values to
+// something useful for your case. The provided values are for reference only
+// and do not provide meaningful defaults!
+{
+"""
+
 
 HEADER_DELIMETER="""\
   //-------------------------------------------------------------------------
@@ -55,10 +64,23 @@
         '  "placeholders": [],'
         '  "messages": {},'
         '}')
-    output = self.GetOutput(grd, 'fr', {'_chromium': '1',}, 'json', 'en')
+    output = self.GetOutput(grd, 'fr', {'_chromium': '1'}, 'json', 'en')
     expected_output = TEMPLATE_HEADER + '}'
     self.CompareOutputs(output, expected_output)
 
+  def testEmptyWithVersion(self):
+    # Test the handling of an empty policy list.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": [],'
+        '  "placeholders": [],'
+        '  "messages": {},'
+        '}')
+    output = self.GetOutput(
+        grd, 'fr', {'_chromium': '1', 'version':'39.0.0.0'}, 'json', 'en')
+    expected_output = TEMPLATE_HEADER_WITH_VERSION + '}'
+    self.CompareOutputs(output, expected_output)
+
   def testMainPolicy(self):
     # Tests a policy group with a single policy of type 'main'.
     grd = self.PrepareTest(
diff --git a/grit/format/policy_templates/writers/plist_strings_writer.py b/grit/format/policy_templates/writers/plist_strings_writer.py
index 966aaf2..4257bf8 100644
--- a/grit/format/policy_templates/writers/plist_strings_writer.py
+++ b/grit/format/policy_templates/writers/plist_strings_writer.py
@@ -22,6 +22,9 @@
   [lang].lproj subdirectories of the manifest bundle.
   '''
 
+  def WriteComment(self, comment):
+    self._out.append('/* ' + comment + ' */' )
+
   def _AddToStringTable(self, item_name, caption, desc):
     '''Add a title and a description of an item to the string table.
 
@@ -63,6 +66,9 @@
 
   def BeginTemplate(self):
     app_name = plist_helper.GetPlistFriendlyName(self.config['app_name'])
+    if self._GetChromiumVersionString() is not None:
+      self.WriteComment(self.config['build'] + ''' version: ''' + \
+          self._GetChromiumVersionString())
     self._AddToStringTable(
         app_name,
         self.config['app_name'],
diff --git a/grit/format/policy_templates/writers/plist_strings_writer_unittest.py b/grit/format/policy_templates/writers/plist_strings_writer_unittest.py
index 17fc85c..efad6f2 100644
--- a/grit/format/policy_templates/writers/plist_strings_writer_unittest.py
+++ b/grit/format/policy_templates/writers/plist_strings_writer_unittest.py
@@ -43,6 +43,33 @@
         'Chromium.pfm_description = "Chromium preferen\\"ces";')
     self.assertEquals(output.strip(), expected_output.strip())
 
+  def testEmptyVersion(self):
+    # Test PListStringsWriter in case of empty polices.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [],
+        'placeholders': [],
+        'messages': {
+          'mac_chrome_preferences': {
+            'text': '$1 preferen"ces',
+            'desc': 'blah'
+          }
+        }
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium': '1',
+         'mac_bundle_id': 'com.example.Test',
+         'version': '39.0.0.0'},
+        'plist_strings',
+        'en')
+    expected_output = (
+        '/* chromium version: 39.0.0.0 */\n'
+        'Chromium.pfm_title = "Chromium";\n'
+        'Chromium.pfm_description = "Chromium preferen\\"ces";')
+    self.assertEquals(output.strip(), expected_output.strip())
+
   def testMainPolicy(self):
     # Tests a policy group with a single policy of type 'main'.
     grd = self.PrepareTest('''
diff --git a/grit/format/policy_templates/writers/plist_writer.py b/grit/format/policy_templates/writers/plist_writer.py
index 6d929d6..cae6844 100644
--- a/grit/format/policy_templates/writers/plist_writer.py
+++ b/grit/format/policy_templates/writers/plist_writer.py
@@ -120,7 +120,9 @@
   def BeginTemplate(self):
     self._plist.attributes['version'] = '1'
     dict = self.AddElement(self._plist, 'dict')
-
+    if self._GetChromiumVersionString() is not None:
+      self.AddComment(self._plist, self.config['build'] + ' version: ' + \
+          self._GetChromiumVersionString())
     app_name = plist_helper.GetPlistFriendlyName(self.config['app_name'])
     self._AddStringKeyValuePair(dict, 'pfm_name', app_name)
     self._AddStringKeyValuePair(dict, 'pfm_description', '')
diff --git a/grit/format/policy_templates/writers/plist_writer_unittest.py b/grit/format/policy_templates/writers/plist_writer_unittest.py
index ddbdbfe..6186c15 100644
--- a/grit/format/policy_templates/writers/plist_writer_unittest.py
+++ b/grit/format/policy_templates/writers/plist_writer_unittest.py
@@ -52,6 +52,41 @@
   </dict>
 </plist>''' % (product_name, bundle_id, policies)
 
+  def _GetExpectedOutputsWithVersion(self, product_name, bundle_id, policies,
+                                     version):
+    '''Substitutes the variable parts into a plist template. The result
+    of this function can be used as an expected result to test the output
+    of PListWriter.
+
+    Args:
+      product_name: The name of the product, normally Chromium or Google Chrome.
+      bundle_id: The mac bundle id of the product.
+      policies: The list of policies.
+
+    Returns:
+      The text of a plist template with the variable parts substituted.
+    '''
+    return '''
+<?xml version="1.0" ?>
+<!DOCTYPE plist  PUBLIC '-//Apple//DTD PLIST 1.0//EN'  'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
+<plist version="1">
+  <dict>
+    <key>pfm_name</key>
+    <string>%s</string>
+    <key>pfm_description</key>
+    <string/>
+    <key>pfm_title</key>
+    <string/>
+    <key>pfm_version</key>
+    <string>1</string>
+    <key>pfm_domain</key>
+    <string>%s</string>
+    <key>pfm_subkeys</key>
+    %s
+  </dict>
+  <!--%s-->
+</plist>''' % (product_name, bundle_id, policies, version)
+
   def testEmpty(self):
     # Test PListWriter in case of empty polices.
     grd = self.PrepareTest('''
@@ -71,6 +106,30 @@
         'Chromium', 'com.example.Test', '<array/>')
     self.assertEquals(output.strip(), expected_output.strip())
 
+  def testEmptyVersion(self):
+    # Test PListWriter in case of empty polices.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [],
+        'placeholders': [],
+        'messages': {},
+      }''')
+
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium': '1',
+         'mac_bundle_id': 'com.example.Test',
+         'version': '39.0.0.0'},
+        'plist',
+        'en')
+    expected_output = self._GetExpectedOutputsWithVersion(
+        'Chromium',
+        'com.example.Test',
+        '<array/>',
+        'chromium version: 39.0.0.0')
+    self.assertEquals(output.strip(), expected_output.strip())
+
   def testMainPolicy(self):
     # Tests a policy group with a single policy of type 'main'.
     grd = self.PrepareTest('''
diff --git a/grit/format/policy_templates/writers/reg_writer.py b/grit/format/policy_templates/writers/reg_writer.py
index ad83046..70c87a3 100644
--- a/grit/format/policy_templates/writers/reg_writer.py
+++ b/grit/format/policy_templates/writers/reg_writer.py
@@ -82,6 +82,9 @@
 
       list.append('"%s"=%s' % (policy['name'], example_value_str))
 
+  def WriteComment(self, comment):
+    self._prefix.append('; ' + comment)
+
   def WritePolicy(self, policy):
     if self.CanBeMandatory(policy):
       self._WritePolicy(policy,
@@ -103,8 +106,12 @@
     self._mandatory = []
     self._recommended = []
     self._last_key = {}
+    self._prefix = []
 
   def GetTemplateText(self):
-    prefix = ['Windows Registry Editor Version 5.00']
-    all = prefix + self._mandatory + self._recommended
+    self._prefix.append('Windows Registry Editor Version 5.00')
+    if self._GetChromiumVersionString() is not None:
+      self.WriteComment(self.config['build'] + ' version: ' + \
+          self._GetChromiumVersionString())
+    all = self._prefix + self._mandatory + self._recommended
     return self.NEWLINE.join(all)
diff --git a/grit/format/policy_templates/writers/reg_writer_unittest.py b/grit/format/policy_templates/writers/reg_writer_unittest.py
index 88fb2e2..2851a8b 100644
--- a/grit/format/policy_templates/writers/reg_writer_unittest.py
+++ b/grit/format/policy_templates/writers/reg_writer_unittest.py
@@ -48,6 +48,20 @@
     expected_output = 'Windows Registry Editor Version 5.00'
     self.CompareOutputs(output, expected_output)
 
+  def testEmptyVersion(self):
+    # Test the handling of an empty policy list.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": [],'
+        '  "placeholders": [],'
+        '  "messages": {}'
+        '}')
+    output = self.GetOutput(
+        grd, 'fr', {'_chromium': '1', 'version': '39.0.0.0' }, 'reg', 'en')
+    expected_output = ('Windows Registry Editor Version 5.00\r\n'
+                       '; chromium version: 39.0.0.0\r\n')
+    self.CompareOutputs(output, expected_output)
+
   def testMainPolicy(self):
     # Tests a policy group with a single policy of type 'main'.
     grd = self.PrepareTest(
diff --git a/grit/format/policy_templates/writers/template_writer.py b/grit/format/policy_templates/writers/template_writer.py
index bd48425..d489d64 100644
--- a/grit/format/policy_templates/writers/template_writer.py
+++ b/grit/format/policy_templates/writers/template_writer.py
@@ -102,6 +102,15 @@
     is_supported = lambda x: platform in x['platforms']
     return any(filter(is_supported, policy['supported_on']))
 
+  def _GetChromiumVersionString(self):
+    '''Returns the Chromium version string stored in the environment variable
+    version (if it is set).
+
+    Returns: The Chromium version string or None if it has not been set.'''
+
+    if 'version' in self.config:
+      return self.config['version']
+
   def _GetPoliciesForWriter(self, group):
     '''Filters the list of policies in the passed group that are supported by
     the writer.
@@ -191,6 +200,13 @@
     '''
     raise NotImplementedError()
 
+  def WriteComment(self, comment):
+    '''Appends the comment to the internal buffer.
+
+      comment: The comment to be added.
+    '''
+    raise NotImplementedError()
+
   def WriteRecommendedPolicy(self, policy):
     '''Appends the template text corresponding to a recommended policy into the
     internal buffer.
diff --git a/grit/format/policy_templates/writers/xml_formatted_writer.py b/grit/format/policy_templates/writers/xml_formatted_writer.py
index dad3717..5917fb4 100644
--- a/grit/format/policy_templates/writers/xml_formatted_writer.py
+++ b/grit/format/policy_templates/writers/xml_formatted_writer.py
@@ -31,7 +31,7 @@
 
     doc = parent.ownerDocument
     element = doc.createElement(name)
-    for key, value in attrs.iteritems():
+    for key, value in sorted(attrs.iteritems()):
       element.setAttribute(key, value)
     if text:
       element.appendChild(doc.createTextNode(text))
diff --git a/grit/node/misc.py b/grit/node/misc.py
index 9a23263..64c4690 100755
--- a/grit/node/misc.py
+++ b/grit/node/misc.py
@@ -292,6 +292,11 @@
     """
     return self.attrs['base_dir']
 
+  def SetShouldOutputAllResourceDefines(self, value):
+    """Overrides the value of output_all_resource_defines found in the grd file.
+    """
+    self.attrs['output_all_resource_defines'] = 'true' if value else 'false'
+
   def ShouldOutputAllResourceDefines(self):
     """Returns true if all resource defines should be output, false if
     defines for resources not emitted to resource files should be
diff --git a/grit/testdata/whitelist.txt b/grit/testdata/whitelist.txt
new file mode 100644
index 0000000..5b3aca4
--- /dev/null
+++ b/grit/testdata/whitelist.txt
@@ -0,0 +1,4 @@
+IDS_MESSAGE_WHITELISTED
+IDR_STRUCTURE_WHITELISTED
+IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED
+IDR_INCLUDE_WHITELISTED
diff --git a/grit/testdata/whitelist_resources.grd b/grit/testdata/whitelist_resources.grd
new file mode 100644
index 0000000..9925688
--- /dev/null
+++ b/grit/testdata/whitelist_resources.grd
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0"
+      current_release="1"
+      output_all_resource_defines="false">
+  <outputs>
+    <output filename="whitelist_test_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="whitelist_test_resources_map.cc"
+            type="resource_file_map_source" />
+    <output filename="whitelist_test_resources_map.h"
+            type="resource_map_header" />
+    <output filename="whitelist_test_resources.pak" type="data_package" />
+  </outputs>
+  <translations>
+    <file path="substitute.xmb" lang="sv" />
+  </translations>
+    <release seq="1">
+    <structures>
+      <structure name="IDR_STRUCTURE_WHITELISTED" file="browser.html"
+                 type="chrome_html" >
+      </structure>
+      <structure name="IDR_STRUCTURE_NOT_WHITELISTED" file="deleted.html"
+                 type="chrome_html" >
+      </structure>
+      <if expr="True">
+        <structure name="IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED"
+                   file="details.html"
+                   type="chrome_html" >
+        </structure>
+        <structure name="IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED"
+                   file="error.html"
+                   type="chrome_html" >
+        </structure>
+      </if>
+      <if expr="False">
+        <structure name="IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED"
+                   file="status.html"
+                   type="chrome_html" >
+        </structure>
+        <structure name="IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED"
+                   file="simple.html"
+                   type="chrome_html" >
+        </structure>
+      </if>
+    </structures>
+    <includes>
+      <include name="IDR_INCLUDE_WHITELISTED" file="klonk.ico"
+               type="BINDATA" />
+      <include name="IDR_INCLUDE_NOT_WHITELISTED" file="klonk.rc"
+               type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/grit/testdata/whitelist_strings.grd b/grit/testdata/whitelist_strings.grd
new file mode 100644
index 0000000..df80f5f
--- /dev/null
+++ b/grit/testdata/whitelist_strings.grd
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0"
+      current_release="1"
+      output_all_resource_defines="false">
+  <outputs >
+    <output filename="whitelist_test_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="en_whitelist_test_strings.rc" type="rc_all" lang="en" />
+  </outputs>
+  <release seq="1">
+    <messages>
+      <message name="IDS_MESSAGE_WHITELISTED"
+               desc="A message in the whiltelist file.">
+        Whitelisted.
+      </message>
+      <message name="IDS_MESSAGE_NOT_WHITELISTED"
+               desc="A message that isn't in the whiltelist file.">
+        Not whitelisted.
+      </message>
+    </messages>
+  </release>
+</grit>
diff --git a/grit/tool/build.py b/grit/tool/build.py
index 537e2c6..c3c1aff 100644
--- a/grit/tool/build.py
+++ b/grit/tool/build.py
@@ -104,6 +104,11 @@
                     and {numeric_id}. E.g. "#define {textual_id} {numeric_id}"
                     Otherwise it will use the default "#define SYMBOL 1234"
 
+  --output-all-resource-defines
+  --no-output-all-resource-defines  If specified, overrides the value of the
+                    output_all_resource_defines attribute of the root <grit>
+                    element of the input .grd file.
+
 Conditional inclusion of resources only affects the output of files which
 control which resources get linked into a binary, e.g. it affects .rc files
 meant for compilation but it does not affect resource header files (that define
@@ -123,8 +128,11 @@
     depfile = None
     depdir = None
     rc_header_format = None
+    output_all_resource_defines = None
     (own_opts, args) = getopt.getopt(args, 'a:o:D:E:f:w:t:h:',
-        ('depdir=','depfile=','assert-file-list='))
+        ('depdir=','depfile=','assert-file-list=',
+         'output-all-resource-defines',
+         'no-output-all-resource-defines',))
     for (key, val) in own_opts:
       if key == '-a':
         assert_output_files.append(val)
@@ -146,6 +154,10 @@
         first_ids_file = val
       elif key == '-w':
         whitelist_filenames.append(val)
+      elif key == '--output-all-resource-defines':
+        output_all_resource_defines = True
+      elif key == '--no-output-all-resource-defines':
+        output_all_resource_defines = False
       elif key == '-t':
         target_platform = val
       elif key == '-h':
@@ -178,6 +190,12 @@
                                 first_ids_file=first_ids_file,
                                 defines=self.defines,
                                 target_platform=target_platform)
+
+    # If the output_all_resource_defines option is specified, override the value
+    # found in the grd file.
+    if output_all_resource_defines is not None:
+      self.res.SetShouldOutputAllResourceDefines(output_all_resource_defines)
+
     # Set an output context so that conditionals can use defines during the
     # gathering stage; we use a dummy language here since we are not outputting
     # a specific language.
@@ -224,11 +242,13 @@
     # be written into the target files (skip markers).
     from grit.node import include
     from grit.node import message
+    from grit.node import structure
     for node in start_node:
       # Same trick data_pack.py uses to see what nodes actually result in
       # real items.
       if (isinstance(node, include.IncludeNode) or
-          isinstance(node, message.MessageNode)):
+          isinstance(node, message.MessageNode) or
+          isinstance(node, structure.StructureNode)):
         text_ids = node.GetTextualIds()
         # Mark the item to be skipped if it wasn't in the whitelist.
         if text_ids and text_ids[0] not in whitelist_names:
@@ -363,12 +383,21 @@
         for i in self.res.GetOutputFiles()])
 
     if asserted != actual:
-      print '''Asserted file list does not match.
+      missing = list(set(actual) - set(asserted))
+      extra = list(set(asserted) - set(actual))
+      error = '''Asserted file list does not match.
 
-Expected output files: %s
-
-Actual output files: %s
-''' % (asserted, actual)
+Expected output files:
+%s
+Actual output files:
+%s
+Missing output files:
+%s
+Extra output files:
+%s
+'''
+      print error % ('\n'.join(asserted), '\n'.join(actual), '\n'.join(missing),
+          '\n'.join(extra))
       return False
     return True
 
diff --git a/grit/tool/build_unittest.py b/grit/tool/build_unittest.py
index debe4d4..b687bb3 100644
--- a/grit/tool/build_unittest.py
+++ b/grit/tool/build_unittest.py
@@ -6,6 +6,7 @@
 '''Unit tests for the 'grit build' tool.
 '''
 
+import codecs
 import os
 import sys
 import tempfile
@@ -85,5 +86,165 @@
             '-a', os.path.abspath(
                 os.path.join(output_dir, 'resource.h'))]))
 
+  def _verifyWhitelistedOutput(self,
+                               filename,
+                               whitelisted_ids,
+                               non_whitelisted_ids,
+                               encoding='utf8'):
+    self.failUnless(os.path.exists(filename))
+    whitelisted_ids_found = []
+    non_whitelisted_ids_found = []
+    with codecs.open(filename, encoding=encoding) as f:
+      for line in f.readlines():
+        for whitelisted_id in whitelisted_ids:
+          if whitelisted_id in line:
+            whitelisted_ids_found.append(whitelisted_id)
+        for non_whitelisted_id in non_whitelisted_ids:
+          if non_whitelisted_id in line:
+            non_whitelisted_ids_found.append(non_whitelisted_id)
+    self.longMessage = True
+    self.assertEqual(whitelisted_ids,
+                     whitelisted_ids_found,
+                     '\nin file {}'.format(os.path.basename(filename)))
+    non_whitelisted_msg = ('Non-Whitelisted IDs {} found in {}'
+        .format(non_whitelisted_ids_found, os.path.basename(filename)))
+    self.assertFalse(non_whitelisted_ids_found, non_whitelisted_msg)
+
+  def testWhitelistStrings(self):
+    output_dir = tempfile.mkdtemp()
+    builder = build.RcBuilder()
+    class DummyOpts(object):
+      def __init__(self):
+        self.input = util.PathFromRoot('grit/testdata/whitelist_strings.grd')
+        self.verbose = False
+        self.extra_verbose = False
+    whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
+    builder.Run(DummyOpts(), ['-o', output_dir,
+                              '-w', whitelist_file])
+    header = os.path.join(output_dir, 'whitelist_test_resources.h')
+    rc = os.path.join(output_dir, 'en_whitelist_test_strings.rc')
+
+    whitelisted_ids = ['IDS_MESSAGE_WHITELISTED']
+    non_whitelisted_ids = ['IDS_MESSAGE_NOT_WHITELISTED']
+    self._verifyWhitelistedOutput(
+      header,
+      whitelisted_ids,
+      non_whitelisted_ids,
+    )
+    self._verifyWhitelistedOutput(
+      rc,
+      whitelisted_ids,
+      non_whitelisted_ids,
+      encoding='utf16'
+    )
+
+  def testWhitelistResources(self):
+    output_dir = tempfile.mkdtemp()
+    builder = build.RcBuilder()
+    class DummyOpts(object):
+      def __init__(self):
+        self.input = util.PathFromRoot('grit/testdata/whitelist_resources.grd')
+        self.verbose = False
+        self.extra_verbose = False
+    whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
+    builder.Run(DummyOpts(), ['-o', output_dir,
+                              '-w', whitelist_file])
+    header = os.path.join(output_dir, 'whitelist_test_resources.h')
+    map_cc = os.path.join(output_dir, 'whitelist_test_resources_map.cc')
+    map_h = os.path.join(output_dir, 'whitelist_test_resources_map.h')
+    pak = os.path.join(output_dir, 'whitelist_test_resources.pak')
+
+    # Ensure the resource map header and .pak files exist, but don't verify
+    # their content.
+    self.failUnless(os.path.exists(map_h))
+    self.failUnless(os.path.exists(pak))
+
+    whitelisted_ids = [
+        'IDR_STRUCTURE_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED',
+        'IDR_INCLUDE_WHITELISTED',
+    ]
+    non_whitelisted_ids = [
+        'IDR_STRUCTURE_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED',
+        'IDR_INCLUDE_NOT_WHITELISTED',
+    ]
+    for output_file in (header, map_cc):
+      self._verifyWhitelistedOutput(
+        output_file,
+        whitelisted_ids,
+        non_whitelisted_ids,
+      )
+
+  def testOutputAllResourceDefinesTrue(self):
+    output_dir = tempfile.mkdtemp()
+    builder = build.RcBuilder()
+    class DummyOpts(object):
+      def __init__(self):
+        self.input = util.PathFromRoot('grit/testdata/whitelist_resources.grd')
+        self.verbose = False
+        self.extra_verbose = False
+    whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
+    builder.Run(DummyOpts(), ['-o', output_dir,
+                              '-w', whitelist_file,
+                              '--output-all-resource-defines',])
+    header = os.path.join(output_dir, 'whitelist_test_resources.h')
+    map_cc = os.path.join(output_dir, 'whitelist_test_resources_map.cc')
+
+    whitelisted_ids = [
+        'IDR_STRUCTURE_WHITELISTED',
+        'IDR_STRUCTURE_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED',
+        'IDR_INCLUDE_WHITELISTED',
+        'IDR_INCLUDE_NOT_WHITELISTED',
+    ]
+    non_whitelisted_ids = []
+    for output_file in (header, map_cc):
+      self._verifyWhitelistedOutput(
+        output_file,
+        whitelisted_ids,
+        non_whitelisted_ids,
+      )
+
+  def testOutputAllResourceDefinesFalse(self):
+    output_dir = tempfile.mkdtemp()
+    builder = build.RcBuilder()
+    class DummyOpts(object):
+      def __init__(self):
+        self.input = util.PathFromRoot('grit/testdata/whitelist_resources.grd')
+        self.verbose = False
+        self.extra_verbose = False
+    whitelist_file = util.PathFromRoot('grit/testdata/whitelist.txt')
+    builder.Run(DummyOpts(), ['-o', output_dir,
+                              '-w', whitelist_file,
+                              '--no-output-all-resource-defines',])
+    header = os.path.join(output_dir, 'whitelist_test_resources.h')
+    map_cc = os.path.join(output_dir, 'whitelist_test_resources_map.cc')
+
+    whitelisted_ids = [
+        'IDR_STRUCTURE_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_WHITELISTED',
+        'IDR_INCLUDE_WHITELISTED',
+    ]
+    non_whitelisted_ids = [
+        'IDR_STRUCTURE_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_TRUE_IF_NOT_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_WHITELISTED',
+        'IDR_STRUCTURE_IN_FALSE_IF_NOT_WHITELISTED',
+        'IDR_INCLUDE_NOT_WHITELISTED',
+    ]
+    for output_file in (header, map_cc):
+      self._verifyWhitelistedOutput(
+        output_file,
+        whitelisted_ids,
+        non_whitelisted_ids,
+      )
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/grit_info.py b/grit_info.py
index 47a51f7..ce52997 100755
--- a/grit_info.py
+++ b/grit_info.py
@@ -112,6 +112,12 @@
   # line flags.
   parser.add_option("-E", action="append", dest="build_env", default=[])
   parser.add_option("-w", action="append", dest="whitelist_files", default=[])
+  parser.add_option("--output-all-resource-defines", action="store_true",
+                    dest="output_all_resource_defines", default=True,
+                    help="Unused")
+  parser.add_option("--no-output-all-resource-defines", action="store_false",
+                    dest="output_all_resource_defines", default=True,
+                    help="Unused")
   parser.add_option("-f", dest="ids_file",
                     default="GRIT_DIR/../gritsettings/resource_ids")
   parser.add_option("-t", dest="target_platform", default=None)