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