3 # Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above
10 # copyright notice, this list of conditions and the following
12 # 2. Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials
15 # provided with the distribution.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
18 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 from webkitpy.common.host import Host
34 from webkitpy.common.webkit_finder import WebKitFinder
35 from HTMLParser import HTMLParser
38 _log = logging.getLogger(__name__)
41 def convert_for_webkit(new_path, filename, reference_support_info, host=Host()):
42 """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit.
44 Returns the list of modified properties and the modified text if the file was modifed, None otherwise."""
45 contents = host.filesystem.read_binary_file(filename)
46 converter = _W3CTestConverter(new_path, filename, reference_support_info, host)
47 if filename.endswith('.css'):
48 return converter.add_webkit_prefix_to_unprefixed_properties(contents)
50 converter.feed(contents)
52 return converter.output()
55 class _W3CTestConverter(HTMLParser):
56 def __init__(self, new_path, filename, reference_support_info, host=Host()):
57 HTMLParser.__init__(self)
60 self._filesystem = self._host.filesystem
61 self._webkit_root = WebKitFinder(self._filesystem).webkit_base()
63 self.converted_data = []
64 self.converted_properties = []
65 self.in_style_tag = False
67 self.filename = filename
68 self.reference_support_info = reference_support_info
70 resources_path = self.path_from_webkit_root('LayoutTests', 'resources')
71 resources_relpath = self._filesystem.relpath(resources_path, new_path)
72 self.resources_relpath = resources_relpath
74 # These settings might vary between WebKit and Blink
75 self._css_property_file = self.path_from_webkit_root('Source', 'core', 'css', 'CSSProperties.in')
77 self.prefixed_properties = self.read_webkit_prefixed_css_property_list()
79 self.prefixed_properties = self.read_webkit_prefixed_css_property_list()
80 prop_regex = '([\s{]|^)(' + "|".join(prop.replace('-webkit-', '') for prop in self.prefixed_properties) + ')(\s+:|:)'
81 self.prop_re = re.compile(prop_regex)
84 return (self.converted_properties, ''.join(self.converted_data))
86 def path_from_webkit_root(self, *comps):
87 return self._filesystem.abspath(self._filesystem.join(self._webkit_root, *comps))
89 def read_webkit_prefixed_css_property_list(self):
90 prefixed_properties = []
91 unprefixed_properties = set()
93 contents = self._filesystem.read_text_file(self._css_property_file)
94 for line in contents.splitlines():
95 if re.match('^(#|//|$)', line):
96 # skip comments and preprocessor directives
98 prop = line.split()[0]
99 # Find properties starting with the -webkit- prefix.
100 match = re.match('-webkit-([\w|-]*)', prop)
102 prefixed_properties.append(match.group(1))
104 unprefixed_properties.add(prop.strip())
106 # Ignore any prefixed properties for which an unprefixed version is supported
107 return [prop for prop in prefixed_properties if prop not in unprefixed_properties]
109 def add_webkit_prefix_to_unprefixed_properties(self, text):
110 """ Searches |text| for instances of properties requiring the -webkit- prefix and adds the prefix to them.
112 Returns the list of converted properties and the modified text."""
114 converted_properties = set()
117 for m in self.prop_re.finditer(text):
118 text_chunks.extend([text[cur_pos:m.start()], m.group(1), '-webkit-', m.group(2), m.group(3)])
119 converted_properties.add(m.group(2))
121 text_chunks.append(text[cur_pos:])
123 for prop in converted_properties:
124 _log.info(' converting %s', prop)
126 # FIXME: Handle the JS versions of these properties and GetComputedStyle, too.
127 return (converted_properties, ''.join(text_chunks))
129 def convert_reference_relpaths(self, text):
130 """ Searches |text| for instances of files in reference_support_info and updates the relative path to be correct for the new ref file location"""
133 for path in self.reference_support_info['files']:
134 if text.find(path) != -1:
135 # FIXME: This doesn't handle an edge case where simply removing the relative path doesn't work.
136 # See crbug.com/421584 for details.
137 new_path = re.sub(self.reference_support_info['reference_relpath'], '', path, 1)
138 converted = re.sub(path, new_path, text)
142 def convert_style_data(self, data):
143 converted = self.add_webkit_prefix_to_unprefixed_properties(data)
145 self.converted_properties.extend(list(converted[0]))
147 if self.reference_support_info is None or self.reference_support_info == {}:
150 return self.convert_reference_relpaths(converted[1])
152 def convert_attributes_if_needed(self, tag, attrs):
153 converted = self.get_starttag_text()
154 if tag in ('script', 'link'):
158 for attr_name, attr_value in attrs:
159 if attr_name == target_attr:
160 new_path = re.sub('/resources/testharness',
161 self.resources_relpath + '/testharness',
163 converted = re.sub(re.escape(attr_value), new_path, converted)
164 new_path = re.sub('/common/vendor-prefix',
165 self.resources_relpath + '/vendor-prefix',
167 converted = re.sub(re.escape(attr_value), new_path, converted)
169 for attr_name, attr_value in attrs:
170 if attr_name == 'style':
171 new_style = self.convert_style_data(attr_value)
172 converted = re.sub(re.escape(attr_value), new_style, converted)
173 if attr_name == 'class' and 'instructions' in attr_value:
174 # Always hide instructions, they're for manual testers.
175 converted = re.sub(' style=".*?"', '', converted)
176 converted = re.sub('\>', ' style="display:none">', converted)
178 src_tags = ('script', 'img', 'style', 'frame', 'iframe', 'input', 'layer', 'textarea', 'video', 'audio')
179 if tag in src_tags and self.reference_support_info is not None and self.reference_support_info != {}:
180 for attr_name, attr_value in attrs:
181 if attr_name == 'src':
182 new_path = self.convert_reference_relpaths(attr_value)
183 converted = re.sub(re.escape(attr_value), new_path, converted)
185 self.converted_data.append(converted)
187 def handle_starttag(self, tag, attrs):
189 self.in_style_tag = True
190 self.convert_attributes_if_needed(tag, attrs)
192 def handle_endtag(self, tag):
194 self.converted_data.append(self.convert_style_data(''.join(self.style_data)))
195 self.in_style_tag = False
197 self.converted_data.extend(['</', tag, '>'])
199 def handle_startendtag(self, tag, attrs):
200 self.convert_attributes_if_needed(tag, attrs)
202 def handle_data(self, data):
203 if self.in_style_tag:
204 self.style_data.append(data)
206 self.converted_data.append(data)
208 def handle_entityref(self, name):
209 self.converted_data.extend(['&', name, ';'])
211 def handle_charref(self, name):
212 self.converted_data.extend(['&#', name, ';'])
214 def handle_comment(self, data):
215 self.converted_data.extend(['<!-- ', data, ' -->'])
217 def handle_decl(self, decl):
218 self.converted_data.extend(['<!', decl, '>'])
220 def handle_pi(self, data):
221 self.converted_data.extend(['<?', data, '>'])