| # Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # |
| # 1. Redistributions of source code must retain the above |
| # copyright notice, this list of conditions and the following |
| # disclaimer. |
| # 2. Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following |
| # disclaimer in the documentation and/or other materials |
| # provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY |
| # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
| # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
| # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
| # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| # SUCH DAMAGE. |
| |
| import logging |
| import re |
| |
| from webkitpy.common.host import Host |
| from webkitpy.common.webkit_finder import WebKitFinder |
| from HTMLParser import HTMLParser |
| |
| |
| _log = logging.getLogger(__name__) |
| |
| |
| def convert_for_webkit(new_path, filename, reference_support_info, host=Host()): |
| """Converts a file's contents so the Blink layout test runner can run it. |
| |
| Args: |
| new_path: Absolute path where file will be copied to in the Chromium repo. |
| filename: Absolute path to where the file is. |
| reference_support_info: Dict of information about a related reference HTML, if any. |
| |
| Returns: |
| A pair of (list of modified CSS properties, modified text). |
| """ |
| # Conversion is not necessary for any tests in wpt now; see http://crbug.com/654081. |
| contents = host.filesystem.read_binary_file(filename) |
| try: |
| contents = contents.decode('utf-8') |
| except UnicodeDecodeError: |
| contents = contents.decode('utf-16') |
| |
| converter = _W3CTestConverter(new_path, filename, reference_support_info, host) |
| if filename.endswith('.css'): |
| return converter.add_webkit_prefix_to_unprefixed_properties(contents) |
| converter.feed(contents) |
| converter.close() |
| return converter.output() |
| |
| |
| class _W3CTestConverter(HTMLParser): |
| """A HTMLParser subclass which converts a HTML file as it is parsed. |
| |
| After the feed() method is called, the converted document will be stored |
| in converted_data, and can be retrieved with the output() method. |
| """ |
| |
| def __init__(self, new_path, filename, reference_support_info, host=Host()): |
| HTMLParser.__init__(self) |
| |
| self._host = host |
| self._filesystem = self._host.filesystem |
| self._webkit_root = WebKitFinder(self._filesystem).webkit_base() |
| |
| self.converted_data = [] |
| self.converted_properties = [] |
| self.in_style_tag = False |
| self.style_data = [] |
| self.filename = filename |
| self.reference_support_info = reference_support_info |
| resources_path = self.path_from_webkit_root('LayoutTests', 'resources') |
| resources_relpath = self._filesystem.relpath(resources_path, new_path) |
| self.resources_relpath = resources_relpath |
| |
| # These settings might vary between WebKit and Blink. |
| # Only -webkit-text-emphasis is currently needed. See: |
| # https://bugs.chromium.org/p/chromium/issues/detail?id=614955#c1 |
| self.prefixed_properties = [ |
| '-webkit-text-emphasis', |
| '-webkit-text-emphasis-color', |
| '-webkit-text-emphasis-position', |
| '-webkit-text-emphasis-style', |
| ] |
| prop_regex = r'([\s{]|^)(' + '|'.join( |
| prop.replace('-webkit-', '') for prop in self.prefixed_properties) + r')(\s+:|:)' |
| self.prop_re = re.compile(prop_regex) |
| |
| def output(self): |
| return (self.converted_properties, ''.join(self.converted_data)) |
| |
| def path_from_webkit_root(self, *comps): |
| return self._filesystem.abspath(self._filesystem.join(self._webkit_root, *comps)) |
| |
| def add_webkit_prefix_to_unprefixed_properties(self, text): |
| """Searches |text| for instances of properties requiring the -webkit- prefix and adds the prefix to them. |
| |
| Returns the list of converted properties and the modified text. |
| """ |
| converted_properties = set() |
| text_chunks = [] |
| cur_pos = 0 |
| for match in self.prop_re.finditer(text): |
| text_chunks.extend([ |
| text[cur_pos:match.start()], |
| match.group(1), '-webkit-', |
| match.group(2), |
| match.group(3) |
| ]) |
| converted_properties.add(match.group(2)) |
| cur_pos = match.end() |
| text_chunks.append(text[cur_pos:]) |
| |
| for prop in converted_properties: |
| _log.info(' converting %s', prop) |
| |
| # FIXME: Handle the JS versions of these properties and GetComputedStyle, too. |
| return (converted_properties, ''.join(text_chunks)) |
| |
| def convert_reference_relpaths(self, text): |
| """Converts reference file paths found in the given text. |
| |
| Searches |text| for instances of files in |self.reference_support_info| and |
| updates the relative path to be correct for the new ref file location. |
| """ |
| converted = text |
| for path in self.reference_support_info['files']: |
| if path in text: |
| # FIXME: This doesn't handle an edge case where simply removing the relative path doesn't work. |
| # See crbug.com/421584 for details. |
| new_path = re.sub(self.reference_support_info['reference_relpath'], '', path, 1) |
| converted = re.sub(path, new_path, text) |
| |
| return converted |
| |
| def convert_style_data(self, data): |
| converted = self.add_webkit_prefix_to_unprefixed_properties(data) |
| if converted[0]: |
| self.converted_properties.extend(list(converted[0])) |
| |
| if self.reference_support_info is None or self.reference_support_info == {}: |
| return converted[1] |
| |
| return self.convert_reference_relpaths(converted[1]) |
| |
| def convert_attributes_if_needed(self, tag, attrs): |
| """Converts attributes in a start tag in HTML. |
| |
| The converted tag text is appended to |self.converted_data|. |
| """ |
| converted = self.get_starttag_text() |
| for attr_name, attr_value in attrs: |
| if attr_name == 'style': |
| new_style = self.convert_style_data(attr_value) |
| converted = re.sub(re.escape(attr_value), new_style, converted) |
| |
| src_tags = ('script', 'img', 'style', 'frame', 'iframe', 'input', 'layer', 'textarea', 'video', 'audio') |
| if tag in src_tags and self.reference_support_info is not None and self.reference_support_info != {}: |
| for attr_name, attr_value in attrs: |
| if attr_name == 'src': |
| new_path = self.convert_reference_relpaths(attr_value) |
| converted = re.sub(re.escape(attr_value), new_path, converted) |
| |
| self.converted_data.append(converted) |
| |
| def parse_endtag(self, i): |
| # parse_endtag is being overridden here instead of handle_endtag |
| # so we can get the original end tag text with the original |
| # capitalization |
| endpos = HTMLParser.parse_endtag(self, i) |
| self.converted_data.extend([self.rawdata[i:endpos]]) |
| return endpos |
| |
| def handle_starttag(self, tag, attrs): |
| if tag == 'style': |
| self.in_style_tag = True |
| self.convert_attributes_if_needed(tag, attrs) |
| |
| def handle_endtag(self, tag): |
| if tag == 'style': |
| self.converted_data.append(self.convert_style_data(''.join(self.style_data))) |
| self.in_style_tag = False |
| self.style_data = [] |
| |
| def handle_startendtag(self, tag, attrs): |
| self.convert_attributes_if_needed(tag, attrs) |
| |
| def handle_data(self, data): |
| if self.in_style_tag: |
| self.style_data.append(data) |
| else: |
| self.converted_data.append(data) |
| |
| def handle_entityref(self, name): |
| self.converted_data.extend(['&', name, ';']) |
| |
| def handle_charref(self, name): |
| self.converted_data.extend(['&#', name, ';']) |
| |
| def handle_comment(self, data): |
| self.converted_data.extend(['<!--', data, '-->']) |
| |
| def handle_decl(self, decl): |
| self.converted_data.extend(['<!', decl, '>']) |
| |
| def handle_pi(self, data): |
| self.converted_data.extend(['<?', data, '>']) |