• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6
7import json
8from xml.dom import minidom
9from grit import lazy_re
10from grit.format.policy_templates.writers import xml_formatted_writer
11
12
13def GetWriter(config):
14  '''Factory method for creating DocWriter objects.
15  See the constructor of TemplateWriter for description of
16  arguments.
17  '''
18  return DocWriter(['*'], config)
19
20
21class DocWriter(xml_formatted_writer.XMLFormattedWriter):
22  '''Class for generating policy templates in HTML format.
23  The intended use of the generated file is to upload it on
24  http://dev.chromium.org, therefore its format has some limitations:
25  - No HTML and body tags.
26  - Restricted set of element attributes: for example no 'class'.
27  Because of the latter the output is styled using the 'style'
28  attributes of HTML elements. This is supported by the dictionary
29  self._STYLES[] and the method self._AddStyledElement(), they try
30  to mimic the functionality of CSS classes. (But without inheritance.)
31
32  This class is invoked by PolicyTemplateGenerator to create the HTML
33  files.
34  '''
35
36  def _GetLocalizedMessage(self, msg_id):
37    '''Returns a localized message for this writer.
38
39    Args:
40      msg_id: The identifier of the message.
41
42    Returns:
43      The localized message.
44    '''
45    return self.messages['doc_' + msg_id]['text']
46
47  def _MapListToString(self, item_map, items):
48    '''Creates a comma-separated list.
49
50    Args:
51      item_map: A dictionary containing all the elements of 'items' as
52        keys.
53      items: A list of arbitrary items.
54
55    Returns:
56      Looks up each item of 'items' in 'item_maps' and concatenates the
57      resulting items into a comma-separated list.
58    '''
59    return ', '.join([item_map[x] for x in items])
60
61  def _AddTextWithLinks(self, parent, text):
62    '''Parse a string for URLs and add it to a DOM node with the URLs replaced
63    with <a> HTML links.
64
65    Args:
66      parent: The DOM node to which the text will be added.
67      text: The string to be added.
68    '''
69    # Iterate through all the URLs and replace them with links.
70    out = []
71    while True:
72      # Look for the first URL.
73      res = self._url_matcher.search(text)
74      if not res:
75        break
76      # Calculate positions of the substring of the URL.
77      url = res.group(0)
78      start = res.start(0)
79      end = res.end(0)
80      # Add the text prior to the URL.
81      self.AddText(parent, text[:start])
82      # Add a link for the URL.
83      self.AddElement(parent, 'a', {'href': url}, url)
84      # Drop the part of text that is added.
85      text = text[end:]
86    self.AddText(parent, text)
87
88
89  def _AddStyledElement(self, parent, name, style_ids, attrs=None, text=None):
90    '''Adds an XML element to a parent, with CSS style-sheets included.
91
92    Args:
93      parent: The parent DOM node.
94      name: Name of the element to add.
95      style_ids: A list of CSS style strings from self._STYLE[].
96      attrs: Dictionary of attributes for the element.
97      text: Text content for the element.
98    '''
99    if attrs == None:
100      attrs = {}
101
102    style = ''.join([self._STYLE[x] for x in style_ids])
103    if style != '':
104      # Apply the style specified by style_ids.
105      attrs['style'] = style + attrs.get('style', '')
106    return self.AddElement(parent, name, attrs, text)
107
108  def _AddDescription(self, parent, policy):
109    '''Adds a string containing the description of the policy. URLs are
110    replaced with links and the possible choices are enumerated in case
111    of 'string-enum' and 'int-enum' type policies.
112
113    Args:
114      parent: The DOM node for which the feature list will be added.
115      policy: The data structure of a policy.
116    '''
117    # Replace URLs with links in the description.
118    self._AddTextWithLinks(parent, policy['desc'])
119    # Add list of enum items.
120    if policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
121      ul = self.AddElement(parent, 'ul')
122      for item in policy['items']:
123        if policy['type'] == 'int-enum':
124          value_string = str(item['value'])
125        else:
126          value_string = '"%s"' % item['value']
127        self.AddElement(
128            ul, 'li', {}, '%s = %s' % (value_string, item['caption']))
129
130  def _AddFeatures(self, parent, policy):
131    '''Adds a string containing the list of supported features of a policy
132    to a DOM node. The text will look like as:
133      Feature_X: Yes, Feature_Y: No
134
135    Args:
136      parent: The DOM node for which the feature list will be added.
137      policy: The data structure of a policy.
138    '''
139    features = []
140    # The sorting is to make the order well-defined for testing.
141    keys = policy['features'].keys()
142    keys.sort()
143    for key in keys:
144      key_name = self._FEATURE_MAP[key]
145      if policy['features'][key]:
146        value_name = self._GetLocalizedMessage('supported')
147      else:
148        value_name = self._GetLocalizedMessage('not_supported')
149      features.append('%s: %s' % (key_name, value_name))
150    self.AddText(parent, ', '.join(features))
151
152  def _AddListExampleMac(self, parent, policy):
153    '''Adds an example value for Mac of a 'list' policy to a DOM node.
154
155    Args:
156      parent: The DOM node for which the example will be added.
157      policy: A policy of type 'list', for which the Mac example value
158        is generated.
159    '''
160    example_value = policy['example_value']
161    self.AddElement(parent, 'dt', {}, 'Mac:')
162    mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
163
164    mac_text = ['<array>']
165    for item in example_value:
166      mac_text.append('  <string>%s</string>' % item)
167    mac_text.append('</array>')
168    self.AddText(mac, '\n'.join(mac_text))
169
170  def _AddListExampleWindows(self, parent, policy):
171    '''Adds an example value for Windows of a 'list' policy to a DOM node.
172
173    Args:
174      parent: The DOM node for which the example will be added.
175      policy: A policy of type 'list', for which the Windows example value
176        is generated.
177    '''
178    example_value = policy['example_value']
179    self.AddElement(parent, 'dt', {}, 'Windows:')
180    win = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
181    win_text = []
182    cnt = 1
183    if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
184      key_name = self.config['win_reg_recommended_key_name']
185    else:
186      key_name = self.config['win_reg_mandatory_key_name']
187    for item in example_value:
188      win_text.append(
189          '%s\\%s\\%d = "%s"' %
190          (key_name, policy['name'], cnt, item))
191      cnt = cnt + 1
192    self.AddText(win, '\n'.join(win_text))
193
194  def _AddListExampleLinux(self, parent, policy):
195    '''Adds an example value for Linux of a 'list' policy to a DOM node.
196
197    Args:
198      parent: The DOM node for which the example will be added.
199      policy: A policy of type 'list', for which the Linux example value
200        is generated.
201    '''
202    example_value = policy['example_value']
203    self.AddElement(parent, 'dt', {}, 'Linux:')
204    linux = self._AddStyledElement(parent, 'dd', ['.monospace'])
205    linux_text = []
206    for item in example_value:
207      linux_text.append('"%s"' % item)
208    self.AddText(linux, '[%s]' % ', '.join(linux_text))
209
210  def _AddListExample(self, parent, policy):
211    '''Adds the example value of a 'list' policy to a DOM node. Example output:
212    <dl>
213      <dt>Windows:</dt>
214      <dd>
215        Software\Policies\Chromium\DisabledPlugins\0 = "Java"
216        Software\Policies\Chromium\DisabledPlugins\1 = "Shockwave Flash"
217      </dd>
218      <dt>Linux:</dt>
219      <dd>["Java", "Shockwave Flash"]</dd>
220      <dt>Mac:</dt>
221      <dd>
222        <array>
223          <string>Java</string>
224          <string>Shockwave Flash</string>
225        </array>
226      </dd>
227    </dl>
228
229    Args:
230      parent: The DOM node for which the example will be added.
231      policy: The data structure of a policy.
232    '''
233    examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
234    if self.IsPolicySupportedOnPlatform(policy, 'win'):
235      self._AddListExampleWindows(examples, policy)
236    if self.IsPolicySupportedOnPlatform(policy, 'linux'):
237      self._AddListExampleLinux(examples, policy)
238    if self.IsPolicySupportedOnPlatform(policy, 'mac'):
239      self._AddListExampleMac(examples, policy)
240
241  def _PythonObjectToPlist(self, obj, indent=''):
242    '''Converts a python object to an equivalent XML plist.
243
244    Returns a list of lines.'''
245    obj_type = type(obj)
246    if obj_type == bool:
247      return [ '%s<%s/>' % (indent, 'true' if obj else 'false') ]
248    elif obj_type == int:
249      return [ '%s<integer>%s</integer>' % (indent, obj) ]
250    elif obj_type == str:
251      return [ '%s<string>%s</string>' % (indent, obj) ]
252    elif obj_type == list:
253      result = [ '%s<array>' % indent ]
254      for item in obj:
255        result += self._PythonObjectToPlist(item, indent + '  ')
256      result.append('%s</array>' % indent)
257      return result
258    elif obj_type == dict:
259      result = [ '%s<dict>' % indent ]
260      for key in sorted(obj.keys()):
261        result.append('%s<key>%s</key>' % (indent + '  ', key))
262        result += self._PythonObjectToPlist(obj[key], indent + '  ')
263      result.append('%s</dict>' % indent)
264      return result
265    else:
266      raise Exception('Invalid object to convert: %s' % obj)
267
268  def _AddDictionaryExampleMac(self, parent, policy):
269    '''Adds an example value for Mac of a 'dict' policy to a DOM node.
270
271    Args:
272      parent: The DOM node for which the example will be added.
273      policy: A policy of type 'dict', for which the Mac example value
274        is generated.
275    '''
276    example_value = policy['example_value']
277    self.AddElement(parent, 'dt', {}, 'Mac:')
278    mac = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
279    mac_text = ['<key>%s</key>' % (policy['name'])]
280    mac_text += self._PythonObjectToPlist(example_value)
281    self.AddText(mac, '\n'.join(mac_text))
282
283  def _AddDictionaryExampleWindows(self, parent, policy):
284    '''Adds an example value for Windows of a 'dict' policy to a DOM node.
285
286    Args:
287      parent: The DOM node for which the example will be added.
288      policy: A policy of type 'dict', for which the Windows example value
289        is generated.
290    '''
291    self.AddElement(parent, 'dt', {}, 'Windows:')
292    win = self._AddStyledElement(parent, 'dd', ['.monospace', '.pre'])
293    if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
294      key_name = self.config['win_reg_recommended_key_name']
295    else:
296      key_name = self.config['win_reg_mandatory_key_name']
297    example = json.dumps(policy['example_value'])
298    self.AddText(win, '%s\\%s = %s' % (key_name, policy['name'], example))
299
300  def _AddDictionaryExampleLinux(self, parent, policy):
301    '''Adds an example value for Linux of a 'dict' policy to a DOM node.
302
303    Args:
304      parent: The DOM node for which the example will be added.
305      policy: A policy of type 'dict', for which the Linux example value
306        is generated.
307    '''
308    self.AddElement(parent, 'dt', {}, 'Linux:')
309    linux = self._AddStyledElement(parent, 'dd', ['.monospace'])
310    example = json.dumps(policy['example_value'])
311    self.AddText(linux, '%s: %s' % (policy['name'], example))
312
313  def _AddDictionaryExample(self, parent, policy):
314    '''Adds the example value of a 'dict' policy to a DOM node. Example output:
315    <dl>
316      <dt>Windows:</dt>
317      <dd>
318        Software\Policies\Chromium\ProxySettings = "{ 'ProxyMode': 'direct' }"
319      </dd>
320      <dt>Linux:</dt>
321      <dd>"ProxySettings": {
322        "ProxyMode": "direct"
323      }
324      </dd>
325      <dt>Mac:</dt>
326      <dd>
327        <key>ProxySettings</key>
328        <dict>
329          <key>ProxyMode</key>
330          <string>direct</string>
331        </dict>
332      </dd>
333    </dl>
334
335    Args:
336      parent: The DOM node for which the example will be added.
337      policy: The data structure of a policy.
338    '''
339    examples = self._AddStyledElement(parent, 'dl', ['dd dl'])
340    if self.IsPolicySupportedOnPlatform(policy, 'win'):
341      self._AddDictionaryExampleWindows(examples, policy)
342    if self.IsPolicySupportedOnPlatform(policy, 'linux'):
343      self._AddDictionaryExampleLinux(examples, policy)
344    if self.IsPolicySupportedOnPlatform(policy, 'mac'):
345      self._AddDictionaryExampleMac(examples, policy)
346
347  def _AddExample(self, parent, policy):
348    '''Adds the HTML DOM representation of the example value of a policy to
349    a DOM node. It is simple text for boolean policies, like
350    '0x00000001 (Windows), true (Linux), <true /> (Mac)' in case of boolean
351    policies, but it may also contain other HTML elements. (See method
352    _AddListExample.)
353
354    Args:
355      parent: The DOM node for which the example will be added.
356      policy: The data structure of a policy.
357
358    Raises:
359      Exception: If the type of the policy is unknown or the example value
360        of the policy is out of its expected range.
361    '''
362    example_value = policy['example_value']
363    policy_type = policy['type']
364    if policy_type == 'main':
365      pieces = []
366      if self.IsPolicySupportedOnPlatform(policy, 'win'):
367        value = '0x00000001' if example_value else '0x00000000'
368        pieces.append(value + ' (Windows)')
369      if self.IsPolicySupportedOnPlatform(policy, 'linux'):
370        value = 'true' if example_value else 'false'
371        pieces.append(value + ' (Linux)')
372      if self.IsPolicySupportedOnPlatform(policy, 'mac'):
373        value = '<true />' if example_value else '<false />'
374        pieces.append(value + ' (Mac)')
375      self.AddText(parent, ', '.join(pieces))
376    elif policy_type == 'string':
377      self.AddText(parent, '"%s"' % example_value)
378    elif policy_type in ('int', 'int-enum'):
379      pieces = []
380      if self.IsPolicySupportedOnPlatform(policy, 'win'):
381        pieces.append('0x%08x (Windows)' % example_value)
382      if self.IsPolicySupportedOnPlatform(policy, 'linux'):
383        pieces.append('%d (Linux)' % example_value)
384      if self.IsPolicySupportedOnPlatform(policy, 'mac'):
385        pieces.append('%d (Mac)' % example_value)
386      self.AddText(parent, ', '.join(pieces))
387    elif policy_type == 'string-enum':
388      self.AddText(parent, '"%s"' % (example_value))
389    elif policy_type in ('list', 'string-enum-list'):
390      self._AddListExample(parent, policy)
391    elif policy_type == 'dict':
392      self._AddDictionaryExample(parent, policy)
393    else:
394      raise Exception('Unknown policy type: ' + policy_type)
395
396  def _AddPolicyAttribute(self, dl, term_id,
397                          definition=None, definition_style=None):
398    '''Adds a term-definition pair to a HTML DOM <dl> node. This method is
399    used by _AddPolicyDetails. Its result will have the form of:
400      <dt style="...">...</dt>
401      <dd style="...">...</dd>
402
403    Args:
404      dl: The DOM node of the <dl> list.
405      term_id: A key to self._STRINGS[] which specifies the term of the pair.
406      definition: The text of the definition. (Optional.)
407      definition_style: List of references to values self._STYLE[] that specify
408        the CSS stylesheet of the <dd> (definition) element.
409
410    Returns:
411      The DOM node representing the definition <dd> element.
412    '''
413    # Avoid modifying the default value of definition_style.
414    if definition_style == None:
415      definition_style = []
416    term = self._GetLocalizedMessage(term_id)
417    self._AddStyledElement(dl, 'dt', ['dt'], {}, term)
418    return self._AddStyledElement(dl, 'dd', definition_style, {}, definition)
419
420  def _AddSupportedOnList(self, parent, supported_on_list):
421    '''Creates a HTML list containing the platforms, products and versions
422    that are specified in the list of supported_on.
423
424    Args:
425      parent: The DOM node for which the list will be added.
426      supported_on_list: The list of supported products, as a list of
427        dictionaries.
428    '''
429    ul = self._AddStyledElement(parent, 'ul', ['ul'])
430    for supported_on in supported_on_list:
431      text = []
432      product = supported_on['product']
433      platforms = supported_on['platforms']
434      text.append(self._PRODUCT_MAP[product])
435      text.append('(%s)' %
436                  self._MapListToString(self._PLATFORM_MAP, platforms))
437      if supported_on['since_version']:
438        since_version = self._GetLocalizedMessage('since_version')
439        text.append(since_version.replace('$6', supported_on['since_version']))
440      if supported_on['until_version']:
441        until_version = self._GetLocalizedMessage('until_version')
442        text.append(until_version.replace('$6', supported_on['until_version']))
443      # Add the list element:
444      self.AddElement(ul, 'li', {}, ' '.join(text))
445
446  def _AddPolicyDetails(self, parent, policy):
447    '''Adds the list of attributes of a policy to the HTML DOM node parent.
448    It will have the form:
449    <dl>
450      <dt>Attribute:</dt><dd>Description</dd>
451      ...
452    </dl>
453
454    Args:
455      parent: A DOM element for which the list will be added.
456      policy: The data structure of the policy.
457    '''
458
459    dl = self.AddElement(parent, 'dl')
460    data_type = self._TYPE_MAP[policy['type']]
461    if (self.IsPolicySupportedOnPlatform(policy, 'win') and
462        self._REG_TYPE_MAP.get(policy['type'], None)):
463      data_type += ' (%s)' % self._REG_TYPE_MAP[policy['type']]
464    self._AddPolicyAttribute(dl, 'data_type', data_type)
465    if policy['type'] != 'external':
466      # All types except 'external' can be set through platform policy.
467      if self.IsPolicySupportedOnPlatform(policy, 'win'):
468        if self.CanBeRecommended(policy) and not self.CanBeMandatory(policy):
469          key_name = self.config['win_reg_recommended_key_name']
470        else:
471          key_name = self.config['win_reg_mandatory_key_name']
472        self._AddPolicyAttribute(
473            dl,
474            'win_reg_loc',
475            key_name + '\\' + policy['name'],
476            ['.monospace'])
477      if (self.IsPolicySupportedOnPlatform(policy, 'linux') or
478          self.IsPolicySupportedOnPlatform(policy, 'mac')):
479        self._AddPolicyAttribute(
480            dl,
481            'mac_linux_pref_name',
482            policy['name'],
483            ['.monospace'])
484    dd = self._AddPolicyAttribute(dl, 'supported_on')
485    self._AddSupportedOnList(dd, policy['supported_on'])
486    dd = self._AddPolicyAttribute(dl, 'supported_features')
487    self._AddFeatures(dd, policy)
488    dd = self._AddPolicyAttribute(dl, 'description')
489    self._AddDescription(dd, policy)
490    if (self.IsPolicySupportedOnPlatform(policy, 'win') or
491        self.IsPolicySupportedOnPlatform(policy, 'linux') or
492        self.IsPolicySupportedOnPlatform(policy, 'mac')):
493      # Don't add an example for ChromeOS-only policies.
494      if policy['type'] != 'external':
495        # All types except 'external' can be set through platform policy.
496        dd = self._AddPolicyAttribute(dl, 'example_value')
497        self._AddExample(dd, policy)
498
499  def _AddPolicyNote(self, parent, policy):
500    '''If a policy has an additional web page assigned with it, then add
501    a link for that page.
502
503    Args:
504      policy: The data structure of the policy.
505    '''
506    if 'problem_href' not in policy:
507      return
508    problem_href = policy['problem_href']
509    div = self._AddStyledElement(parent, 'div', ['div.note'])
510    note = self._GetLocalizedMessage('note').replace('$6', problem_href)
511    self._AddTextWithLinks(div, note)
512
513  def _AddPolicyRow(self, parent, policy):
514    '''Adds a row for the policy in the summary table.
515
516    Args:
517      parent: The DOM node of the summary table.
518      policy: The data structure of the policy.
519    '''
520    tr = self._AddStyledElement(parent, 'tr', ['tr'])
521    indent = 'padding-left: %dpx;' % (7 + self._indent_level * 14)
522    if policy['type'] != 'group':
523      # Normal policies get two columns with name and caption.
524      name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'],
525                                       {'style': indent})
526      self.AddElement(name_td, 'a',
527                      {'href': '#' + policy['name']}, policy['name'])
528      self._AddStyledElement(tr, 'td', ['td', 'td.right'], {},
529                             policy['caption'])
530    else:
531      # Groups get one column with caption.
532      name_td = self._AddStyledElement(tr, 'td', ['td', 'td.left'],
533                                       {'style': indent, 'colspan': '2'})
534      self.AddElement(name_td, 'a', {'href': '#' + policy['name']},
535                      policy['caption'])
536
537  def _AddPolicySection(self, parent, policy):
538    '''Adds a section about the policy in the detailed policy listing.
539
540    Args:
541      parent: The DOM node of the <div> of the detailed policy list.
542      policy: The data structure of the policy.
543    '''
544    # Set style according to group nesting level.
545    indent = 'margin-left: %dpx' % (self._indent_level * 28)
546    if policy['type'] == 'group':
547      heading = 'h2'
548    else:
549      heading = 'h3'
550    parent2 = self.AddElement(parent, 'div', {'style': indent})
551
552    h2 = self.AddElement(parent2, heading)
553    self.AddElement(h2, 'a', {'name': policy['name']})
554    if policy['type'] != 'group':
555      # Normal policies get a full description.
556      policy_name_text = policy['name']
557      if 'deprecated' in policy and policy['deprecated'] == True:
558        policy_name_text += " ("
559        policy_name_text += self._GetLocalizedMessage('deprecated') + ")"
560      self.AddText(h2, policy_name_text)
561      self.AddElement(parent2, 'span', {}, policy['caption'])
562      self._AddPolicyNote(parent2, policy)
563      self._AddPolicyDetails(parent2, policy)
564    else:
565      # Groups get a more compact description.
566      self.AddText(h2, policy['caption'])
567      self._AddStyledElement(parent2, 'div', ['div.group_desc'],
568                             {}, policy['desc'])
569    self.AddElement(
570        parent2, 'a', {'href': '#top'},
571        self._GetLocalizedMessage('back_to_top'))
572
573  #
574  # Implementation of abstract methods of TemplateWriter:
575  #
576
577  def IsDeprecatedPolicySupported(self, policy):
578    return True
579
580  def WritePolicy(self, policy):
581    self._AddPolicyRow(self._summary_tbody, policy)
582    self._AddPolicySection(self._details_div, policy)
583
584  def BeginPolicyGroup(self, group):
585    self.WritePolicy(group)
586    self._indent_level += 1
587
588  def EndPolicyGroup(self):
589    self._indent_level -= 1
590
591  def BeginTemplate(self):
592    # Add a <div> for the summary section.
593    summary_div = self.AddElement(self._main_div, 'div')
594    self.AddElement(summary_div, 'a', {'name': 'top'})
595    self.AddElement(summary_div, 'br')
596    self._AddTextWithLinks(
597        summary_div,
598        self._GetLocalizedMessage('intro'))
599    self.AddElement(summary_div, 'br')
600    self.AddElement(summary_div, 'br')
601    self.AddElement(summary_div, 'br')
602    # Add the summary table of policies.
603    summary_table = self._AddStyledElement(summary_div, 'table', ['table'])
604    # Add the first row.
605    thead = self.AddElement(summary_table, 'thead')
606    tr = self._AddStyledElement(thead, 'tr', ['tr'])
607    self._AddStyledElement(
608        tr, 'td', ['td', 'td.left', 'thead td'], {},
609        self._GetLocalizedMessage('name_column_title'))
610    self._AddStyledElement(
611        tr, 'td', ['td', 'td.right', 'thead td'], {},
612        self._GetLocalizedMessage('description_column_title'))
613    self._summary_tbody = self.AddElement(summary_table, 'tbody')
614
615    # Add a <div> for the detailed policy listing.
616    self._details_div = self.AddElement(self._main_div, 'div')
617
618  def Init(self):
619    dom_impl = minidom.getDOMImplementation('')
620    self._doc = dom_impl.createDocument(None, 'html', None)
621    body = self.AddElement(self._doc.documentElement, 'body')
622    self._main_div = self.AddElement(body, 'div')
623    self._indent_level = 0
624
625    # Human-readable names of supported platforms.
626    self._PLATFORM_MAP = {
627      'win': 'Windows',
628      'mac': 'Mac',
629      'linux': 'Linux',
630      'chrome_os': self.config['os_name'],
631      'android': 'Android',
632      'ios': 'iOS',
633    }
634    # Human-readable names of supported products.
635    self._PRODUCT_MAP = {
636      'chrome': self.config['app_name'],
637      'chrome_frame': self.config['frame_name'],
638      'chrome_os': self.config['os_name'],
639    }
640    # Human-readable names of supported features. Each supported feature has
641    # a 'doc_feature_X' entry in |self.messages|.
642    self._FEATURE_MAP = {}
643    for message in self.messages:
644      if message.startswith('doc_feature_'):
645        self._FEATURE_MAP[message[12:]] = self.messages[message]['text']
646    # Human-readable names of types.
647    self._TYPE_MAP = {
648      'string': 'String',
649      'int': 'Integer',
650      'main': 'Boolean',
651      'int-enum': 'Integer',
652      'string-enum': 'String',
653      'list': 'List of strings',
654      'string-enum-list': 'List of strings',
655      'dict': 'Dictionary',
656      'external': 'External data reference',
657    }
658    reg_dict = 'REG_SZ; %s' % self._GetLocalizedMessage(
659        'complex_policies_on_windows')
660    self._REG_TYPE_MAP = {
661      'string': 'REG_SZ',
662      'int': 'REG_DWORD',
663      'main': 'REG_DWORD',
664      'int-enum': 'REG_DWORD',
665      'string-enum': 'REG_SZ',
666      'dict': reg_dict,
667    }
668    # The CSS style-sheet used for the document. It will be used in Google
669    # Sites, which strips class attributes from HTML tags. To work around this,
670    # the style-sheet is a dictionary and the style attributes will be added
671    # "by hand" for each element.
672    self._STYLE = {
673      'table': 'border-style: none; border-collapse: collapse;',
674      'tr': 'height: 0px;',
675      'td': 'border: 1px dotted rgb(170, 170, 170); padding: 7px; '
676          'vertical-align: top; width: 236px; height: 15px;',
677      'thead td': 'font-weight: bold;',
678      'td.left': 'width: 200px;',
679      'td.right': 'width: 100%;',
680      'dt': 'font-weight: bold;',
681      'dd dl': 'margin-top: 0px; margin-bottom: 0px;',
682      '.monospace': 'font-family: monospace;',
683      '.pre': 'white-space: pre;',
684      'div.note': 'border: 2px solid black; padding: 5px; margin: 5px;',
685      'div.group_desc': 'margin-top: 20px; margin-bottom: 20px;',
686      'ul': 'padding-left: 0px; margin-left: 0px;'
687    }
688
689    # A simple regexp to search for URLs. It is enough for now.
690    self._url_matcher = lazy_re.compile('(http://[^\\s]*[^\\s\\.])')
691
692  def GetTemplateText(self):
693    # Return the text representation of the main <div> tag.
694    return self._main_div.toxml()
695    # To get a complete HTML file, use the following.
696    # return self._doc.toxml()
697