1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import re
7
8from py_vulcanize import js_utils
9from py_vulcanize import module
10from py_vulcanize import parse_html_deps
11from py_vulcanize import style_sheet
12
13
14def IsHTMLResourceTheModuleGivenConflictingResourceNames(
15    js_resource, html_resource):  # pylint: disable=unused-argument
16  return 'polymer-element' in html_resource.contents
17
18
19class HTMLModule(module.Module):
20
21  @property
22  def _module_dir_name(self):
23    return os.path.dirname(self.resource.absolute_path)
24
25  def Parse(self):
26    try:
27      parser_results = parse_html_deps.HTMLModuleParser().Parse(self.contents)
28    except Exception as ex:
29      raise Exception('While parsing %s: %s' % (self.name, str(ex)))
30
31    self.dependency_metadata = Parse(self.loader,
32                                     self.name,
33                                     self._module_dir_name,
34                                     self.IsThirdPartyComponent(),
35                                     parser_results)
36    self._parser_results = parser_results
37
38  def Load(self):
39    super(HTMLModule, self).Load()
40
41    reachable_names = set([m.name
42                           for m in self.all_dependent_modules_recursive])
43    if 'tr.exportTo' in self.contents:
44      if 'tracing.base.base' not in reachable_names:
45        raise Exception('%s: Does not have a dependency on base' %
46                        os.path.relpath(self.resource.absolute_path))
47
48  def GetTVCMDepsModuleType(self):
49    return 'py_vulcanize.HTML_MODULE_TYPE'
50
51  def AppendJSContentsToFile(self,
52                             f,
53                             use_include_tags_for_scripts,
54                             dir_for_include_tag_root):
55    super(HTMLModule, self).AppendJSContentsToFile(f,
56                                                   use_include_tags_for_scripts,
57                                                   dir_for_include_tag_root)
58    for inline_script in self._parser_results.inline_scripts:
59      if not HasPolymerCall(inline_script.stripped_contents):
60        js = inline_script.contents
61      else:
62        js = GetInlineScriptContentWithPolymerizingApplied(inline_script)
63
64      js = js_utils.EscapeJSIfNeeded(js)
65
66      f.write(js)
67      f.write('\n')
68
69  def AppendHTMLContentsToFile(self, f, ctl, minify=False):
70    super(HTMLModule, self).AppendHTMLContentsToFile(f, ctl)
71
72    ctl.current_module = self
73    try:
74      for piece in self._parser_results.YieldHTMLInPieces(ctl, minify=minify):
75        f.write(piece)
76    finally:
77      ctl.current_module = None
78
79  def HRefToResource(self, href, tag_for_err_msg):
80    return _HRefToResource(self.loader, self.name, self._module_dir_name,
81                           href, tag_for_err_msg)
82
83  def AppendDirectlyDependentFilenamesTo(
84      self, dependent_filenames, include_raw_scripts=True):
85    super(HTMLModule, self).AppendDirectlyDependentFilenamesTo(
86        dependent_filenames, include_raw_scripts)
87    for contents in self._parser_results.inline_stylesheets:
88      module_dirname = os.path.dirname(self.resource.absolute_path)
89      ss = style_sheet.ParsedStyleSheet(
90          self.loader, module_dirname, contents)
91      ss.AppendDirectlyDependentFilenamesTo(dependent_filenames)
92
93
94def GetInlineScriptContentWithPolymerizingApplied(inline_script):
95  polymer_element_name = GetPolymerElementNameFromOpenTags(
96      inline_script.open_tags)
97  if polymer_element_name is None:
98    raise module.DepsException(
99        'Tagless Polymer() call must be made inside a <polymer-element> tag')
100
101  return UpdatePolymerCallsGivenElementName(
102      inline_script.stripped_contents, polymer_element_name)
103
104
105def GetPolymerElementNameFromOpenTags(open_tags):
106  found_tag = None
107  for tag in reversed(open_tags):
108    if tag.tag == 'polymer-element':
109      found_tag = tag
110      break
111
112  if not found_tag:
113    return None
114
115  return found_tag.attrs.get('name', None)
116
117_POLYMER_RE_1 = 'Polymer(\s*?)\((\s*?)\{'
118_POLYMER_RE_2 = 'Polymer(\s*?)\((\s*?)\)'
119
120
121def HasPolymerCall(js):
122  if re.search(_POLYMER_RE_1, js) is not None:
123    return True
124  if re.search(_POLYMER_RE_2, js) is not None:
125    return True
126  return False
127
128
129def UpdatePolymerCallsGivenElementName(js, polymer_element_name):
130  if re.search(_POLYMER_RE_1, js) is not None:
131    return re.sub(_POLYMER_RE_1,
132                  'Polymer\g<1>(\g<2>\'%s\', {' % polymer_element_name,
133                  js, 0, re.DOTALL)
134  if re.search(_POLYMER_RE_2, js) is not None:
135    return re.sub(_POLYMER_RE_2,
136                  'Polymer\g<1>(\g<2>\'%s\')' % polymer_element_name,
137                  js, 0, re.DOTALL)
138  assert False, 'This should never be reached'
139
140
141def _HRefToResource(
142    loader, module_name, module_dir_name, href, tag_for_err_msg):
143  if href[0] == '/':
144    resource = loader.FindResourceGivenRelativePath(
145        os.path.normpath(href[1:]))
146  else:
147    abspath = os.path.normpath(os.path.join(module_dir_name,
148                                            os.path.normpath(href)))
149    resource = loader.FindResourceGivenAbsolutePath(abspath)
150
151  if not resource:
152    raise module.DepsException(
153        'In %s, the %s cannot be loaded because '
154        'it is not in the search path' % (module_name, tag_for_err_msg))
155  try:
156    resource.contents
157  except:
158    raise module.DepsException('In %s, %s points at a nonexistent file ' % (
159        module_name, tag_for_err_msg))
160  return resource
161
162
163def Parse(loader, module_name, module_dir_name, is_component, parser_results):
164  res = module.ModuleDependencyMetadata()
165  if is_component:
166    return res
167
168  # External script references.
169  for href in parser_results.scripts_external:
170    resource = _HRefToResource(loader, module_name, module_dir_name,
171                               href,
172                               tag_for_err_msg='<script src="%s">' % href)
173    res.dependent_raw_script_relative_paths.append(
174        resource.unix_style_relative_path)
175
176  # External imports. Mostly the same as <script>, but we know its a module.
177  for href in parser_results.imports:
178    if not href.endswith('.html'):
179      raise Exception(
180          'In %s, the <link rel="import" href="%s"> must point at a '
181          'file with an html suffix' % (module_name, href))
182
183    resource = _HRefToResource(
184        loader, module_name, module_dir_name, href,
185        tag_for_err_msg='<link rel="import" href="%s">' % href)
186    res.dependent_module_names.append(resource.name)
187
188  # Style sheets.
189  for href in parser_results.stylesheets:
190    resource = _HRefToResource(
191        loader, module_name, module_dir_name, href,
192        tag_for_err_msg='<link rel="stylesheet" href="%s">' % href)
193    res.style_sheet_names.append(resource.name)
194
195  return res
196