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"""Handling of the <include> element.
7"""
8
9import os
10
11import grit.format.html_inline
12import grit.format.rc_header
13import grit.format.rc
14
15from grit.node import base
16from grit import util
17
18class IncludeNode(base.Node):
19  """An <include> element."""
20  def __init__(self):
21    super(IncludeNode, self).__init__()
22
23    # Cache flattened data so that we don't flatten the same file
24    # multiple times.
25    self._flattened_data = None
26    # Also keep track of the last filename we flattened to, so we can
27    # avoid doing it more than once.
28    self._last_flat_filename = None
29
30  def _IsValidChild(self, child):
31    return False
32
33  def _GetFlattenedData(self, allow_external_script=False):
34    if not self._flattened_data:
35      filename = self.ToRealPath(self.GetInputPath())
36      self._flattened_data = (
37          grit.format.html_inline.InlineToString(filename, self,
38              allow_external_script=allow_external_script))
39    return self._flattened_data
40
41  def MandatoryAttributes(self):
42    return ['name', 'type', 'file']
43
44  def DefaultAttributes(self):
45    return {'translateable' : 'true',
46            'generateid': 'true',
47            'filenameonly': 'false',
48            'mkoutput': 'false',
49            'flattenhtml': 'false',
50            'allowexternalscript': 'false',
51            'relativepath': 'false',
52            'use_base_dir': 'true',
53           }
54
55  def GetInputPath(self):
56    # Do not mess with absolute paths, that would make them invalid.
57    if os.path.isabs(os.path.expandvars(self.attrs['file'])):
58      return self.attrs['file']
59
60    # We have no control over code that calles ToRealPath later, so convert
61    # the path to be relative against our basedir.
62    if self.attrs.get('use_base_dir', 'true') != 'true':
63      return os.path.relpath(self.attrs['file'], self.GetRoot().GetBaseDir())
64
65    return self.attrs['file']
66
67  def FileForLanguage(self, lang, output_dir):
68    """Returns the file for the specified language.  This allows us to return
69    different files for different language variants of the include file.
70    """
71    return self.ToRealPath(self.GetInputPath())
72
73  def GetDataPackPair(self, lang, encoding):
74    """Returns a (id, string) pair that represents the resource id and raw
75    bytes of the data.  This is used to generate the data pack data file.
76    """
77    # TODO(benrg/joi): Move this and other implementations of GetDataPackPair
78    # to grit.format.data_pack?
79    from grit.format import rc_header
80    id_map = rc_header.GetIds(self.GetRoot())
81    id = id_map[self.GetTextualIds()[0]]
82    if self.attrs['flattenhtml'] == 'true':
83      allow_external_script = self.attrs['allowexternalscript'] == 'true'
84      data = self._GetFlattenedData(allow_external_script=allow_external_script)
85    else:
86      filename = self.ToRealPath(self.GetInputPath())
87      data = util.ReadFile(filename, util.BINARY)
88
89    # Include does not care about the encoding, because it only returns binary
90    # data.
91    return id, data
92
93  def Process(self, output_dir):
94    """Rewrite file references to be base64 encoded data URLs.  The new file
95    will be written to output_dir and the name of the new file is returned."""
96    filename = self.ToRealPath(self.GetInputPath())
97    flat_filename = os.path.join(output_dir,
98        self.attrs['name'] + '_' + os.path.basename(filename))
99
100    if self._last_flat_filename == flat_filename:
101      return
102
103    with open(flat_filename, 'wb') as outfile:
104      outfile.write(self._GetFlattenedData())
105
106    self._last_flat_filename = flat_filename
107    return os.path.basename(flat_filename)
108
109  def GetHtmlResourceFilenames(self):
110    """Returns a set of all filenames inlined by this file."""
111    allow_external_script = self.attrs['allowexternalscript'] == 'true'
112    return grit.format.html_inline.GetResourceFilenames(
113         self.ToRealPath(self.GetInputPath()),
114         allow_external_script=allow_external_script)
115
116  def IsResourceMapSource(self):
117    return True
118
119  def GeneratesResourceMapEntry(self, output_all_resource_defines,
120                                is_active_descendant):
121    # includes always generate resource entries.
122    if output_all_resource_defines:
123      return True
124    return is_active_descendant
125
126  @staticmethod
127  def Construct(parent, name, type, file, translateable=True,
128                filenameonly=False, mkoutput=False, relativepath=False):
129    """Creates a new node which is a child of 'parent', with attributes set
130    by parameters of the same name.
131    """
132    # Convert types to appropriate strings
133    translateable = util.BoolToString(translateable)
134    filenameonly = util.BoolToString(filenameonly)
135    mkoutput = util.BoolToString(mkoutput)
136    relativepath = util.BoolToString(relativepath)
137
138    node = IncludeNode()
139    node.StartParsing('include', parent)
140    node.HandleAttribute('name', name)
141    node.HandleAttribute('type', type)
142    node.HandleAttribute('file', file)
143    node.HandleAttribute('translateable', translateable)
144    node.HandleAttribute('filenameonly', filenameonly)
145    node.HandleAttribute('mkoutput', mkoutput)
146    node.HandleAttribute('relativepath', relativepath)
147    node.EndParsing()
148    return node
149