blob: 235fd92ffd93af1d59d8dd0080a92e21244112c3 [file] [log] [blame]
# 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, '>'])