Merge tag android-5.1.0_r1 into AOSP_5.1_MERGE

Change-Id: I1f84faa617cf30a8c259dc1790bd96300857f662
diff --git a/grit/format/policy_templates/policy_template_generator.py b/grit/format/policy_templates/policy_template_generator.py
index 11d097e..a1eb123 100644
--- a/grit/format/policy_templates/policy_template_generator.py
+++ b/grit/format/policy_templates/policy_template_generator.py
@@ -118,7 +118,7 @@
 
     if policy['type'] == 'group':
       self._ProcessPolicyList(policy['policies'])
-    elif policy['type'] in ('string-enum', 'int-enum'):
+    elif policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
       # Iterate through all the items of an enum-type policy, and add captions.
       for item in policy['items']:
         item['caption'] = self._ImportMessage(item['caption'])
diff --git a/grit/format/policy_templates/policy_template_generator_unittest.py b/grit/format/policy_templates/policy_template_generator_unittest.py
index f06cc2d..adc4a22 100644
--- a/grit/format/policy_templates/policy_template_generator_unittest.py
+++ b/grit/format/policy_templates/policy_template_generator_unittest.py
@@ -262,6 +262,29 @@
         self.tester.assertEquals(policy['items'][2]['caption'], 'string3')
     self.do_test(policy_data_mock, LocalMockWriter())
 
+  def testStringEnumTexts(self):
+    # Test that GUI messages are assigned correctly to string-enums
+    # (aka dropdown menus).
+    policy_data_mock = {
+      'policy_definitions': [{
+        'name': 'Policy1',
+        'type': 'string-enum-list',
+        'caption': '', 'desc': '',
+        'supported_on': [],
+        'items': [
+          {'name': 'item1', 'value': 'one', 'caption': 'string1', 'desc': ''},
+          {'name': 'item2', 'value': 'two', 'caption': 'string2', 'desc': ''},
+          {'name': 'item3', 'value': 'three', 'caption': 'string3', 'desc': ''},
+        ]
+      }]
+    }
+    class LocalMockWriter(mock_writer.MockWriter):
+      def WritePolicy(self, policy):
+        self.tester.assertEquals(policy['items'][0]['caption'], 'string1')
+        self.tester.assertEquals(policy['items'][1]['caption'], 'string2')
+        self.tester.assertEquals(policy['items'][2]['caption'], 'string3')
+    self.do_test(policy_data_mock, LocalMockWriter())
+
   def testPolicyFiltering(self):
     # Test that policies are filtered correctly based on their annotations.
     policy_data_mock = {
diff --git a/grit/format/policy_templates/writer_configuration.py b/grit/format/policy_templates/writer_configuration.py
index 00da0cc..88de6b5 100644
--- a/grit/format/policy_templates/writer_configuration.py
+++ b/grit/format/policy_templates/writer_configuration.py
@@ -33,6 +33,7 @@
       'win_recommended_category_path': ['chromium_recommended'],
       'admx_namespace': 'Chromium.Policies.Chromium',
       'admx_prefix': 'chromium',
+      'linux_policy_path': '/etc/chromium/policies/',
     }
   elif '_google_chrome' in defines:
     config = {
@@ -47,6 +48,7 @@
       'win_recommended_category_path': ['google', 'googlechrome_recommended'],
       'admx_namespace': 'Google.Policies.Chrome',
       'admx_prefix': 'chrome',
+      'linux_policy_path': '/etc/opt/chrome/policies/',
     }
   else:
     raise Exception('Unknown build')
diff --git a/grit/format/policy_templates/writers/adm_writer.py b/grit/format/policy_templates/writers/adm_writer.py
index 3103c3e..bffbeb5 100644
--- a/grit/format/policy_templates/writers/adm_writer.py
+++ b/grit/format/policy_templates/writers/adm_writer.py
@@ -71,6 +71,7 @@
     'string-enum': 'DROPDOWNLIST',
     'int-enum': 'DROPDOWNLIST',
     'list': 'LISTBOX',
+    'string-enum-list': 'LISTBOX',
     'dict': 'EDITTEXT'
   }
 
@@ -106,7 +107,7 @@
     builder.AddLine()
     adm_type = self.TYPE_TO_INPUT[policy['type']]
     builder.AddLine('PART !!%s  %s' % (policy_part_name, adm_type), 1)
-    if policy['type'] == 'list':
+    if policy['type'] in ('list', 'string-enum-list'):
       # Note that the following line causes FullArmor ADMX Migrator to create
       # corrupt ADMX files. Please use admx_writer to get ADMX files.
       builder.AddLine('KEYNAME "%s\\%s"' % (key_name, policy['name']))
@@ -152,9 +153,10 @@
     builder.AddLine()
 
   def WritePolicy(self, policy):
-    self._WritePolicy(policy,
-                      self.config['win_reg_mandatory_key_name'],
-                      self.policies)
+    if self.CanBeMandatory(policy):
+      self._WritePolicy(policy,
+                        self.config['win_reg_mandatory_key_name'],
+                        self.policies)
 
   def WriteRecommendedPolicy(self, policy):
     self._WritePolicy(policy,
diff --git a/grit/format/policy_templates/writers/adm_writer_unittest.py b/grit/format/policy_templates/writers/adm_writer_unittest.py
index 49c31d8..82374bb 100644
--- a/grit/format/policy_templates/writers/adm_writer_unittest.py
+++ b/grit/format/policy_templates/writers/adm_writer_unittest.py
@@ -147,6 +147,70 @@
 MainPolicy_Explain="Description of main."''')
     self.CompareOutputs(output, expected_output)
 
+  def testMainPolicyRecommendedOnly(self):
+    # Tests a policy group with a single policy of type 'main'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'MainPolicy',
+            'type': 'main',
+            'supported_on': ['chrome.win:8-'],
+            'features': {
+              'can_be_recommended': True,
+              'can_be_mandatory': False
+            },
+            'caption': 'Caption of main.',
+            'desc': 'Description of main.',
+          },
+        ],
+        'placeholders': [],
+        'messages': {
+          'win_supported_winxpsp2': {
+            'text': 'At least Windows 3.12', '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"
+
+    END CATEGORY
+  END CATEGORY
+
+  CATEGORY !!google
+    CATEGORY !!googlechrome_recommended
+      KEYNAME "Software\\Policies\\Google\\Chrome\\Recommended"
+
+      POLICY !!MainPolicy_Policy
+        #if version >= 4
+          SUPPORTED !!SUPPORTED_WINXPSP2
+        #endif
+        EXPLAIN !!MainPolicy_Explain
+        VALUENAME "MainPolicy"
+        VALUEON NUMERIC 1
+        VALUEOFF NUMERIC 0
+      END POLICY
+
+    END CATEGORY
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WINXPSP2="At least Windows 3.12"
+google="Google"
+googlechrome="Google Chrome"
+googlechrome_recommended="Google Chrome - Recommended"
+MainPolicy_Policy="Caption of main."
+MainPolicy_Explain="Description of main."''')
+    self.CompareOutputs(output, expected_output)
+
   def testStringPolicy(self):
     # Tests a policy group with a single policy of type 'string'.
     grd = self.PrepareTest('''
@@ -552,6 +616,86 @@
 ''')
     self.CompareOutputs(output, expected_output)
 
+  def testStringEnumListPolicy(self):
+    # Tests a policy group with a single policy of type 'string-enum-list'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'ListPolicy',
+            'type': 'string-enum-list',
+            'supported_on': ['chrome.win:8-'],
+            'features': { 'can_be_recommended': True },
+            'desc': """Description of list policy.
+With a newline.""",
+            'items': [
+              {'name': 'ProxyServerDisabled', 'value': 'one',
+               'caption': 'Option1'},
+              {'name': 'ProxyServerAutoDetect', 'value': 'two',
+               'caption': 'Option2'},
+            ],
+            'caption': 'Caption of list policy.',
+            'label': 'Label of list policy.'
+          },
+        ],
+        'placeholders': [],
+        'messages': {
+          'win_supported_winxpsp2': {
+            'text': 'At least Windows 3.15', 'desc': 'blah'
+          },
+          'doc_recommended': {
+            'text': 'Recommended', 'desc': 'bleh'
+          }
+        },
+      }''')
+    output = self.GetOutput(grd, 'fr', {'_chromium' : '1'}, 'adm', 'en')
+    expected_output = self.ConstructOutput(
+        ['MACHINE', 'USER'], '''
+  CATEGORY !!chromium
+    KEYNAME "Software\\Policies\\Chromium"
+
+    POLICY !!ListPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WINXPSP2
+      #endif
+      EXPLAIN !!ListPolicy_Explain
+
+      PART !!ListPolicy_Part  LISTBOX
+        KEYNAME "Software\\Policies\\Chromium\\ListPolicy"
+        VALUEPREFIX ""
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+  CATEGORY !!chromium_recommended
+    KEYNAME "Software\\Policies\\Chromium\\Recommended"
+
+    POLICY !!ListPolicy_Policy
+      #if version >= 4
+        SUPPORTED !!SUPPORTED_WINXPSP2
+      #endif
+      EXPLAIN !!ListPolicy_Explain
+
+      PART !!ListPolicy_Part  LISTBOX
+        KEYNAME "Software\\Policies\\Chromium\\Recommended\\ListPolicy"
+        VALUEPREFIX ""
+      END PART
+    END POLICY
+
+  END CATEGORY
+
+
+''', '''[Strings]
+SUPPORTED_WINXPSP2="At least Windows 3.15"
+chromium="Chromium"
+chromium_recommended="Chromium - Recommended"
+ListPolicy_Policy="Caption of list policy."
+ListPolicy_Explain="Description of list policy.\\nWith a newline."
+ListPolicy_Part="Label of list policy."
+''')
+    self.CompareOutputs(output, expected_output)
+
   def testDictionaryPolicy(self):
     # Tests a policy group with a single policy of type 'dict'.
     grd = self.PrepareTest('''
diff --git a/grit/format/policy_templates/writers/adml_writer.py b/grit/format/policy_templates/writers/adml_writer.py
index 20b7c0d..c525602 100644
--- a/grit/format/policy_templates/writers/adml_writer.py
+++ b/grit/format/policy_templates/writers/adml_writer.py
@@ -99,7 +99,7 @@
       dropdownlist_elem = self.AddElement(presentation_elem, 'dropdownList',
                                           {'refId': policy_name})
       dropdownlist_elem.appendChild(self._doc.createTextNode(policy_label))
-    elif policy_type == 'list':
+    elif policy_type in ('list', 'string-enum-list'):
       self._AddString(self._string_table_elem,
                       policy_name + 'Desc',
                       policy_caption)
diff --git a/grit/format/policy_templates/writers/adml_writer_unittest.py b/grit/format/policy_templates/writers/adml_writer_unittest.py
index 3600005..8a8f4f7 100644
--- a/grit/format/policy_templates/writers/adml_writer_unittest.py
+++ b/grit/format/policy_templates/writers/adml_writer_unittest.py
@@ -284,6 +284,44 @@
         '</presentation>')
     self.AssertXMLEquals(output, expected_output)
 
+  def testStringEnumListPolicy(self):
+    list_policy = {
+      'name': 'ListPolicyStub',
+      'type': 'string-enum-list',
+      'caption': 'List policy caption',
+      'label': 'List policy label',
+      'desc': 'This is a test description.',
+      'items': [
+          {
+           'name': 'item 1',
+           'value': 'value 1',
+           'caption': 'Caption Item 1',
+          },
+          {
+           'name': 'item 2',
+           'value': 'value 2',
+           'caption': 'Caption Item 2',
+          },
+      ],
+    }
+    self. _InitWriterForAddingPolicies(self.writer, list_policy)
+    self.writer.WritePolicy(list_policy)
+    # Assert generated string elements.
+    output = self.GetXMLOfChildren(self.writer._string_table_elem)
+    expected_output = (
+        '<string id="ListPolicyStub">List policy caption</string>\n'
+        '<string id="ListPolicyStub_Explain">'
+        'This is a test description.</string>\n'
+        '<string id="ListPolicyStubDesc">List policy caption</string>')
+    self.AssertXMLEquals(output, expected_output)
+    # Assert generated presentation elements.
+    output = self.GetXMLOfChildren(self.writer._presentation_table_elem)
+    expected_output = (
+        '<presentation id="ListPolicyStub">\n'
+        '  <listBox refId="ListPolicyStubDesc">List policy label</listBox>\n'
+        '</presentation>')
+    self.AssertXMLEquals(output, expected_output)
+
   def testDictionaryPolicy(self):
     dict_policy = {
       'name': 'DictionaryPolicyStub',
diff --git a/grit/format/policy_templates/writers/admx_writer.py b/grit/format/policy_templates/writers/admx_writer.py
index 69c797d..0d4394b 100644
--- a/grit/format/policy_templates/writers/admx_writer.py
+++ b/grit/format/policy_templates/writers/admx_writer.py
@@ -286,7 +286,7 @@
     elif policy_type in ('int-enum', 'string-enum'):
       parent = self._GetElements(policy_elem)
       self._AddEnumPolicy(parent, policy)
-    elif policy_type == 'list':
+    elif policy_type in ('list', 'string-enum-list'):
       parent = self._GetElements(policy_elem)
       self._AddListPolicy(parent, key, policy_name)
     elif policy_type == 'group':
@@ -295,10 +295,11 @@
       raise Exception('Unknown policy type %s.' % policy_type)
 
   def WritePolicy(self, policy):
-    self._WritePolicy(policy,
-                      policy['name'],
-                      self.config['win_reg_mandatory_key_name'],
-                      self._active_mandatory_policy_group_name)
+    if self.CanBeMandatory(policy):
+      self._WritePolicy(policy,
+                        policy['name'],
+                        self.config['win_reg_mandatory_key_name'],
+                        self._active_mandatory_policy_group_name)
 
   def WriteRecommendedPolicy(self, policy):
     self._WritePolicy(policy,
diff --git a/grit/format/policy_templates/writers/admx_writer_unittest.py b/grit/format/policy_templates/writers/admx_writer_unittest.py
index c99131f..cb3d39e 100644
--- a/grit/format/policy_templates/writers/admx_writer_unittest.py
+++ b/grit/format/policy_templates/writers/admx_writer_unittest.py
@@ -219,6 +219,46 @@
 
     self.AssertXMLEquals(output, expected_output)
 
+  def testRecommendedOnlyPolicy(self):
+    main_policy = {
+      'name': 'DummyMainPolicy',
+      'type': 'main',
+      'features': {
+        'can_be_recommended': True,
+        'can_be_mandatory': False,
+      }
+    }
+
+    policy_group = {
+      'name': 'PolicyGroup',
+      'policies': [main_policy],
+    }
+    self.writer.BeginTemplate()
+    self.writer.BeginRecommendedPolicyGroup(policy_group)
+
+    self.writer.WritePolicy(main_policy)
+    self.writer.WriteRecommendedPolicy(main_policy)
+
+    output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+    expected_output = (
+        '<policy class="TestClass" displayName="$(string.DummyMainPolicy)"'
+        ' explainText="$(string.DummyMainPolicy_Explain)"'
+        ' key="Software\\Policies\\Test\\Recommended"'
+        ' name="DummyMainPolicy_recommended"'
+        ' presentation="$(presentation.DummyMainPolicy)"'
+        ' valueName="DummyMainPolicy">\n'
+        '  <parentCategory ref="PolicyGroup_recommended"/>\n'
+        '  <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+        '  <enabledValue>\n'
+        '    <decimal value="1"/>\n'
+        '  </enabledValue>\n'
+        '  <disabledValue>\n'
+        '    <decimal value="0"/>\n'
+        '  </disabledValue>\n'
+        '</policy>')
+
+    self.AssertXMLEquals(output, expected_output)
+
   def testStringPolicy(self):
     string_policy = {
       'name': 'SampleStringPolicy',
@@ -369,6 +409,33 @@
 
     self.AssertXMLEquals(output, expected_output)
 
+  def testStringEnumListPolicy(self):
+    list_policy = {
+      'name': 'SampleListPolicy',
+      'type': 'string-enum-list',
+      'items': [
+        {'name': 'item_1', 'value': 'one'},
+        {'name': 'item_2', 'value': 'two'},
+      ]
+    }
+    self._initWriterForPolicy(self.writer, list_policy)
+    self.writer.WritePolicy(list_policy)
+    output = self.GetXMLOfChildren(self._GetPoliciesElement(self.writer._doc))
+    expected_output = (
+        '<policy class="TestClass" displayName="$(string.SampleListPolicy)"'
+        ' explainText="$(string.SampleListPolicy_Explain)"'
+        ' key="Software\\Policies\\Test" name="SampleListPolicy"'
+        ' presentation="$(presentation.SampleListPolicy)">\n'
+        '  <parentCategory ref="PolicyGroup"/>\n'
+        '  <supportedOn ref="SUPPORTED_TESTOS"/>\n'
+        '  <elements>\n'
+        '    <list id="SampleListPolicyDesc"'
+        ' key="Software\Policies\Test\SampleListPolicy" valuePrefix=""/>\n'
+        '  </elements>\n'
+        '</policy>')
+
+    self.AssertXMLEquals(output, expected_output)
+
   def testDictionaryPolicy(self):
     dict_policy = {
       'name': 'SampleDictionaryPolicy',
diff --git a/grit/format/policy_templates/writers/doc_writer.py b/grit/format/policy_templates/writers/doc_writer.py
index 7cca976..d8f108d 100644
--- a/grit/format/policy_templates/writers/doc_writer.py
+++ b/grit/format/policy_templates/writers/doc_writer.py
@@ -4,6 +4,7 @@
 # found in the LICENSE file.
 
 
+import json
 from xml.dom import minidom
 from grit import lazy_re
 from grit.format.policy_templates.writers import xml_formatted_writer
@@ -116,7 +117,7 @@
     # Replace URLs with links in the description.
     self._AddTextWithLinks(parent, policy['desc'])
     # Add list of enum items.
-    if policy['type'] in ('string-enum', 'int-enum'):
+    if policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
       ul = self.AddElement(parent, 'ul')
       for item in policy['items']:
         if policy['type'] == 'int-enum':
@@ -179,7 +180,10 @@
     win = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
     win_text = []
     cnt = 1
-    key_name = self.config['win_reg_mandatory_key_name']
+    if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
+      key_name = self.config['win_reg_recommended_key_name']
+    else:
+      key_name = self.config['win_reg_mandatory_key_name']
     for item in example_value:
       win_text.append(
           '%s\\%s\\%d = "%s"' %
@@ -286,9 +290,12 @@
     '''
     self.AddElement(parent, 'dt', {}, 'Windows:')
     win = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
-    key_name = self.config['win_reg_mandatory_key_name']
-    example = str(policy['example_value'])
-    self.AddText(win, '%s\\%s = "%s"' % (key_name, policy['name'], example))
+    if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
+      key_name = self.config['win_reg_recommended_key_name']
+    else:
+      key_name = self.config['win_reg_mandatory_key_name']
+    example = json.dumps(policy['example_value'])
+    self.AddText(win, '%s\\%s = %s' % (key_name, policy['name'], example))
 
   def _AddDictionaryExampleLinux(self, parent, policy):
     '''Adds an example value for Linux of a 'dict' policy to a DOM node.
@@ -300,7 +307,7 @@
     '''
     self.AddElement(parent, 'dt', {}, 'Linux:')
     linux = self._AddStyledElement(parent, 'dd', ['.monospace'])
-    example = str(policy['example_value'])
+    example = json.dumps(policy['example_value'])
     self.AddText(linux, '%s: %s' % (policy['name'], example))
 
   def _AddDictionaryExample(self, parent, policy):
@@ -379,7 +386,7 @@
       self.AddText(parent, ', '.join(pieces))
     elif policy_type == 'string-enum':
       self.AddText(parent, '"%s"' % (example_value))
-    elif policy_type == 'list':
+    elif policy_type in ('list', 'string-enum-list'):
       self._AddListExample(parent, policy)
     elif policy_type == 'dict':
       self._AddDictionaryExample(parent, policy)
@@ -458,10 +465,14 @@
     if policy['type'] != 'external':
       # All types except 'external' can be set through platform policy.
       if self.IsPolicySupportedOnPlatform(policy, 'win'):
+        if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
+          key_name = self.config['win_reg_recommended_key_name']
+        else:
+          key_name = self.config['win_reg_mandatory_key_name']
         self._AddPolicyAttribute(
             dl,
             'win_reg_loc',
-            self.config['win_reg_mandatory_key_name'] + '\\' + policy['name'],
+            key_name + '\\' + policy['name'],
             ['.monospace'])
       if (self.IsPolicySupportedOnPlatform(policy, 'linux') or
           self.IsPolicySupportedOnPlatform(policy, 'mac')):
@@ -640,16 +651,19 @@
       'int-enum': 'Integer',
       'string-enum': 'String',
       'list': 'List of strings',
+      'string-enum-list': 'List of strings',
       'dict': 'Dictionary',
       'external': 'External data reference',
     }
+    reg_dict = 'REG_SZ; %s' % self._GetLocalizedMessage(
+        'complex_policies_on_windows')
     self._REG_TYPE_MAP = {
       'string': 'REG_SZ',
       'int': 'REG_DWORD',
       'main': 'REG_DWORD',
       'int-enum': 'REG_DWORD',
       'string-enum': 'REG_SZ',
-      'dict': 'REG_SZ, encoded as a JSON string',
+      'dict': reg_dict,
     }
     # The CSS style-sheet used for the document. It will be used in Google
     # Sites, which strips class attributes from HTML tags. To work around this,
diff --git a/grit/format/policy_templates/writers/doc_writer_unittest.py b/grit/format/policy_templates/writers/doc_writer_unittest.py
index 6c087d5..15d8b85 100644
--- a/grit/format/policy_templates/writers/doc_writer_unittest.py
+++ b/grit/format/policy_templates/writers/doc_writer_unittest.py
@@ -6,6 +6,7 @@
 '''Unit tests for grit.format.policy_templates.writers.doc_writer'''
 
 
+import json
 import os
 import sys
 if __name__ == '__main__':
@@ -37,9 +38,11 @@
         'frame_name': 'Chrome Frame',
         'os_name': 'Chrome OS',
         'win_reg_mandatory_key_name': 'MockKey',
+        'win_reg_recommended_key_name': 'MockKeyRec',
       })
     self.writer.messages = {
       'doc_back_to_top': {'text': '_test_back_to_top'},
+      'doc_complex_policies_on_windows': {'text': '_test_complex_policies_win'},
       'doc_data_type': {'text': '_test_data_type'},
       'doc_description': {'text': '_test_description'},
       'doc_description_column_title': {
@@ -48,6 +51,7 @@
       'doc_example_value': {'text': '_test_example_value'},
       'doc_feature_dynamic_refresh': {'text': '_test_feature_dynamic_refresh'},
       'doc_feature_can_be_recommended': {'text': '_test_feature_recommended'},
+      'doc_feature_can_be_mandatory': {'text': '_test_feature_mandatory'},
       'doc_intro': {'text': '_test_intro'},
       'doc_mac_linux_pref_name': {'text': '_test_mac_linux_pref_name'},
       'doc_note': {'text': '_test_note'},
@@ -282,7 +286,7 @@
         '<root>0x00000010 (Windows), 16 (Linux), 16 (Mac)</root>')
 
   def testStringEnumExample(self):
-    # Test representation of 'int-enum' example values.
+    # Test representation of 'string-enum' example values.
     policy = {
       'name': 'PolicyName',
       'type': 'string-enum',
@@ -293,6 +297,40 @@
         self.doc_root.toxml(),
         '<root>&quot;wacky&quot;</root>')
 
+  def testListExample(self):
+    # Test representation of 'list' example values.
+    policy = {
+      'name': 'PolicyName',
+      'type': 'list',
+      'example_value': ['one', 'two'],
+      'supported_on': [ { 'platforms': ['linux'] } ]
+    }
+    self.writer._AddExample(self.doc_root, policy)
+    self.assertEquals(
+        self.doc_root.toxml(),
+        '<root><dl style="style_dd dl;">'
+        '<dt>Linux:</dt>'
+        '<dd style="style_.monospace;">'
+        '[&quot;one&quot;, &quot;two&quot;]'
+        '</dd></dl></root>')
+
+  def testStringEnumListExample(self):
+    # Test representation of 'string-enum-list' example values.
+    policy = {
+      'name': 'PolicyName',
+      'type': 'string-enum-list',
+      'example_value': ['one', 'two'],
+      'supported_on': [ { 'platforms': ['linux'] } ]
+    }
+    self.writer._AddExample(self.doc_root, policy)
+    self.assertEquals(
+        self.doc_root.toxml(),
+        '<root><dl style="style_dd dl;">'
+        '<dt>Linux:</dt>'
+        '<dd style="style_.monospace;">'
+        '[&quot;one&quot;, &quot;two&quot;]'
+        '</dd></dl></root>')
+
   def testStringExample(self):
     # Test representation of 'string' example values.
     policy = {
@@ -380,6 +418,117 @@
         '<dd>0x00000000 (Windows), false (Linux), &lt;false /&gt; (Mac)</dd>'
       '</dl></root>')
 
+  def testAddDictPolicyDetails(self):
+    # Test if the definition list (<dl>) of policy details is created correctly
+    # for 'dict' policies.
+    policy = {
+      'type': 'dict',
+      'name': 'TestPolicyName',
+      'caption': 'TestPolicyCaption',
+      'desc': 'TestPolicyDesc',
+      'supported_on': [{
+        'product': 'chrome',
+        'platforms': ['win', 'mac', 'linux'],
+        'since_version': '8',
+        'until_version': '',
+      }],
+      'features': {'dynamic_refresh': False},
+      'example_value': { 'foo': 123 }
+    }
+    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
+    self.writer._AddPolicyDetails(self.doc_root, policy)
+    self.assertEquals(
+      self.doc_root.toxml(),
+      '<root><dl>'
+      '<dt style="style_dt;">_test_data_type</dt><dd>Dictionary (REG_SZ; _test_complex_policies_win)</dd>'
+      '<dt style="style_dt;">_test_win_reg_loc</dt>'
+      '<dd style="style_.monospace;">MockKey\TestPolicyName</dd>'
+      '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+        '<dd style="style_.monospace;">TestPolicyName</dd>'
+      '<dt style="style_dt;">_test_supported_on</dt>'
+      '<dd>'
+        '<ul style="style_ul;">'
+          '<li>Chrome (Windows, Mac, Linux) ...8...</li>'
+        '</ul>'
+      '</dd>'
+      '<dt style="style_dt;">_test_supported_features</dt>'
+        '<dd>_test_feature_dynamic_refresh: _test_not_supported</dd>'
+      '<dt style="style_dt;">_test_description</dt><dd>TestPolicyDesc</dd>'
+      '<dt style="style_dt;">_test_example_value</dt>'
+        '<dd>'
+          '<dl style="style_dd dl;">'
+            '<dt>Windows:</dt>'
+            '<dd style="style_.monospace;style_.pre;">MockKey\TestPolicyName = {&quot;foo&quot;: 123}</dd>'
+            '<dt>Linux:</dt>'
+            '<dd style="style_.monospace;">TestPolicyName: {&quot;foo&quot;: 123}</dd>'
+            '<dt>Mac:</dt>'
+            '<dd style="style_.monospace;style_.pre;">'
+              '&lt;key&gt;TestPolicyName&lt;/key&gt;\n'
+              '&lt;dict&gt;\n'
+              '  &lt;key&gt;foo&lt;/key&gt;\n'
+              '  &lt;integer&gt;123&lt;/integer&gt;\n'
+              '&lt;/dict&gt;'
+            '</dd>'
+          '</dl>'
+        '</dd>'
+      '</dl></root>')
+
+  def testAddPolicyDetailsRecommendedOnly(self):
+    policy = {
+      'type': 'main',
+      'name': 'TestPolicyName',
+      'caption': 'TestPolicyCaption',
+      'desc': 'TestPolicyDesc',
+      'supported_on': [{
+        'product': 'chrome',
+        'platforms': ['win', 'mac', 'linux'],
+        'since_version': '8',
+        'until_version': '',
+      }, {
+        'product': 'chrome',
+        'platforms': ['android'],
+        'since_version': '30',
+        'until_version': '',
+      }, {
+        'product': 'chrome',
+        'platforms': ['ios'],
+        'since_version': '34',
+        'until_version': '',
+      }],
+      'features': {
+        'dynamic_refresh': False,
+        'can_be_mandatory': False,
+        'can_be_recommended': True
+      },
+      'example_value': False
+    }
+    self.writer.messages['doc_since_version'] = {'text': '...$6...'}
+    self.writer._AddPolicyDetails(self.doc_root, policy)
+    self.assertEquals(
+      self.doc_root.toxml(),
+      '<root><dl>'
+      '<dt style="style_dt;">_test_data_type</dt><dd>Boolean (REG_DWORD)</dd>'
+      '<dt style="style_dt;">_test_win_reg_loc</dt>'
+      '<dd style="style_.monospace;">MockKeyRec\TestPolicyName</dd>'
+      '<dt style="style_dt;">_test_mac_linux_pref_name</dt>'
+        '<dd style="style_.monospace;">TestPolicyName</dd>'
+      '<dt style="style_dt;">_test_supported_on</dt>'
+      '<dd>'
+        '<ul style="style_ul;">'
+          '<li>Chrome (Windows, Mac, Linux) ...8...</li>'
+          '<li>Chrome (Android) ...30...</li>'
+          '<li>Chrome (iOS) ...34...</li>'
+        '</ul>'
+      '</dd>'
+      '<dt style="style_dt;">_test_supported_features</dt>'
+        '<dd>_test_feature_mandatory: _test_not_supported,'
+        ' _test_feature_recommended: _test_supported,'
+        ' _test_feature_dynamic_refresh: _test_not_supported</dd>'
+      '<dt style="style_dt;">_test_description</dt><dd>TestPolicyDesc</dd>'
+      '<dt style="style_dt;">_test_example_value</dt>'
+        '<dd>0x00000000 (Windows), false (Linux), &lt;false /&gt; (Mac)</dd>'
+      '</dl></root>')
+
   def testAddPolicyNote(self):
     # TODO(jkummerow): The functionality tested by this test is currently not
     # used for anything and will probably soon be removed.
@@ -654,14 +803,14 @@
       },
     }
     self.writer._AddDictionaryExample(self.doc_root, policy)
-    value = str(policy['example_value'])
+    value = json.dumps(policy['example_value']).replace('"', '&quot;')
     self.assertEquals(
       self.doc_root.toxml(),
       '<root>'
         '<dl style="style_dd dl;">'
           '<dt>Windows:</dt>'
           '<dd style="style_.monospace;style_.pre;">MockKey\PolicyName = '
-              '&quot;' + value + '&quot;'
+              + value +
           '</dd>'
           '<dt>Linux:</dt>'
           '<dd style="style_.monospace;">PolicyName: ' + value + '</dd>'
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 14a0cab..4743e4e 100644
--- a/grit/format/policy_templates/writers/ios_plist_writer_unittest.py
+++ b/grit/format/policy_templates/writers/ios_plist_writer_unittest.py
@@ -180,6 +180,21 @@
     }
     self._VerifyGeneratedOutput(templates, expected)
 
+  def testStringEnumList(self):
+    templates = self._MakeTemplate('StringEnumListPolicy',
+                                   'string-enum-list', '["a", "b"]',
+        '''
+          'items': [
+            { 'name': 'Foo', 'value': 'a', 'caption': '' },
+            { 'name': 'Bar', 'value': 'b', 'caption': '' },
+          ],
+        ''')
+
+    expected = {
+      'StringEnumListPolicy': [ "a", "b" ],
+    }
+    self._VerifyGeneratedOutput(templates, expected)
+
   def testListOfDictionary(self):
     templates = self._MakeTemplate(
         'ManagedBookmarks', 'dict',
diff --git a/grit/format/policy_templates/writers/json_writer.py b/grit/format/policy_templates/writers/json_writer.py
index 673bbf7..f5af8c1 100644
--- a/grit/format/policy_templates/writers/json_writer.py
+++ b/grit/format/policy_templates/writers/json_writer.py
@@ -49,6 +49,13 @@
     if not self._first_written:
       self._out[-2] += ','
 
+    if not self.CanBeMandatory(policy) and self.CanBeRecommended(policy):
+      line = '  // Note: this policy is supported only in recommended mode.'
+      self._out.append(line)
+      line = '  // The JSON file should be placed in %srecommended.' % \
+             self.config['linux_policy_path']
+      self._out.append(line)
+
     line = '  // %s' % policy['caption']
     self._out.append(line)
     self._out.append(HEADER_DELIMETER)
diff --git a/grit/format/policy_templates/writers/json_writer_unittest.py b/grit/format/policy_templates/writers/json_writer_unittest.py
index 00acde3..b2ed1ef 100644
--- a/grit/format/policy_templates/writers/json_writer_unittest.py
+++ b/grit/format/policy_templates/writers/json_writer_unittest.py
@@ -86,6 +86,40 @@
         '}')
     self.CompareOutputs(output, expected_output)
 
+  def testRecommendedOnlyPolicy(self):
+    # Tests a policy group with a single policy of type 'main'.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": ['
+        '    {'
+        '      "name": "MainPolicy",'
+        '      "type": "main",'
+        '      "caption": "Example Main Policy",'
+        '      "desc": "Example Main Policy",'
+        '      "features": {'
+        '        "can_be_recommended": True,'
+        '        "can_be_mandatory": False'
+        '      },'
+        '      "supported_on": ["chrome.linux:8-"],'
+        '      "example_value": True'
+        '    },'
+        '  ],'
+        '  "placeholders": [],'
+        '  "messages": {},'
+        '}')
+    output = self.GetOutput(grd, 'fr', {'_google_chrome' : '1'}, 'json', 'en')
+    expected_output = (
+        TEMPLATE_HEADER +
+        '  // Note: this policy is supported only in recommended mode.\n' +
+        '  // The JSON file should be placed in' +
+        ' /etc/opt/chrome/policies/recommended.\n' +
+        '  // Example Main Policy\n' +
+        HEADER_DELIMETER +
+        '  // Example Main Policy\n\n'
+        '  //"MainPolicy": true\n\n'
+        '}')
+    self.CompareOutputs(output, expected_output)
+
   def testStringPolicy(self):
     # Tests a policy group with a single policy of type 'string'.
     grd = self.PrepareTest(
@@ -231,6 +265,39 @@
         '}')
     self.CompareOutputs(output, expected_output)
 
+  def testStringEnumListPolicy(self):
+    # Tests a policy group with a single policy of type 'string-enum-list'.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": ['
+        '    {'
+        '      "name": "ListPolicy",'
+        '      "type": "string-enum-list",'
+        '      "caption": "Example List",'
+        '      "desc": "Example List",'
+        '      "items": ['
+        '        {"name": "ProxyServerDisabled", "value": "one",'
+        '         "caption": ""},'
+        '        {"name": "ProxyServerAutoDetect", "value": "two",'
+        '         "caption": ""},'
+        '      ],'
+        '      "supported_on": ["chrome.linux:8-"],'
+        '      "example_value": ["one", "two"]'
+        '    },'
+        '  ],'
+        '  "placeholders": [],'
+        '  "messages": {},'
+        '}')
+    output = self.GetOutput(grd, 'fr', {'_chromium' : '1'}, 'json', 'en')
+    expected_output = (
+        TEMPLATE_HEADER +
+        '  // Example List\n' +
+        HEADER_DELIMETER +
+        '  // Example List\n\n'
+        '  //"ListPolicy": ["one", "two"]\n\n'
+        '}')
+    self.CompareOutputs(output, expected_output)
+
   def testDictionaryPolicy(self):
     # Tests a policy group with a single policy of type 'dict'.
     example = {
diff --git a/grit/format/policy_templates/writers/plist_strings_writer.py b/grit/format/policy_templates/writers/plist_strings_writer.py
index 08fcddd..966aaf2 100644
--- a/grit/format/policy_templates/writers/plist_strings_writer.py
+++ b/grit/format/policy_templates/writers/plist_strings_writer.py
@@ -52,7 +52,7 @@
     if policy['type'] == 'external':
       # This type can only be set through cloud policy.
       return
-    elif policy['type'] in ('int-enum','string-enum'):
+    elif policy['type'] in ('int-enum','string-enum', 'string-enum-list'):
       # Append the captions of enum items to the description string.
       item_descs = []
       for item in policy['items']:
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 613a3cd..17fc85c 100644
--- a/grit/format/policy_templates/writers/plist_strings_writer_unittest.py
+++ b/grit/format/policy_templates/writers/plist_strings_writer_unittest.py
@@ -127,6 +127,111 @@
             '"Description of policy.\\nWith a newline.";')
     self.assertEquals(output.strip(), expected_output.strip())
 
+  def testStringListPolicy(self):
+    # Tests a policy group with a single policy of type 'list'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'ListGroup',
+            'type': 'group',
+            'caption': '',
+            'desc': '',
+            'policies': [{
+              'name': 'ListPolicy',
+              'type': 'list',
+              'caption': 'Caption of policy.',
+              'desc': """Description of policy.
+With a newline.""",
+              'schema': {
+                  'type': 'array',
+                  'items': { 'type': 'string' },
+              },
+              'supported_on': ['chrome.mac:8-'],
+            }],
+          },
+        ],
+        'placeholders': [],
+        'messages': {
+          'mac_chrome_preferences': {
+            'text': 'Preferences of $1',
+            'desc': 'blah'
+          }
+        }
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist_strings',
+        'en')
+    expected_output = (
+        'Chromium.pfm_title = "Chromium";\n'
+        'Chromium.pfm_description = "Preferences of Chromium";\n'
+        'ListPolicy.pfm_title = "Caption of policy.";\n'
+        'ListPolicy.pfm_description = '
+            '"Description of policy.\\nWith a newline.";')
+    self.assertEquals(output.strip(), expected_output.strip())
+
+  def testStringEnumListPolicy(self):
+    # Tests a policy group with a single policy of type 'string-enum-list'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'EnumGroup',
+            'type': 'group',
+            'caption': '',
+            'desc': '',
+            'policies': [{
+              'name': 'EnumPolicy',
+              'type': 'string-enum-list',
+              'caption': 'Caption of policy.',
+              'desc': """Description of policy.
+With a newline.""",
+              'schema': {
+                  'type': 'array',
+                  'items': { 'type': 'string' },
+              },
+              'items': [
+                {
+                  'name': 'ProxyServerDisabled',
+                  'value': 'one',
+                  'caption': 'Option1'
+                },
+                {
+                  'name': 'ProxyServerAutoDetect',
+                  'value': 'two',
+                  'caption': 'Option2'
+                },
+              ],
+              'supported_on': ['chrome.mac:8-'],
+            }],
+          },
+        ],
+        'placeholders': [],
+        'messages': {
+          'mac_chrome_preferences': {
+            'text': 'Preferences of $1',
+            'desc': 'blah'
+          }
+        }
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist_strings',
+        'en')
+    expected_output = (
+        'Chromium.pfm_title = "Chromium";\n'
+        'Chromium.pfm_description = "Preferences of Chromium";\n'
+        'EnumPolicy.pfm_title = "Caption of policy.";\n'
+        'EnumPolicy.pfm_description = '
+            '"one - Option1\\ntwo - Option2\\n'
+            'Description of policy.\\nWith a newline.";')
+    self.assertEquals(output.strip(), expected_output.strip())
+
   def testIntEnumPolicy(self):
     # Tests a policy group with a single policy of type 'int-enum'.
     grd = self.PrepareTest('''
diff --git a/grit/format/policy_templates/writers/plist_writer.py b/grit/format/policy_templates/writers/plist_writer.py
index 2297858..6d929d6 100644
--- a/grit/format/policy_templates/writers/plist_writer.py
+++ b/grit/format/policy_templates/writers/plist_writer.py
@@ -32,6 +32,7 @@
     'int': 'integer',
     'int-enum': 'integer',
     'string-enum': 'string',
+    'string-enum-list': 'array',
     'main': 'boolean',
     'list': 'array',
     'dict': 'dictionary',
@@ -66,7 +67,7 @@
     self.AddElement(parent, 'key', {}, key_string)
     self.AddElement(parent, 'string', {}, value_string)
 
-  def _AddTargets(self, parent):
+  def _AddTargets(self, parent, policy):
     '''Adds the following XML snippet to an XML element:
       <key>pfm_targets</key>
       <array>
@@ -77,7 +78,10 @@
         parent: The parent XML element where the snippet will be added.
     '''
     array = self._AddKeyValuePair(parent, 'pfm_targets', 'array')
-    self.AddElement(array, 'string', {}, 'user-managed')
+    if self.CanBeRecommended(policy):
+      self.AddElement(array, 'string', {}, 'user')
+    if self.CanBeMandatory(policy):
+      self.AddElement(array, 'string', {}, 'user-managed')
 
   def PreprocessPolicies(self, policy_list):
     return self.FlattenGroupsAndSortPolicies(policy_list)
@@ -96,7 +100,7 @@
     # Those files are generated by plist_strings_writer.
     self._AddStringKeyValuePair(dict, 'pfm_description', '')
     self._AddStringKeyValuePair(dict, 'pfm_title', '')
-    self._AddTargets(dict)
+    self._AddTargets(dict, policy)
     self._AddStringKeyValuePair(dict, 'pfm_type',
                                 self.TYPE_TO_INPUT[policy_type])
     if policy_type in ('int-enum', 'string-enum'):
@@ -107,7 +111,7 @@
         else:
           element_type = 'string'
         self.AddElement(range_list, element_type, {}, str(item['value']))
-    elif policy_type == 'list':
+    elif policy_type in ('list', 'string-enum-list'):
       subkeys = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array')
       subkeys_dict = self.AddElement(subkeys, 'dict')
       subkeys_type = self._AddKeyValuePair(subkeys_dict, 'pfm_type', 'string')
diff --git a/grit/format/policy_templates/writers/plist_writer_unittest.py b/grit/format/policy_templates/writers/plist_writer_unittest.py
index a9d146d..ddbdbfe 100644
--- a/grit/format/policy_templates/writers/plist_writer_unittest.py
+++ b/grit/format/policy_templates/writers/plist_writer_unittest.py
@@ -118,6 +118,108 @@
     </array>''')
     self.assertEquals(output.strip(), expected_output.strip())
 
+  def testRecommendedPolicy(self):
+    # Tests a policy group with a single policy of type 'main'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'MainGroup',
+            'type': 'group',
+            'policies': [{
+              'name': 'MainPolicy',
+              'type': 'main',
+              'desc': '',
+              'caption': '',
+              'features': {
+                'can_be_recommended' : True
+              },
+              'supported_on': ['chrome.mac:8-'],
+            }],
+            'desc': '',
+            'caption': '',
+          },
+        ],
+        'placeholders': [],
+        'messages': {}
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist',
+        'en')
+    expected_output = self._GetExpectedOutputs(
+        'Chromium', 'com.example.Test', '''<array>
+      <dict>
+        <key>pfm_name</key>
+        <string>MainPolicy</string>
+        <key>pfm_description</key>
+        <string/>
+        <key>pfm_title</key>
+        <string/>
+        <key>pfm_targets</key>
+        <array>
+          <string>user</string>
+          <string>user-managed</string>
+        </array>
+        <key>pfm_type</key>
+        <string>boolean</string>
+      </dict>
+    </array>''')
+    self.assertEquals(output.strip(), expected_output.strip())
+
+  def testRecommendedOnlyPolicy(self):
+    # Tests a policy group with a single policy of type 'main'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'MainGroup',
+            'type': 'group',
+            'policies': [{
+              'name': 'MainPolicy',
+              'type': 'main',
+              'desc': '',
+              'caption': '',
+              'features': {
+                'can_be_recommended' : True,
+                'can_be_mandatory' : False
+              },
+              'supported_on': ['chrome.mac:8-'],
+            }],
+            'desc': '',
+            'caption': '',
+          },
+        ],
+        'placeholders': [],
+        'messages': {}
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist',
+        'en')
+    expected_output = self._GetExpectedOutputs(
+        'Chromium', 'com.example.Test', '''<array>
+      <dict>
+        <key>pfm_name</key>
+        <string>MainPolicy</string>
+        <key>pfm_description</key>
+        <string/>
+        <key>pfm_title</key>
+        <string/>
+        <key>pfm_targets</key>
+        <array>
+          <string>user</string>
+        </array>
+        <key>pfm_type</key>
+        <string>boolean</string>
+      </dict>
+    </array>''')
+    self.assertEquals(output.strip(), expected_output.strip())
+
   def testStringPolicy(self):
     # Tests a policy group with a single policy of type 'string'.
     grd = self.PrepareTest('''
@@ -165,6 +267,127 @@
     </array>''')
     self.assertEquals(output.strip(), expected_output.strip())
 
+  def testListPolicy(self):
+    # Tests a policy group with a single policy of type 'list'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'ListGroup',
+            'type': 'group',
+            'desc': '',
+            'caption': '',
+            'policies': [{
+              'name': 'ListPolicy',
+              'type': 'list',
+              'schema': {
+                'type': 'array',
+                'items': { 'type': 'string' },
+              },
+              'supported_on': ['chrome.mac:8-'],
+              'desc': '',
+              'caption': '',
+            }],
+          },
+        ],
+        'placeholders': [],
+        'messages': {},
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist',
+        'en')
+    expected_output = self._GetExpectedOutputs(
+        'Chromium', 'com.example.Test', '''<array>
+      <dict>
+        <key>pfm_name</key>
+        <string>ListPolicy</string>
+        <key>pfm_description</key>
+        <string/>
+        <key>pfm_title</key>
+        <string/>
+        <key>pfm_targets</key>
+        <array>
+          <string>user-managed</string>
+        </array>
+        <key>pfm_type</key>
+        <string>array</string>
+        <key>pfm_subkeys</key>
+        <array>
+          <dict>
+            <key>pfm_type</key>
+            <string>string</string>
+          </dict>
+        </array>
+      </dict>
+    </array>''')
+    self.assertEquals(output.strip(), expected_output.strip())
+
+  def testStringEnumListPolicy(self):
+    # Tests a policy group with a single policy of type 'string-enum-list'.
+    grd = self.PrepareTest('''
+      {
+        'policy_definitions': [
+          {
+            'name': 'ListGroup',
+            'type': 'group',
+            'desc': '',
+            'caption': '',
+            'policies': [{
+              'name': 'ListPolicy',
+              'type': 'string-enum-list',
+              'schema': {
+                'type': 'array',
+                'items': { 'type': 'string' },
+              },
+              'items': [
+                {'name': 'ProxyServerDisabled', 'value': 'one', 'caption': ''},
+                {'name': 'ProxyServerAutoDetect', 'value': 'two', 'caption': ''},
+              ],
+              'supported_on': ['chrome.mac:8-'],
+              'supported_on': ['chrome.mac:8-'],
+              'desc': '',
+              'caption': '',
+            }],
+          },
+        ],
+        'placeholders': [],
+        'messages': {},
+      }''')
+    output = self.GetOutput(
+        grd,
+        'fr',
+        {'_chromium' : '1', 'mac_bundle_id': 'com.example.Test'},
+        'plist',
+        'en')
+    expected_output = self._GetExpectedOutputs(
+        'Chromium', 'com.example.Test', '''<array>
+      <dict>
+        <key>pfm_name</key>
+        <string>ListPolicy</string>
+        <key>pfm_description</key>
+        <string/>
+        <key>pfm_title</key>
+        <string/>
+        <key>pfm_targets</key>
+        <array>
+          <string>user-managed</string>
+        </array>
+        <key>pfm_type</key>
+        <string>array</string>
+        <key>pfm_subkeys</key>
+        <array>
+          <dict>
+            <key>pfm_type</key>
+            <string>string</string>
+          </dict>
+        </array>
+      </dict>
+    </array>''')
+    self.assertEquals(output.strip(), expected_output.strip())
+
   def testIntPolicy(self):
     # Tests a policy group with a single policy of type 'int'.
     grd = self.PrepareTest('''
diff --git a/grit/format/policy_templates/writers/reg_writer.py b/grit/format/policy_templates/writers/reg_writer.py
index beb1590..ad83046 100644
--- a/grit/format/policy_templates/writers/reg_writer.py
+++ b/grit/format/policy_templates/writers/reg_writer.py
@@ -47,7 +47,7 @@
     list.sort() methods to sort policies.
     See TemplateWriter.SortPoliciesGroupsFirst for usage.
     '''
-    is_list = policy['type'] == 'list'
+    is_list = policy['type'] in ('list', 'string-enum-list')
     # Lists come after regular policies.
     return (is_list, policy['name'])
 
@@ -57,7 +57,7 @@
     if policy['type'] == 'external':
       # This type can only be set through cloud policy.
       return
-    elif policy['type'] == 'list':
+    elif policy['type'] in ('list', 'string-enum-list'):
       self._StartBlock(key, policy['name'], list)
       i = 1
       for item in example_value:
@@ -83,9 +83,10 @@
       list.append('"%s"=%s' % (policy['name'], example_value_str))
 
   def WritePolicy(self, policy):
-    self._WritePolicy(policy,
-                      self.config['win_reg_mandatory_key_name'],
-                      self._mandatory)
+    if self.CanBeMandatory(policy):
+      self._WritePolicy(policy,
+                        self.config['win_reg_mandatory_key_name'],
+                        self._mandatory)
 
   def WriteRecommendedPolicy(self, policy):
     self._WritePolicy(policy,
diff --git a/grit/format/policy_templates/writers/reg_writer_unittest.py b/grit/format/policy_templates/writers/reg_writer_unittest.py
index d559c9f..88fb2e2 100644
--- a/grit/format/policy_templates/writers/reg_writer_unittest.py
+++ b/grit/format/policy_templates/writers/reg_writer_unittest.py
@@ -77,6 +77,35 @@
         '"MainPolicy"=dword:00000001'])
     self.CompareOutputs(output, expected_output)
 
+  def testRecommendedMainPolicy(self):
+    # Tests a policy group with a single policy of type 'main'.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": ['
+        '    {'
+        '      "name": "MainPolicy",'
+        '      "type": "main",'
+        '      "features": {'
+        '        "can_be_recommended": True,'
+        '        "can_be_mandatory": False '
+        '      },'
+        '      "caption": "",'
+        '      "desc": "",'
+        '      "supported_on": ["chrome.win:8-"],'
+        '      "example_value": True'
+        '    },'
+        '  ],'
+        '  "placeholders": [],'
+        '  "messages": {},'
+        '}')
+    output = self.GetOutput(grd, 'fr', {'_google_chrome' : '1'}, 'reg', 'en')
+    expected_output = self.NEWLINE.join([
+        'Windows Registry Editor Version 5.00',
+        '',
+        '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Google\\Chrome\\Recommended]',
+        '"MainPolicy"=dword:00000001'])
+    self.CompareOutputs(output, expected_output)
+
   def testStringPolicy(self):
     # Tests a policy group with a single policy of type 'string'.
     grd = self.PrepareTest(
@@ -212,6 +241,37 @@
         '"1"="foo"',
         '"2"="bar"'])
 
+  def testStringEnumListPolicy(self):
+    # Tests a policy group with a single policy of type 'string-enum-list'.
+    grd = self.PrepareTest(
+        '{'
+        '  "policy_definitions": ['
+        '    {'
+        '      "name": "ListPolicy",'
+        '      "type": "string-enum-list",'
+        '      "caption": "",'
+        '      "desc": "",'
+        '      "items": ['
+        '        {"name": "ProxyServerDisabled", "value": "foo",'
+        '         "caption": ""},'
+        '        {"name": "ProxyServerAutoDetect", "value": "bar",'
+                '         "caption": ""},'
+        '      ],'
+        '      "supported_on": ["chrome.linux:8-"],'
+        '      "example_value": ["foo", "bar"]'
+        '    },'
+        '  ],'
+        '  "placeholders": [],'
+        '  "messages": {},'
+        '}')
+    output = self.GetOutput(grd, 'fr', {'_chromium' : '1'}, 'reg', 'en')
+    expected_output = self.NEWLINE.join([
+        'Windows Registry Editor Version 5.00',
+        '',
+        '[HKEY_LOCAL_MACHINE\\Software\\Policies\\Chromium\\ListPolicy]',
+        '"1"="foo"',
+        '"2"="bar"'])
+
   def testDictionaryPolicy(self):
     # Tests a policy group with a single policy of type 'dict'.
     example = {
diff --git a/grit/format/policy_templates/writers/template_writer.py b/grit/format/policy_templates/writers/template_writer.py
index 2a8b548..bd48425 100644
--- a/grit/format/policy_templates/writers/template_writer.py
+++ b/grit/format/policy_templates/writers/template_writer.py
@@ -87,6 +87,10 @@
     '''Checks if the given policy can be recommended.'''
     return policy.get('features', {}).get('can_be_recommended', False)
 
+  def CanBeMandatory(self, policy):
+    '''Checks if the given policy can be mandatory.'''
+    return policy.get('features', {}).get('can_be_mandatory', True)
+
   def IsPolicySupportedOnPlatform(self, policy, platform):
     '''Checks if |policy| is supported on |platform|.
 
diff --git a/grit/format/resource_map.py b/grit/format/resource_map.py
index b5a7b45..37ac54a 100644
--- a/grit/format/resource_map.py
+++ b/grit/format/resource_map.py
@@ -109,18 +109,18 @@
   tids = rc_header.GetIds(root)
   seen = set()
   active_descendants = [item for item in root.ActiveDescendants()]
+  output_all_resource_defines = root.ShouldOutputAllResourceDefines()
   for item in root:
-    if isinstance(item, (include.IncludeNode,
-                         structure.StructureNode,
-                         message.MessageNode)):
-      key = get_key(item)
-      tid = item.attrs['name']
-      if tid in tids and key not in seen:
-        seen.add(key)
-        # For messages, only include the active ones
-        if not isinstance(item, message.MessageNode) \
-            or item in active_descendants:
-          yield '  {"%s", %s},\n' % (key, tid)
+    if not item.IsResourceMapSource():
+      continue
+    key = get_key(item)
+    tid = item.attrs['name']
+    if tid not in tids or key in seen:
+      continue
+    seen.add(key)
+    if item.GeneratesResourceMapEntry(output_all_resource_defines,
+                                      item in active_descendants):
+      yield '  {"%s", %s},\n' % (key, tid)
   yield _FormatSourceFooter(root)
 
 
diff --git a/grit/format/resource_map_unittest.py b/grit/format/resource_map_unittest.py
index 8f162b5..55de504 100644
--- a/grit/format/resource_map_unittest.py
+++ b/grit/format/resource_map_unittest.py
@@ -94,6 +94,129 @@
 };
 const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
 
+  def testFormatResourceMapWithOutputAllEqualsFalseForStructures(self):
+    grd = grd_reader.Parse(StringIO.StringIO(
+      '''<?xml version="1.0" encoding="UTF-8"?>
+      <grit latest_public_release="2" source_lang_id="en" current_release="3"
+            base_dir="." output_all_resource_defines="false">
+        <outputs>
+          <output type="rc_header" filename="the_rc_header.h" />
+          <output type="resource_map_header"
+                  filename="the_resource_map_header.h" />
+          <output type="resource_map_source"
+                  filename="the_resource_map_header.cc" />
+        </outputs>
+        <release seq="3">
+          <structures first_id="300">
+            <structure type="chrome_scaled_image" name="IDR_KLONKMENU"
+                       file="foo.png" />
+            <if expr="False">
+              <structure type="chrome_scaled_image" name="IDR_MISSING"
+                         file="bar.png" />
+            </if>
+         </structures>
+        </release>
+      </grit>'''), util.PathFromRoot('.'))
+    grd.SetOutputLanguage('en')
+    grd.RunGatherers()
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include <stddef.h>
+#ifndef GRIT_RESOURCE_MAP_STRUCT_
+#define GRIT_RESOURCE_MAP_STRUCT_
+struct GritResourceMap {
+  const char* const name;
+  int value;
+};
+#endif // GRIT_RESOURCE_MAP_STRUCT_
+extern const GritResourceMap kTheRcHeader[];
+extern const size_t kTheRcHeaderSize;''', output)
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include "the_resource_map_header.h"
+#include "base/basictypes.h"
+#include "the_rc_header.h"
+const GritResourceMap kTheRcHeader[] = {
+  {"IDR_KLONKMENU", IDR_KLONKMENU},
+};
+const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include "the_resource_map_header.h"
+#include "base/basictypes.h"
+#include "the_rc_header.h"
+const GritResourceMap kTheRcHeader[] = {
+  {"IDR_KLONKMENU", IDR_KLONKMENU},
+};
+const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
+
+  def testFormatResourceMapWithOutputAllEqualsFalseForIncludes(self):
+    grd = grd_reader.Parse(StringIO.StringIO(
+      '''<?xml version="1.0" encoding="UTF-8"?>
+      <grit latest_public_release="2" source_lang_id="en" current_release="3"
+            base_dir="." output_all_resource_defines="false">
+        <outputs>
+          <output type="rc_header" filename="the_rc_header.h" />
+          <output type="resource_map_header"
+                  filename="the_resource_map_header.h" />
+        </outputs>
+        <release seq="3">
+          <structures first_id="300">
+            <structure type="menu" name="IDC_KLONKMENU"
+                       file="grit\\testdata\\klonk.rc" encoding="utf-16" />
+          </structures>
+          <includes first_id="10000">
+            <include type="foo" file="abc" name="IDS_FIRSTPRESENT" />
+            <if expr="False">
+              <include type="foo" file="def" name="IDS_MISSING" />
+            </if>
+            <include type="foo" file="mno" name="IDS_THIRDPRESENT" />
+         </includes>
+        </release>
+      </grit>'''), util.PathFromRoot('.'))
+    grd.SetOutputLanguage('en')
+    grd.RunGatherers()
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include <stddef.h>
+#ifndef GRIT_RESOURCE_MAP_STRUCT_
+#define GRIT_RESOURCE_MAP_STRUCT_
+struct GritResourceMap {
+  const char* const name;
+  int value;
+};
+#endif // GRIT_RESOURCE_MAP_STRUCT_
+extern const GritResourceMap kTheRcHeader[];
+extern const size_t kTheRcHeaderSize;''', output)
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_map_source')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include "the_resource_map_header.h"
+#include "base/basictypes.h"
+#include "the_rc_header.h"
+const GritResourceMap kTheRcHeader[] = {
+  {"IDC_KLONKMENU", IDC_KLONKMENU},
+  {"IDS_FIRSTPRESENT", IDS_FIRSTPRESENT},
+  {"IDS_THIRDPRESENT", IDS_THIRDPRESENT},
+};
+const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
+    output = util.StripBlankLinesAndComments(''.join(
+        resource_map.GetFormatter('resource_file_map_source')(grd, 'en', '.')))
+    self.assertEqual('''\
+#include "the_resource_map_header.h"
+#include "base/basictypes.h"
+#include "the_rc_header.h"
+const GritResourceMap kTheRcHeader[] = {
+  {"grit/testdata/klonk.rc", IDC_KLONKMENU},
+  {"abc", IDS_FIRSTPRESENT},
+  {"mno", IDS_THIRDPRESENT},
+};
+const size_t kTheRcHeaderSize = arraysize(kTheRcHeader);''', output)
+
   def testFormatStringResourceMap(self):
     grd = grd_reader.Parse(StringIO.StringIO(
       '''<?xml version="1.0" encoding="UTF-8"?>
diff --git a/grit/node/base.py b/grit/node/base.py
index 3de51b6..a40794b 100644
--- a/grit/node/base.py
+++ b/grit/node/base.py
@@ -590,6 +590,21 @@
     '''Whether we need to expand variables on a given node.'''
     return False
 
+  def IsResourceMapSource(self):
+    '''Whether this node is a resource map source.'''
+    return False
+
+  def GeneratesResourceMapEntry(self, output_all_resource_defines,
+                                is_active_descendant):
+    '''Whether this node should output a resource map entry.
+
+    Args:
+      output_all_resource_defines: The value of output_all_resource_defines for
+                                   the root node.
+      is_active_descendant: Whether the current node is an active descendant
+                            from the root node.'''
+    return False
+
 
 class ContentNode(Node):
   '''Convenience baseclass for nodes that can have content.'''
diff --git a/grit/node/include.py b/grit/node/include.py
index 9c3685f..8d32064 100644
--- a/grit/node/include.py
+++ b/grit/node/include.py
@@ -113,6 +113,16 @@
          self.ToRealPath(self.GetInputPath()),
          allow_external_script=allow_external_script)
 
+  def IsResourceMapSource(self):
+    return True
+
+  def GeneratesResourceMapEntry(self, output_all_resource_defines,
+                                is_active_descendant):
+    # includes always generate resource entries.
+    if output_all_resource_defines:
+      return True
+    return is_active_descendant
+
   @staticmethod
   def Construct(parent, name, type, file, translateable=True,
                 filenameonly=False, mkoutput=False, relativepath=False):
diff --git a/grit/node/message.py b/grit/node/message.py
index ca80f41..48cd1c7 100644
--- a/grit/node/message.py
+++ b/grit/node/message.py
@@ -224,6 +224,13 @@
     message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
     return id, util.Encode(message, encoding)
 
+  def IsResourceMapSource(self):
+    return True
+
+  def GeneratesResourceMapEntry(self, output_all_resource_defines,
+                                is_active_descendant):
+    return is_active_descendant
+
   @staticmethod
   def Construct(parent, message, name, desc='', meaning='', translateable=True):
     '''Constructs a new message node that is a child of 'parent', with the
diff --git a/grit/node/structure.py b/grit/node/structure.py
index 48968f6..7ccd2fb 100644
--- a/grit/node/structure.py
+++ b/grit/node/structure.py
@@ -332,6 +332,15 @@
 
     return filename
 
+  def IsResourceMapSource(self):
+    return True
+
+  def GeneratesResourceMapEntry(self, output_all_resource_defines,
+                                is_active_descendant):
+    if output_all_resource_defines:
+      return True
+    return is_active_descendant
+
   @staticmethod
   def Construct(parent, name, type, file, encoding='cp1252'):
     '''Creates a new node which is a child of 'parent', with attributes set
diff --git a/grit/tool/build.py b/grit/tool/build.py
index 87ee412..537e2c6 100644
--- a/grit/tool/build.py
+++ b/grit/tool/build.py
@@ -63,6 +63,20 @@
 
 Options:
 
+  -a FILE           Assert that the given file is an output. There can be
+                    multiple "-a" flags listed for multiple outputs. If a "-a"
+                    or "--assert-file-list" argument is present, then the list
+                    of asserted files must match the output files or the tool
+                    will fail. The use-case is for the build system to maintain
+                    separate lists of output files and to catch errors if the
+                    build system's list and the grit list are out-of-sync.
+
+  --assert-file-list  Provide a file listing multiple asserted output files.
+                    There is one file name per line. This acts like specifying
+                    each file with "-a" on the command line, but without the
+                    possibility of running into OS line-length limits for very
+                    long lists.
+
   -o OUTPUTDIR      Specify what directory output paths are relative to.
                     Defaults to the current directory.
 
@@ -104,13 +118,20 @@
     self.output_directory = '.'
     first_ids_file = None
     whitelist_filenames = []
+    assert_output_files = []
     target_platform = None
     depfile = None
     depdir = None
     rc_header_format = None
-    (own_opts, args) = getopt.getopt(args, 'o:D:E:f:w:t:h:', ('depdir=','depfile='))
+    (own_opts, args) = getopt.getopt(args, 'a:o:D:E:f:w:t:h:',
+        ('depdir=','depfile=','assert-file-list='))
     for (key, val) in own_opts:
-      if key == '-o':
+      if key == '-a':
+        assert_output_files.append(val)
+      elif key == '--assert-file-list':
+        with open(val) as f:
+          assert_output_files += f.read().splitlines()
+      elif key == '-o':
         self.output_directory = val
       elif key == '-D':
         name, val = util.ParseDefine(val)
@@ -166,8 +187,12 @@
     self.res.RunGatherers()
     self.Process()
 
+    if assert_output_files:
+      if not self.CheckAssertedOutputFiles(assert_output_files):
+        return 2
+
     if depfile and depdir:
-      self.GenerateDepfile(opts.input, depfile, depdir)
+      self.GenerateDepfile(depfile, depdir)
 
     return 0
 
@@ -324,7 +349,31 @@
       print self.res.UberClique().missing_translations_
       sys.exit(-1)
 
-  def GenerateDepfile(self, input_filename, depfile, depdir):
+
+  def CheckAssertedOutputFiles(self, assert_output_files):
+    '''Checks that the asserted output files are specified in the given list.
+
+    Returns true if the asserted files are present. If they are not, returns
+    False and prints the failure.
+    '''
+    # Compare the absolute path names, sorted.
+    asserted = sorted([os.path.abspath(i) for i in assert_output_files])
+    actual = sorted([
+        os.path.abspath(os.path.join(self.output_directory, i.GetFilename()))
+        for i in self.res.GetOutputFiles()])
+
+    if asserted != actual:
+      print '''Asserted file list does not match.
+
+Expected output files: %s
+
+Actual output files: %s
+''' % (asserted, actual)
+      return False
+    return True
+
+
+  def GenerateDepfile(self, depfile, depdir):
     '''Generate a depfile that contains the imlicit dependencies of the input
     grd. The depfile will be in the same format as a makefile, and will contain
     references to files relative to |depdir|. It will be put in |depfile|.
@@ -343,19 +392,27 @@
     from the directory src/ we will generate a depfile ../out/gen/blah.grd.d
     that has the contents
 
-      gen/blah.grd.d: ../src/input1.xtb ../src/input2.xtb
+      gen/blah.h: ../src/input1.xtb ../src/input2.xtb
+
+    Where "gen/blah.h" is the first output (Ninja expects the .d file to list
+    the first output in cases where there is more than one).
 
     Note that all paths in the depfile are relative to ../out, the depdir.
     '''
     depfile = os.path.abspath(depfile)
     depdir = os.path.abspath(depdir)
+    infiles = self.res.GetInputFiles()
+
+    # Get the first output file relative to the depdir.
+    outputs = self.res.GetOutputFiles()
+    output_file = os.path.relpath(os.path.join(
+          self.output_directory, outputs[0].GetFilename()), depdir)
+
     # The path prefix to prepend to dependencies in the depfile.
     prefix = os.path.relpath(os.getcwd(), depdir)
-    # The path that the depfile refers to itself by.
-    self_ref_depfile = os.path.relpath(depfile, depdir)
-    infiles = self.res.GetInputFiles()
     deps_text = ' '.join([os.path.join(prefix, i) for i in infiles])
-    depfile_contents = self_ref_depfile + ': ' + deps_text
+
+    depfile_contents = output_file + ': ' + deps_text
     self.MakeDirectoriesTo(depfile)
     outfile = self.fo_create(depfile, 'wb')
     outfile.writelines(depfile_contents)
diff --git a/grit/tool/build_unittest.py b/grit/tool/build_unittest.py
index fd640f7..debe4d4 100644
--- a/grit/tool/build_unittest.py
+++ b/grit/tool/build_unittest.py
@@ -49,15 +49,41 @@
     self.failUnless(os.path.isfile(expected_dep_file))
     with open(expected_dep_file) as f:
       line = f.readline()
-      (dep_file_name, deps_string) = line.split(': ')
+      (dep_output_file, deps_string) = line.split(': ')
       deps = deps_string.split(' ')
-      self.failUnlessEqual(os.path.abspath(expected_dep_file),
-          os.path.abspath(os.path.join(output_dir, dep_file_name)),
-          "depfile should refer to itself as the depended upon file")
+
+      self.failUnlessEqual("resource.h", dep_output_file)
       self.failUnlessEqual(1, len(deps))
       self.failUnlessEqual(deps[0],
           util.PathFromRoot('grit/testdata/substitute.xmb'))
 
+  def testAssertOutputs(self):
+    output_dir = tempfile.mkdtemp()
+    class DummyOpts(object):
+      def __init__(self):
+        self.input = util.PathFromRoot('grit/testdata/substitute.grd')
+        self.verbose = False
+        self.extra_verbose = False
+
+    # Incomplete output file list should fail.
+    builder_fail = build.RcBuilder()
+    self.failUnlessEqual(2,
+        builder_fail.Run(DummyOpts(), [
+            '-o', output_dir,
+            '-a', os.path.abspath(
+                os.path.join(output_dir, 'en_generated_resources.rc'))]))
+
+    # Complete output file list should succeed.
+    builder_ok = build.RcBuilder()
+    self.failUnlessEqual(0,
+        builder_ok.Run(DummyOpts(), [
+            '-o', output_dir,
+            '-a', os.path.abspath(
+                os.path.join(output_dir, 'en_generated_resources.rc')),
+            '-a', os.path.abspath(
+                os.path.join(output_dir, 'sv_generated_resources.rc')),
+            '-a', os.path.abspath(
+                os.path.join(output_dir, 'resource.h'))]))
 
 if __name__ == '__main__':
   unittest.main()