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