Second pass
This commit is contained in:
Родитель
ac8e0163f0
Коммит
b38db0cac7
|
@ -5,7 +5,6 @@
|
|||
.coverage
|
||||
.figleaf
|
||||
*.swp
|
||||
/validator/constants_local.py
|
||||
/appvalidator/constants_local.py
|
||||
/src
|
||||
/extras/jslibs
|
||||
/extras/language_controls
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
{
|
||||
"1":{"name":"Firefox",
|
||||
"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
|
||||
"versions":
|
||||
["0.3", "0.6", "0.7", "0.7+", "0.8", "0.8+", "0.9.x", "0.9", "0.9.0+",
|
||||
"0.9.1+", "0.9.2+", "0.9.3", "0.9.3+", "0.9+", "0.10", "0.10.1",
|
||||
"0.10+", "1.0", "1.0.1", "1.0.2", "1.0.3", "1.0.4", "1.0.5", "1.0.6",
|
||||
"1.0.7", "1.0.8", "1.0+", "1.4", "1.4.0", "1.4.1", "1.5b1", "1.5b2",
|
||||
"1.5", "1.5.0.4", "1.5.0.*", "2.0a1", "2.0a2", "2.0a3", "2.0b1",
|
||||
"2.0b2", "2.0", "2.0.0.4", "2.0.0.8", "2.0.0.*", "3.0a1", "3.0a2",
|
||||
"3.0a3", "3.0a4", "3.0a5", "3.0a6", "3.0a7", "3.0a8pre", "3.0a8",
|
||||
"3.0a9", "3.0b1", "3.0b2pre", "3.0b2", "3.0b3pre", "3.0b3",
|
||||
"3.0b4pre", "3.0b4", "3.0b5pre", "3.0b5", "3.0pre", "3.0", "3.0.9",
|
||||
"3.0.11", "3.0.12", "3.0.*", "3.1a1pre", "3.1a1", "3.1a2pre", "3.1a2",
|
||||
"3.1b1pre", "3.1b1", "3.1b2pre", "3.1b2", "3.1b3pre", "3.1b3",
|
||||
"3.5b4pre", "3.5b4", "3.5b5pre", "3.5", "3.5.*", "3.6a1pre", "3.6a1",
|
||||
"3.6a2pre", "3.6b1pre", "3.6b2", "3.6", "3.6.4", "3.6.*", "3.7a1pre",
|
||||
"3.7a1", "3.7a2pre", "3.7a2", "3.7a3pre", "3.7a3", "3.7a4pre",
|
||||
"3.7a4", "3.7a5pre", "3.7a5", "3.7a6pre", "4.0b1", "4.0b2pre",
|
||||
"4.0b2", "4.0b3pre", "4.0b3", "4.0b4pre", "4.0b4", "4.0b5pre",
|
||||
"4.0b5", "4.0b6pre", "4.0b6", "4.0b7pre", "4.0b7", "4.0b8pre",
|
||||
"4.0b8", "4.0b9pre", "4.0b9", "4.0b10pre", "4.0b10", "4.0b11pre",
|
||||
"4.0b11", "4.0b12pre", "4.0b12", "4.0", "4.0.*", "4.2a1pre", "5.0a2",
|
||||
"5.0", "5.*", "6.0a1", "6.0a2", "6.0", "6.*", "7.0a1", "7.0a2", "7.0",
|
||||
"7.*", "8.0a1", "8.0a2", "8.0", "8.*", "9.0a1", "9.0a2", "9.0",
|
||||
"9.*", "10.0a1", "10.0a2", "10.0", "10.*", "11.0a1", "11.0a2", "11.0",
|
||||
"11.*", "12.0a1", "12.0a2", "12.0", "12.*", "13.0a1", "13.0a2", "13.0",
|
||||
"13.*", "14.0a1", "14.0a2", "14.0", "14.*", "15.0a1", "15.0a2", "15.0",
|
||||
"15.*", "16.0a1"]
|
||||
},
|
||||
"2":{"name":"Mozilla",
|
||||
"guid":"{86c18b42-e466-45a9-ae7a-9b95ba6f5640}",
|
||||
"versions":
|
||||
["1.0", "1.1", "1.3", "1.4", "1.4.1", "1.5", "1.5.1", "1.6", "1.7",
|
||||
"1.7.7", "1.7.*", "1.8a", "1.8a3", "1.8a4", "1.8a5", "1.8a6", "1.8b1",
|
||||
"1.8", "1.8+"]
|
||||
},
|
||||
"3":{"name":"Thunderbird",
|
||||
"guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}",
|
||||
"versions":
|
||||
["0.3", "0.4", "0.5", "0.6", "0.7", "0.7.1", "0.7.1+", "0.7.2",
|
||||
"0.7.3", "0.7.3+", "0.7+", "0.8", "0.8+", "0.9", "0.9+", "1.0",
|
||||
"1.0.2", "1.0.6", "1.0.8", "1.1a1", "1.0+", "1.5b", "1.5b1", "1.5b2",
|
||||
"1.5", "1.5.0.4", "1.5.0.5", "1.5.0.*", "2.0a1", "2.0b1", "2.0b2",
|
||||
"2.0", "2.0.0.8", "2.0.0.*", "3.0a1pre", "3.0a1", "3.0a2pre", "3.0a2",
|
||||
"3.0a3", "3.0b1pre", "3.0b1", "3.0b2pre", "3.0b2", "3.0b3pre",
|
||||
"3.0b3", "3.0b4pre", "3.0b4", "3.0pre", "3.0", "3.0.1", "3.0.*",
|
||||
"3.1a1pre", "3.1a1", "3.1b1pre", "3.1b1", "3.1b2pre", "3.1b2",
|
||||
"3.1pre", "3.1", "3.1.*", "3.2a1pre", "3.3a1pre", "3.3a1", "3.3a2pre",
|
||||
"3.3a2", "3.3a3pre", "3.3a3", "3.3a4pre", "5.0b1", "5.0b2pre", "5.0",
|
||||
"5.*", "6.0a1", "6.0a2", "6.0", "6.*", "7.0a1", "7.0a2", "7.*",
|
||||
"8.0a1", "8.0a2", "8.0", "8.*", "9.0a1", "9.0a2", "9.0", "9.*",
|
||||
"10.0a1", "10.0a2", "10.0", "10.*", "11.0a1", "11.0a2", "11.0",
|
||||
"11.*", "12.0a1", "12.0a2", "12.0", "12.*", "13.0a1", "13.0a2", "13.0",
|
||||
"13.*", "14.0a1", "14.0a2", "14.0", "14.*", "15.0a1", "15.0a2", "15.0",
|
||||
"15.*", "16.0a1"]
|
||||
},
|
||||
"4":{"name":"Sunbird",
|
||||
"guid":"{718e30fb-e89b-41dd-9da7-e25a45638b28}",
|
||||
"versions":
|
||||
["0.2", "0.3a1", "0.3a2", "0.2+", "0.3", "0.3.1", "0.4a1", "0.5",
|
||||
"0.6a1", "0.7pre", "0.7", "0.8pre", "0.8", "0.9pre", "0.9", "1.0b1",
|
||||
"1.0pre", "5.0b2pre"]
|
||||
},
|
||||
"5":{"name":"SeaMonkey",
|
||||
"guid":"{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}",
|
||||
"versions":
|
||||
["1.0a", "1.0", "1.0.*", "1.1a", "1.1b", "1.1", "1.1.*", "1.5a",
|
||||
"2.0a", "2.0a1pre", "2.0a1", "2.0a2pre", "2.0a2", "2.0a3pre", "2.0a3",
|
||||
"2.0b1pre", "2.0b1", "2.0b2pre", "2.0b2", "2.0pre", "2.0", "2.0.1",
|
||||
"2.0.*", "2.1a1pre", "2.1a1", "2.1a2pre", "2.1a2", "2.1a3pre",
|
||||
"2.1a3", "2.1b1pre", "2.1b1", "2.1b2pre", "2.1b2", "2.1b3pre",
|
||||
"2.1b3", "2.1", "2.1.*", "2.2a1pre", "2.2", "2.2.*", "2.3a2", "2.3",
|
||||
"2.3.*", "2.4a1", "2.4a2", "2.5a1", "2.5a2", "2.6a1", "2.6a2", "2.6",
|
||||
"2.6.*", "2.7a1", "2.7a2", "2.7", "2.7.*", "2.8a1", "2.8a2", "2.8",
|
||||
"2.8.*", "2.9a1", "2.9a2", "2.9", "2.9.*", "2.10a1", "2.10a2", "2.10",
|
||||
"2.10.*", "2.11a1", "2.11a2", "2.11", "2.11.*", "2.12a1", "2.12a2",
|
||||
"2.12", "2.12.*", "2.13a1"]
|
||||
},
|
||||
"6":{"name":"Mobile",
|
||||
"guid":"{a23983c0-fd0e-11dc-95ff-0800200c9a66}",
|
||||
"versions":
|
||||
["0.1", "0.7", "1.0a1", "1.0b1", "1.0b2pre", "1.0b2", "1.0b6pre",
|
||||
"1.0", "1.0.*", "1.1a1", "1.1a2pre", "1.1b1", "1.1", "1.1.*",
|
||||
"2.0a1pre", "2.0a1", "4.0b1pre", "4.0b1", "4.0b2pre", "4.0b2",
|
||||
"4.0b3pre", "4.0b3", "4.0b4pre", "4.0b4", "4.0b5pre", "4.0b5",
|
||||
"4.0b6pre", "4.0", "4.0.*", "4.1a1pre", "5.0a2", "5.0", "5.*",
|
||||
"6.0a1", "6.0a2", "6.0", "6.*", "7.0a1", "7.0a2", "7.0", "7.*",
|
||||
"8.0a1", "8.0a2", "8.0", "8.*", "9.0a1", "9.0a2", "9.0", "9.*",
|
||||
"10.0a1", "10.0a2", "10.0", "10.*", "11.0a1", "11.0a2", "11.0",
|
||||
"11.*", "12.0a1", "12.0a2", "12.0", "12.*", "13.0a1", "13.0a2",
|
||||
"13.0", "13.*", "14.0a1", "14.0a2", "14.0", "14.*", "15.0a1",
|
||||
"15.0a2", "15.0", "15.*", "16.0a1"]
|
||||
},
|
||||
"7":{"name":"Android",
|
||||
"guid":"{aa3c5121-dab2-40e2-81ca-7ea25febc110}",
|
||||
"versions":
|
||||
["10.0a1", "10.0a2", "10.0", "10.*", "11.0a1", "11.0a2", "11.0",
|
||||
"11.*", "12.0a1", "12.0a2", "12.0", "12.*", "13.0a1", "13.0a2",
|
||||
"13.0", "13.*", "14.0a1"]
|
||||
}
|
||||
}
|
|
@ -1,240 +0,0 @@
|
|||
from validator.contextgenerator import ContextGenerator
|
||||
|
||||
|
||||
class ChromeManifest(object):
|
||||
"""This class enables convenient parsing and iteration of
|
||||
chrome.manifest files."""
|
||||
|
||||
def __init__(self, data, path):
|
||||
self.context = ContextGenerator(data)
|
||||
self.lines = data.split("\n")
|
||||
|
||||
# Extract the data from the triples in the manifest
|
||||
triples = []
|
||||
counter = 0
|
||||
|
||||
for line in self.lines:
|
||||
line = line.strip()
|
||||
|
||||
counter += 1
|
||||
|
||||
# Skip weird lines.
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
|
||||
triple = line.split(None, 2)
|
||||
if not triple:
|
||||
continue
|
||||
elif len(triple) == 2:
|
||||
triple.append("")
|
||||
if len(triple) < 3:
|
||||
continue
|
||||
|
||||
triples.append({"subject": triple[0],
|
||||
"predicate": triple[1],
|
||||
"object": triple[2],
|
||||
"line": counter,
|
||||
"filename": path,
|
||||
"context": self.context})
|
||||
|
||||
self.triples = triples
|
||||
|
||||
def get_value(self, subject=None, predicate=None, object_=None):
|
||||
"""Returns the first triple value matching the given subject,
|
||||
predicate, and/or object"""
|
||||
|
||||
for triple in self.triples:
|
||||
|
||||
# Filter out non-matches
|
||||
if (subject and triple["subject"] != subject) or \
|
||||
(predicate and triple["predicate"] != predicate) or \
|
||||
(object_ and triple["object"] != object_): # pragma: no cover
|
||||
continue
|
||||
|
||||
# Return the first found.
|
||||
return triple
|
||||
|
||||
return None
|
||||
|
||||
def get_objects(self, subject=None, predicate=None):
|
||||
"""Returns a generator of objects that correspond to the
|
||||
specified subjects and predicates."""
|
||||
|
||||
for triple in self.triples:
|
||||
|
||||
# Filter out non-matches
|
||||
if (subject and
|
||||
triple["subject"] != subject) or \
|
||||
(predicate and
|
||||
triple["predicate"] != predicate): # pragma: no cover
|
||||
continue
|
||||
|
||||
yield triple["object"]
|
||||
|
||||
def get_triples(self, subject=None, predicate=None, object_=None):
|
||||
"""Returns triples that correspond to the specified subject,
|
||||
predicates, and objects."""
|
||||
|
||||
for triple in self.triples:
|
||||
|
||||
# Filter out non-matches
|
||||
if subject is not None and triple["subject"] != subject:
|
||||
continue
|
||||
if predicate is not None and triple["predicate"] != predicate:
|
||||
continue
|
||||
if object_ is not None and triple["object"] != object_:
|
||||
continue
|
||||
|
||||
yield triple
|
||||
|
||||
def get_applicable_overlays(self, error_bundle):
|
||||
"""
|
||||
Given an error bundle, a list of overlays that are present in the
|
||||
current package or subpackage are returned.
|
||||
"""
|
||||
|
||||
content_paths = self.get_triples(subject="content")
|
||||
if not content_paths:
|
||||
return set()
|
||||
|
||||
# Create some variables that will store where the applicable content
|
||||
# instruction path references and where it links to.
|
||||
chrome_path = ""
|
||||
content_root_path = "/"
|
||||
|
||||
# Look through each of the listed packages and paths.
|
||||
for path in content_paths:
|
||||
chrome_name = path["predicate"]
|
||||
if not path["object"]:
|
||||
continue
|
||||
path_location = path["object"].strip().split()[0]
|
||||
|
||||
# Handle jarred paths differently.
|
||||
if path_location.startswith("jar:"):
|
||||
if not error_bundle.is_nested_package():
|
||||
continue
|
||||
|
||||
# Parse out the JAR and it's location within the chrome.
|
||||
split_jar_url = path_location[4:].split("!", 2)
|
||||
# Ignore invalid/unsupported JAR URLs.
|
||||
if len(split_jar_url) != 2:
|
||||
continue
|
||||
|
||||
# Unpack the JAR URL.
|
||||
jar_path, package_path = split_jar_url
|
||||
|
||||
# Ignore the instruction if the JAR it points to doesn't match
|
||||
# up with the current subpackage tree.
|
||||
if jar_path != error_bundle.package_stack[0]:
|
||||
continue
|
||||
chrome_path = self._url_chunk_join(chrome_name, package_path)
|
||||
# content_root_path stays at the default: /
|
||||
|
||||
break
|
||||
else:
|
||||
# If we're in a subpackage, a content instruction referring to
|
||||
# the root of the package obviously doesn't apply.
|
||||
if error_bundle.is_nested_package():
|
||||
continue
|
||||
|
||||
chrome_path = self._url_chunk_join(chrome_name, "content")
|
||||
content_root_path = "/%s/" % path_location.strip("/")
|
||||
break
|
||||
|
||||
if not chrome_path:
|
||||
return set()
|
||||
|
||||
applicable_overlays = set()
|
||||
chrome_path = "chrome://%s" % self._url_chunk_join(chrome_path + "/")
|
||||
|
||||
for overlay in self.get_triples(subject="overlay"):
|
||||
if not overlay["object"]:
|
||||
error_bundle.error(
|
||||
err_id=("chromemanifest", "get_applicable_overalys",
|
||||
"object"),
|
||||
error="Overlay instruction missing a property.",
|
||||
description="When overlays are registered in a chrome "
|
||||
"manifest file, they require a namespace and "
|
||||
"a chrome URL at minimum.",
|
||||
filename=overlay["filename"],
|
||||
line=overlay["line"],
|
||||
context=self.context) #TODO(basta): Update this!
|
||||
continue
|
||||
overlay_url = overlay["object"].split()[0]
|
||||
if overlay_url.startswith(chrome_path):
|
||||
overlay_relative_path = overlay_url[len(chrome_path):]
|
||||
applicable_overlays.add("/%s" %
|
||||
self._url_chunk_join(content_root_path,
|
||||
overlay_relative_path))
|
||||
|
||||
return applicable_overlays
|
||||
|
||||
def reverse_lookup(self, state, path):
|
||||
"""
|
||||
Returns a chrome URL for a given path, given the current package depth
|
||||
in an error bundle.
|
||||
|
||||
State may either be an error bundle or the actual package stack.
|
||||
"""
|
||||
|
||||
# Make sure the path starts with a forward slash.
|
||||
if not path.startswith("/"):
|
||||
path = "/%s" % path
|
||||
|
||||
# If the state is an error bundle, extract the package stack.
|
||||
if not isinstance(state, list):
|
||||
state = state.package_stack
|
||||
|
||||
content_paths = self.get_triples(subject="content")
|
||||
for content_path in content_paths:
|
||||
chrome_name = content_path["predicate"]
|
||||
if not content_path["object"]:
|
||||
continue
|
||||
path_location = content_path["object"].split()[0]
|
||||
|
||||
if path_location.startswith("jar:"):
|
||||
if not state:
|
||||
continue
|
||||
|
||||
# Parse out the JAR and it's location within the chrome.
|
||||
split_jar_url = path_location[4:].split("!", 2)
|
||||
# Ignore invalid/unsupported JAR URLs.
|
||||
if len(split_jar_url) != 2:
|
||||
continue
|
||||
|
||||
# Unpack the JAR URL.
|
||||
jar_path, package_path = split_jar_url
|
||||
|
||||
if jar_path != state[0]:
|
||||
continue
|
||||
|
||||
return "chrome://%s" % self._url_chunk_join(chrome_name,
|
||||
package_path,
|
||||
path)
|
||||
else:
|
||||
if state:
|
||||
continue
|
||||
|
||||
path_location = "/%s/" % path_location.strip("/")
|
||||
if not path.startswith(path_location):
|
||||
continue
|
||||
|
||||
sliced_path = "/%s" % path[len(path_location):]
|
||||
|
||||
return "chrome://%s" % self._url_chunk_join(chrome_name,
|
||||
sliced_path)
|
||||
|
||||
return None
|
||||
|
||||
def _url_chunk_join(self, *args):
|
||||
"""Join the arguments together to form a predictable URL chunk."""
|
||||
# Strip slashes from either side of each path piece.
|
||||
pathlets = map(lambda s: s.strip("/"), args)
|
||||
# Remove empty pieces.
|
||||
pathlets = filter(None, pathlets)
|
||||
url = "/".join(pathlets)
|
||||
# If this is a directory, add a trailing slash.
|
||||
if args[-1].endswith("/"):
|
||||
url = "%s/" % url
|
||||
return url
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
from validator.decorator import version_range
|
||||
from validator.constants import (FIREFOX_GUID, FENNEC_GUID,
|
||||
THUNDERBIRD_GUID as TB_GUID, ANDROID_GUID)
|
||||
|
||||
|
||||
# Compatibility app/version ranges:
|
||||
FX4_DEFINITION = {FIREFOX_GUID: version_range("firefox", "3.7a1pre", "5.0a2"),
|
||||
FENNEC_GUID: version_range("fennec", "4.0b1pre", "5.0a2")}
|
||||
FX5_DEFINITION = {FIREFOX_GUID: version_range("firefox", "5.0a2", "6.0a1"),
|
||||
FENNEC_GUID: version_range("fennec", "5.0a2", "6.0a1")}
|
||||
|
||||
# Starting with FX6, the version number coalesced, so we can use a simple
|
||||
# helper to build these out.
|
||||
def _build_definition(maj_version_num, firefox=True, fennec=True,
|
||||
thunderbird=True, android=False):
|
||||
definition = {}
|
||||
app_version_range = (lambda app:
|
||||
version_range(app,
|
||||
"%d.0a1" % maj_version_num,
|
||||
"%d.0a1" % (maj_version_num + 1)))
|
||||
if firefox:
|
||||
definition[FIREFOX_GUID] = app_version_range("firefox")
|
||||
if fennec:
|
||||
definition[FENNEC_GUID] = app_version_range("fennec")
|
||||
if thunderbird:
|
||||
definition[TB_GUID] = app_version_range("thunderbird")
|
||||
if android:
|
||||
definition[ANDROID_GUID] = app_version_range("android")
|
||||
|
||||
return definition
|
||||
|
||||
|
||||
FX6_DEFINITION = _build_definition(6, thunderbird=False)
|
||||
FX7_DEFINITION = _build_definition(7)
|
||||
FX8_DEFINITION = _build_definition(8)
|
||||
FX9_DEFINITION = _build_definition(9)
|
||||
FX10_DEFINITION = _build_definition(10, android=True)
|
||||
FX11_DEFINITION = _build_definition(11, android=True)
|
||||
FX12_DEFINITION = _build_definition(12, android=True)
|
||||
FX13_DEFINITION = _build_definition(13, android=True)
|
||||
FX14_DEFINITION = _build_definition(14, android=True)
|
||||
|
||||
TB6_DEFINITION = {TB_GUID: version_range("thunderbird", "6.0a1", "7.0a1")}
|
||||
TB7_DEFINITION = {TB_GUID: version_range("thunderbird", "7.0a1", "8.0a1")}
|
||||
TB8_DEFINITION = {TB_GUID: version_range("thunderbird", "8.0a1", "9.0a1")}
|
||||
TB9_DEFINITION = {TB_GUID: version_range("thunderbird", "9.0a1", "10.0a1")}
|
||||
TB10_DEFINITION = {TB_GUID: version_range("thunderbird", "10.0a1", "11.0a1")}
|
||||
TB11_DEFINITION = {TB_GUID: version_range("thunderbird", "11.0a1", "12.0a1")}
|
||||
TB12_DEFINITION = {TB_GUID: version_range("thunderbird", "12.0a1", "13.0a1")}
|
||||
TB13_DEFINITION = _build_definition(13, firefox=False, fennec=False)
|
||||
TB14_DEFINITION = _build_definition(14, firefox=False, fennec=False)
|
||||
|
|
@ -14,9 +14,6 @@ PACKAGE_MULTI = 1 # A multi extension is an extension
|
|||
PACKAGE_SUBPACKAGE = 7
|
||||
PACKAGE_WEBAPP = 8
|
||||
|
||||
# The "earliest" version number for Firefox 4
|
||||
FF4_MIN = "3.7a1pre"
|
||||
|
||||
# Application GUIDs
|
||||
FIREFOX_GUID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
|
||||
MOZILLA_GUID = "{86c18b42-e466-45a9-ae7a-9b95ba6f5640}"
|
||||
|
@ -36,18 +33,12 @@ APPLICATIONS = {
|
|||
ANDROID_GUID: "android",
|
||||
}
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "app_versions.json")) as avs:
|
||||
APPROVED_APPLICATIONS = json.load(avs)
|
||||
|
||||
SPIDERMONKEY_INSTALLATION = os.environ.get("SPIDERMONKEY_INSTALLATION")
|
||||
|
||||
DEFAULT_WEBAPP_MRKT_URLS = ["https://marketplace.mozilla.org",
|
||||
"https://marketplace-dev.allizom.org"]
|
||||
BUGZILLA_BUG = "https://bugzilla.mozilla.org/show_bug.cgi?id=%d"
|
||||
|
||||
JETPACK_URI_URL = "https://wiki.mozilla.org/Labs/Jetpack/Release_Notes/" \
|
||||
"1.4#Known_Issues"
|
||||
|
||||
# Graciously provided by @kumar in bug 614574
|
||||
if (not SPIDERMONKEY_INSTALLATION or
|
||||
not os.path.exists(SPIDERMONKEY_INSTALLATION)):
|
||||
|
@ -66,7 +57,7 @@ if not os.path.exists(SPIDERMONKEY_INSTALLATION):
|
|||
SPIDERMONKEY_INSTALLATION = None
|
||||
|
||||
try:
|
||||
from validator.constants_local import *
|
||||
from constants_local import *
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import validator.constants
|
||||
from validator.constants import *
|
||||
import constants
|
||||
from .constants import *
|
||||
|
||||
|
||||
TEST_TIERS = {}
|
||||
|
@ -44,33 +44,3 @@ def get_tests(tier, type_=None):
|
|||
|
||||
# List comprehension to sort and filter and the like.
|
||||
return (test for test in ctier if test["type"] in types)
|
||||
|
||||
|
||||
def version_range(guid, version, before=None, app_versions=None):
|
||||
"""Returns all values after (and including) `version` for the app `guid`"""
|
||||
|
||||
if app_versions is None:
|
||||
app_versions = validator.constants.APPROVED_APPLICATIONS
|
||||
app_key = None
|
||||
|
||||
# Support for shorthand instead of full GUIDs.
|
||||
for app_guid, app_name in APPLICATIONS.items():
|
||||
if app_name == guid:
|
||||
guid = app_guid
|
||||
break
|
||||
|
||||
for key in app_versions.keys():
|
||||
if app_versions[key]["guid"] == guid:
|
||||
app_key = key
|
||||
break
|
||||
|
||||
if not app_key or version not in app_versions[app_key]["versions"]:
|
||||
raise Exception("Bad GUID or version provided for version range")
|
||||
|
||||
all_versions = app_versions[app_key]["versions"]
|
||||
version_pos = all_versions.index(version)
|
||||
before_pos = None
|
||||
if before is not None and before in all_versions:
|
||||
before_pos = all_versions.index(before)
|
||||
return all_versions[version_pos:before_pos]
|
||||
|
||||
|
|
|
@ -42,10 +42,6 @@ class ErrorBundle(object):
|
|||
self.message_count = 0
|
||||
self.message_tree = {}
|
||||
|
||||
self.compat_summary = {"errors": 0,
|
||||
"warnings": 0,
|
||||
"notices": 0}
|
||||
|
||||
self.ending_tier = 1
|
||||
self.tier = 1
|
||||
|
||||
|
@ -81,8 +77,7 @@ class ErrorBundle(object):
|
|||
|
||||
def error(self, err_id, error,
|
||||
description='', filename='', line=None, column=None,
|
||||
context=None, tier=None, for_appversions=None,
|
||||
compatibility_type=None):
|
||||
context=None, tier=None, for_appversions=None):
|
||||
"Stores an error message for the validation process"
|
||||
self._save_message(self.errors,
|
||||
"errors",
|
||||
|
@ -93,15 +88,13 @@ class ErrorBundle(object):
|
|||
"line": line,
|
||||
"column": column,
|
||||
"tier": tier,
|
||||
"for_appversions": for_appversions,
|
||||
"compatibility_type": compatibility_type},
|
||||
"for_appversions": for_appversions},
|
||||
context=context)
|
||||
return self
|
||||
|
||||
def warning(self, err_id, warning,
|
||||
description='', filename='', line=None, column=None,
|
||||
context=None, tier=None, for_appversions=None,
|
||||
compatibility_type=None):
|
||||
context=None, tier=None, for_appversions=None):
|
||||
"Stores a warning message for the validation process"
|
||||
self._save_message(self.warnings,
|
||||
"warnings",
|
||||
|
@ -112,15 +105,13 @@ class ErrorBundle(object):
|
|||
"line": line,
|
||||
"column": column,
|
||||
"tier": tier,
|
||||
"for_appversions": for_appversions,
|
||||
"compatibility_type": compatibility_type},
|
||||
"for_appversions": for_appversions},
|
||||
context=context)
|
||||
return self
|
||||
|
||||
def notice(self, err_id, notice,
|
||||
description="", filename="", line=None, column=None,
|
||||
context=None, tier=None, for_appversions=None,
|
||||
compatibility_type=None):
|
||||
context=None, tier=None, for_appversions=None):
|
||||
"Stores an informational message about the validation"
|
||||
self._save_message(self.notices,
|
||||
"notices",
|
||||
|
@ -131,8 +122,7 @@ class ErrorBundle(object):
|
|||
"line": line,
|
||||
"column": column,
|
||||
"tier": tier,
|
||||
"for_appversions": for_appversions,
|
||||
"compatibility_type": compatibility_type},
|
||||
"for_appversions": for_appversions},
|
||||
context=context)
|
||||
return self
|
||||
|
||||
|
@ -186,10 +176,6 @@ class ErrorBundle(object):
|
|||
if message["tier"] is None:
|
||||
message["tier"] = self.tier
|
||||
|
||||
# Build out the compatibility summary if possible.
|
||||
if message["compatibility_type"]:
|
||||
self.compat_summary["%ss" % message["compatibility_type"]] += 1
|
||||
|
||||
# Build out the message tree entry.
|
||||
if message["id"]:
|
||||
tree = self.message_tree
|
||||
|
@ -311,8 +297,7 @@ class ErrorBundle(object):
|
|||
column=message["column"],
|
||||
context=message["context"],
|
||||
tier=message["tier"],
|
||||
for_appversions=message["for_appversions"],
|
||||
compatibility_type=message["compatibility_type"])
|
||||
for_appversions=message["for_appversions"])
|
||||
|
||||
def render_json(self):
|
||||
"Returns a JSON summary of the validation operation."
|
||||
|
@ -332,7 +317,6 @@ class ErrorBundle(object):
|
|||
"warnings": len(self.warnings),
|
||||
"notices": len(self.notices),
|
||||
"message_tree": self.message_tree,
|
||||
"compatibility_summary": self.compat_summary,
|
||||
"metadata": self.metadata}
|
||||
|
||||
messages = output["messages"]
|
||||
|
@ -357,12 +341,6 @@ class ErrorBundle(object):
|
|||
"Prints a summary of the validation process so far."
|
||||
|
||||
types = {0: "Unknown",
|
||||
1: "Extension/Multi-Extension",
|
||||
2: "Theme",
|
||||
3: "Dictionary",
|
||||
4: "Language Pack",
|
||||
5: "Search Provider",
|
||||
7: "Subpackage",
|
||||
8: "App"}
|
||||
detected_type = types[self.detected_type]
|
||||
|
||||
|
@ -394,22 +372,6 @@ class ErrorBundle(object):
|
|||
message=notice,
|
||||
verbose=verbose)
|
||||
|
||||
if "is_jetpack" in self.metadata and verbose:
|
||||
self.handler.write("\n")
|
||||
self.handler.write("<<GREEN>>Jetpack add-on detected.<<NORMAL>>\n"
|
||||
"Identified files:")
|
||||
if "jetpack_identified_files" in self.metadata:
|
||||
for filename, data in \
|
||||
self.metadata["jetpack_identified_files"].items():
|
||||
self.handler.write((" %s\n" % filename) +
|
||||
(" %s : %s" % data))
|
||||
|
||||
if "jetpack_unknown_files" in self.metadata:
|
||||
self.handler.write("Unknown files:")
|
||||
for filename in self.metadata["jetpack_unknown_files"]:
|
||||
self.handler.write(" %s" % filename)
|
||||
|
||||
|
||||
self.handler.write("\n")
|
||||
if self.unfinished:
|
||||
self.handler.write("<<RED>>Validation terminated early")
|
||||
|
@ -486,35 +448,6 @@ class ErrorBundle(object):
|
|||
# Send the final output to the handler to be rendered.
|
||||
self.handler.write(u''.join(map(unicodehelper.decode, output)))
|
||||
|
||||
def supports_version(self, guid_set):
|
||||
"""
|
||||
Returns whether a GUID set in for_appversions format is compatbile with
|
||||
the current supported applications list.
|
||||
"""
|
||||
|
||||
# Don't let the test run if we haven't parsed install.rdf yet.
|
||||
if self.supported_versions is None:
|
||||
raise Exception("Early compatibility test run before install.rdf "
|
||||
"was parsed.")
|
||||
|
||||
return self._compare_version(requirements=guid_set,
|
||||
support=self.supported_versions)
|
||||
|
||||
def _compare_version(self, requirements, support):
|
||||
"""
|
||||
Return whether there is an intersection between a support applications
|
||||
GUID set and a set of supported applications.
|
||||
"""
|
||||
|
||||
for guid in requirements:
|
||||
# If we support any of the GUIDs in the guid_set, test if any of
|
||||
# the provided versions for the GUID are supported.
|
||||
if (guid in support and
|
||||
any((detected_version in requirements[guid]) for
|
||||
detected_version in
|
||||
support[guid])):
|
||||
return True
|
||||
|
||||
def discard_unused_messages(self, ending_tier):
|
||||
"""
|
||||
Delete messages from errors, warnings, and notices whose tier is
|
||||
|
@ -526,4 +459,3 @@ class ErrorBundle(object):
|
|||
for message in stack:
|
||||
if message["tier"] > ending_tier:
|
||||
stack.remove(message)
|
||||
|
||||
|
|
|
@ -1,11 +1,2 @@
|
|||
import validator.testcases.chromemanifest
|
||||
import validator.testcases.conduit
|
||||
import validator.testcases.content
|
||||
import validator.testcases.installrdf
|
||||
import validator.testcases.jetpack
|
||||
import validator.testcases.l10ncompleteness
|
||||
import validator.testcases.langpack
|
||||
import validator.testcases.packagelayout
|
||||
import validator.testcases.targetapplication
|
||||
import validator.testcases.themes
|
||||
|
||||
import testcases.content
|
||||
import testcases.packagelayout
|
|
@ -5,7 +5,7 @@ import sys
|
|||
import zipfile
|
||||
from StringIO import StringIO
|
||||
|
||||
from validator.validate import validate
|
||||
from .validate import validate
|
||||
from constants import *
|
||||
|
||||
|
||||
|
@ -13,12 +13,6 @@ def main():
|
|||
"Main function. Handles delegation to other functions."
|
||||
|
||||
expectations = {"any": PACKAGE_ANY,
|
||||
"extension": PACKAGE_EXTENSION,
|
||||
"theme": PACKAGE_THEME,
|
||||
"dictionary": PACKAGE_DICTIONARY,
|
||||
"languagepack": PACKAGE_LANGPACK,
|
||||
"search": PACKAGE_SEARCHPROV,
|
||||
"multi": PACKAGE_MULTI,
|
||||
"webapp": PACKAGE_WEBAPP}
|
||||
|
||||
# Parse the arguments that
|
||||
|
@ -27,12 +21,6 @@ def main():
|
|||
|
||||
parser.add_argument("package",
|
||||
help="The path of the package you're testing")
|
||||
parser.add_argument("-t",
|
||||
"--type",
|
||||
default="any",
|
||||
choices=expectations.keys(),
|
||||
help="Type of addon you assume you're testing",
|
||||
required=False)
|
||||
parser.add_argument("-o",
|
||||
"--output",
|
||||
default="text",
|
||||
|
@ -61,34 +49,6 @@ def main():
|
|||
help="""Indicates that the addon will not be
|
||||
hosted on addons.mozilla.org. This allows the
|
||||
<em:updateURL> element to be set.""")
|
||||
parser.add_argument("--approved_applications",
|
||||
default="validator/app_versions.json",
|
||||
help="""A JSON file containing acceptable applications
|
||||
and their versions""")
|
||||
parser.add_argument("--target-maxversion",
|
||||
help="""JSON string to override the package's
|
||||
targetapp_maxVersion for validation. The JSON object
|
||||
should be a dict of versions keyed by application
|
||||
GUID. For example, setting a package's max Firefox
|
||||
version to 5.*:
|
||||
{"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": "5.*"}
|
||||
""")
|
||||
parser.add_argument("--target-minversion",
|
||||
help="""JSON string to override the package's
|
||||
targetapp_minVersion for validation. The JSON object
|
||||
should be a dict of versions keyed by application
|
||||
GUID. For example, setting a package's min Firefox
|
||||
version to 5.*:
|
||||
{"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": "5.*"}
|
||||
""")
|
||||
parser.add_argument("--for-appversions",
|
||||
help="""JSON string to run validation tests for
|
||||
compatibility with a specific app/version. The JSON
|
||||
object should be a dict of version lists keyed by
|
||||
application GUID. For example, running Firefox 6.*
|
||||
compatibility tests:
|
||||
{"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": ["6.*"]}
|
||||
""")
|
||||
parser.add_argument("--timeout",
|
||||
help="The amount of time before validation is "
|
||||
"terminated with a timeout exception.",
|
||||
|
@ -104,16 +64,6 @@ def main():
|
|||
args.type
|
||||
sys.exit(1)
|
||||
|
||||
overrides = {}
|
||||
if args.target_minversion:
|
||||
overrides['targetapp_minVersion'] = json.loads(args.target_minversion)
|
||||
if args.target_maxversion:
|
||||
overrides['targetapp_maxVersion'] = json.loads(args.target_maxversion)
|
||||
|
||||
for_appversions = None
|
||||
if args.for_appversions:
|
||||
for_appversions = json.loads(args.for_appversions)
|
||||
|
||||
try:
|
||||
timeout = int(args.timeout)
|
||||
except ValueError:
|
||||
|
@ -123,12 +73,8 @@ def main():
|
|||
expectation = expectations[args.type]
|
||||
error_bundle = validate(args.package,
|
||||
format=None,
|
||||
approved_applications=args.approved_applications,
|
||||
determined=args.determined,
|
||||
listed=not args.selfhosted,
|
||||
overrides=overrides,
|
||||
for_appversions=for_appversions,
|
||||
expectation=expectation,
|
||||
timeout=timeout)
|
||||
|
||||
# Print the output of the tests based on the requested format.
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
from xml.dom.minidom import parse
|
||||
from validator.constants import *
|
||||
|
||||
|
||||
def detect_opensearch(err, package, listed=False):
|
||||
"Detect, parse, and validate an OpenSearch provider"
|
||||
|
||||
# Parse the file.
|
||||
try:
|
||||
srch_prov = parse(package)
|
||||
except:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"parse_error"),
|
||||
error="OpenSearch: XML Parse Error",
|
||||
description="The OpenSearch extension could not be parsed due to "
|
||||
"a syntax error in the XML.")
|
||||
return err
|
||||
|
||||
# Make sure that the root element is OpenSearchDescription.
|
||||
if srch_prov.documentElement.tagName != "OpenSearchDescription":
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"invalid_document_root"),
|
||||
error="OpenSearch: Invalid Document Root",
|
||||
description="The root element of the OpenSearch provider is not "
|
||||
"'OpenSearchDescription'.")
|
||||
|
||||
# Per bug 617822
|
||||
if not srch_prov.documentElement.hasAttribute("xmlns"):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"no_xmlns"),
|
||||
error="OpenSearch: Missing XMLNS attribute",
|
||||
description="The XML namespace attribute is missing from the "
|
||||
"OpenSearch document.")
|
||||
|
||||
if ("xmlns" not in srch_prov.documentElement.attributes.keys() or
|
||||
srch_prov.documentElement.attributes["xmlns"].value not in (
|
||||
'http://a9.com/-/spec/opensearch/1.0/',
|
||||
'http://a9.com/-/spec/opensearch/1.1/',
|
||||
'http://a9.com/-/spec/opensearchdescription/1.1/',
|
||||
'http://a9.com/-/spec/opensearchdescription/1.0/')):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"invalid_xmlns"),
|
||||
error="OpenSearch: Bad XMLNS attribute",
|
||||
description="The XML namespace attribute contains an "
|
||||
"value.")
|
||||
|
||||
# Make sure that there is exactly one ShortName.
|
||||
sn = srch_prov.documentElement.getElementsByTagName("ShortName")
|
||||
if not sn:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_shortname"),
|
||||
error="OpenSearch: Missing <ShortName> elements",
|
||||
description="ShortName elements are mandatory OpenSearch provider "
|
||||
"elements.")
|
||||
elif len(sn) > 1:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"extra_shortnames"),
|
||||
error="OpenSearch: Too many <ShortName> elements",
|
||||
description="Too many ShortName elements exist in the OpenSearch "
|
||||
"provider.")
|
||||
else:
|
||||
sn_children = sn[0].childNodes
|
||||
short_name = 0
|
||||
for node in sn_children:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
short_name += len(node.data)
|
||||
if short_name > 16:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"big_shortname"),
|
||||
error="OpenSearch: <ShortName> element too long",
|
||||
description="The ShortName element must contains less than "
|
||||
"seventeen characters.")
|
||||
|
||||
# Make sure that there is exactly one Description.
|
||||
if len(srch_prov.documentElement.getElementsByTagName("Description")) != 1:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_description"),
|
||||
error="OpenSearch: Invalid number of <Description> elements",
|
||||
description="There are too many or too few Description elements "
|
||||
"in the OpenSearch provider.")
|
||||
|
||||
# Grab the URLs and make sure that there is at least one.
|
||||
urls = srch_prov.documentElement.getElementsByTagName("Url")
|
||||
if not urls:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_url"),
|
||||
error="OpenSearch: Missing <Url> elements",
|
||||
description="The OpenSearch provider is missing a Url element.")
|
||||
|
||||
if listed and any(url.hasAttribute("rel") and
|
||||
url.attributes["rel"].value == "self" for
|
||||
url in urls):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"rel_self"),
|
||||
error="OpenSearch: <Url> elements may not be rel=self",
|
||||
description="Per AMO guidelines, OpenSearch providers cannot "
|
||||
"contain <Url /> elements with a 'rel' attribute "
|
||||
"pointing to the URL's current location. It must be "
|
||||
"removed before posting this provider to AMO.")
|
||||
|
||||
acceptable_mimes = ("text/html", "application/xhtml+xml")
|
||||
acceptable_urls = [url for url in urls if url.hasAttribute("type") and
|
||||
url.attributes["type"].value in acceptable_mimes]
|
||||
|
||||
# At least one Url must be text/html
|
||||
if not acceptable_urls:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_url_texthtml"),
|
||||
error="OpenSearch: Missing <Url> element with 'text/html' type",
|
||||
description="OpenSearch providers must have at least one Url "
|
||||
"element with a type attribute set to 'text/html'.")
|
||||
|
||||
# Make sure that each Url has the require attributes.
|
||||
for url in acceptable_urls:
|
||||
|
||||
if url.hasAttribute("rel") and url.attributes["rel"].value == "self":
|
||||
continue
|
||||
|
||||
if url.hasAttribute("method") and \
|
||||
url.attributes["method"].value.upper() not in ("GET", "POST"):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_method"),
|
||||
error="OpenSearch: <Url> element with invalid 'method'",
|
||||
description="A Url element in the OpenSearch provider lists a "
|
||||
"method attribute, but the value is not GET or "
|
||||
"POST.")
|
||||
|
||||
# Test for attribute presence.
|
||||
if not url.hasAttribute("template"):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"missing_template"),
|
||||
error="OpenSearch: <Url> element missing template attribute",
|
||||
description="<Url> elements of OpenSearch providers must "
|
||||
"include a template attribute.")
|
||||
|
||||
url_template = url.attributes["template"].value
|
||||
if url_template[:4] != "http":
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"invalid_template"),
|
||||
error="OpenSearch: <Url> element with invalid 'template'",
|
||||
description="A <Url> element in the OpenSearch provider lists a "
|
||||
"template attribute, but the value is not a valid "
|
||||
"HTTP URL.")
|
||||
|
||||
# Make sure that there is a {searchTerms} placeholder in the
|
||||
# URL template.
|
||||
found_template = url_template.count("{searchTerms}") > 0
|
||||
|
||||
# If we didn't find it in a simple parse of the template=""
|
||||
# attribute, look deeper at the <Param /> elements.
|
||||
if not found_template:
|
||||
for param in url.getElementsByTagName("Param"):
|
||||
# As long as we're in here and dependent on the
|
||||
# attributes, we'd might as well validate them.
|
||||
attribute_keys = param.attributes.keys()
|
||||
if "name" not in attribute_keys or \
|
||||
"value" not in attribute_keys:
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"param_missing_attrs"),
|
||||
error="OpenSearch: <Param> element missing name/value",
|
||||
description="Param elements in the OpenSearch "
|
||||
"provider must include a name and a value "
|
||||
"attribute.")
|
||||
|
||||
param_value = (param.attributes["value"].value if
|
||||
"value" in param.attributes.keys() else
|
||||
"")
|
||||
if param_value.count("{searchTerms}"):
|
||||
found_template = True
|
||||
|
||||
# Since we're in a validating spirit, continue
|
||||
# looking for more errors and don't break
|
||||
|
||||
# If the template still hasn't been found...
|
||||
if not found_template:
|
||||
tpl = url.attributes["template"].value
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"template_not_found"),
|
||||
error="OpenSearch: <Url> element missing template attribute",
|
||||
description=["<Url> elements of OpenSearch providers must "
|
||||
"include a template attribute or specify a "
|
||||
"placeholder with {searchTerms}.",
|
||||
"Missing template: %s" % tpl])
|
||||
|
||||
# Make sure there are no updateURL elements
|
||||
if srch_prov.getElementsByTagName("updateURL"):
|
||||
err.error(
|
||||
err_id=("opensearch",
|
||||
"detect_opensearch",
|
||||
"banned_updateurl"),
|
||||
error="OpenSearch: <updateURL> elements are banned in OpenSearch "
|
||||
"providers.",
|
||||
description="OpenSearch providers may not contain <updateURL> "
|
||||
"elements.")
|
||||
|
||||
# The OpenSearch provider is valid!
|
||||
return err
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
import types
|
||||
from rdflib import Graph, URIRef
|
||||
from rdflib.exceptions import ParserError
|
||||
from StringIO import StringIO
|
||||
from xml.sax import SAXParseException
|
||||
|
||||
|
||||
class RDFException(Exception):
|
||||
"""Exception thrown when the RDF parser encounters a problem."""
|
||||
|
||||
def __init__(self, message=None, orig_exception=None):
|
||||
if message is None and orig_exception is not None:
|
||||
message = orig_exception.message
|
||||
|
||||
super(RDFException, self).__init__(message)
|
||||
self.orig_exception = orig_exception
|
||||
|
||||
def line(self):
|
||||
return (self.orig_exception.getLineNumber() if self.orig_exception else
|
||||
None)
|
||||
|
||||
|
||||
class AddonRDFEntity(object):
|
||||
"""
|
||||
A "resolved" entity within an RDF file in an add-on. For use by SAX during
|
||||
the entity resolution process.
|
||||
"""
|
||||
|
||||
def getByteStream(self):
|
||||
yield None
|
||||
|
||||
def getSystemId(self):
|
||||
return ""
|
||||
|
||||
|
||||
class AddonRDFEntityResolver(object):
|
||||
"""
|
||||
An entity resolver to be used by SAX for resolving internal entity
|
||||
references.
|
||||
"""
|
||||
|
||||
def __init__(self, err):
|
||||
self.err = err
|
||||
|
||||
def resolveEntity(self, public, system):
|
||||
if system.startswith("data:"):
|
||||
self.err.warning(
|
||||
err_id=("rdf", "entity_resolver", "data_uri"),
|
||||
warning="`data:` URIs are not permitted in `install.rdf`.",
|
||||
filename="install.rdf")
|
||||
elif system.startswith("chrome://"):
|
||||
self.err.warning(
|
||||
err_id=("rdf", "entity_resolver", "chrome_uri"),
|
||||
warning="`chrome://` URI referenced before initialization.",
|
||||
description="A chrome URI was referenced before the "
|
||||
"browser chrome was initialized.",
|
||||
filename="install.rdf")
|
||||
else:
|
||||
self.err.warning(
|
||||
err_id=("rdf", "entity_resolver", "remote_uri"),
|
||||
warning="Remote URI referenced from `install.rdf`.",
|
||||
description="Remote URIs should not be used within "
|
||||
"`install.rdf` files.",
|
||||
filename="install.rdf")
|
||||
|
||||
return AddonRDFEntity()
|
||||
|
||||
|
||||
class RDFParser(object):
|
||||
"""Parser wrapper for RDF files."""
|
||||
|
||||
def __init__(self, err, data, namespace=None):
|
||||
self.err = err
|
||||
self.manifest = u"urn:mozilla:install-manifest"
|
||||
self.namespace = namespace or "http://www.mozilla.org/2004/em-rdf"
|
||||
|
||||
if isinstance(data, types.StringTypes):
|
||||
data = StringIO(data) # Wrap data in a pseudo-file
|
||||
|
||||
from rdflib.plugins.parsers import rdfxml
|
||||
orig_create_parser = rdfxml.create_parser
|
||||
|
||||
try:
|
||||
# Patch rdflib to not resolve URL entities.
|
||||
def create_parser(*args, **kwargs):
|
||||
parser = orig_create_parser(*args, **kwargs)
|
||||
parser.setEntityResolver(AddonRDFEntityResolver(err))
|
||||
return parser
|
||||
rdfxml.create_parser = create_parser
|
||||
|
||||
# Load up and parse the file in XML format.
|
||||
graph = Graph()
|
||||
graph.parse(data, format="xml")
|
||||
self.rdf = graph
|
||||
|
||||
except ParserError as ex:
|
||||
# Re-raise the exception in a local exception type.
|
||||
raise RDFException(message=ex.message)
|
||||
except SAXParseException as ex:
|
||||
# Raise the SAX parse exceptions so we get some line info.
|
||||
raise RDFException(orig_exception=ex)
|
||||
finally:
|
||||
# If we fail, we don't want to sully up the creation function.
|
||||
rdfxml.create_parser = orig_create_parser
|
||||
|
||||
def uri(self, element, namespace=None):
|
||||
"Returns a URIRef object for use with the RDF document."
|
||||
|
||||
if namespace is None:
|
||||
namespace = self.namespace
|
||||
|
||||
return URIRef("%s#%s" % (namespace, element))
|
||||
|
||||
def get_root_subject(self):
|
||||
"Returns the BNode which describes the topmost subject of the graph."
|
||||
|
||||
manifest = URIRef(self.manifest)
|
||||
|
||||
if list(self.rdf.triples((manifest, None, None))):
|
||||
return manifest
|
||||
else:
|
||||
return self.rdf.subjects(None, self.manifest).next()
|
||||
|
||||
def get_object(self, subject=None, predicate=None):
|
||||
"""Eliminates some of the glue code for searching RDF. Pass
|
||||
in a URIRef object (generated by the `uri` function above or
|
||||
a BNode object (returned by this function) for either of the
|
||||
parameters."""
|
||||
|
||||
# Get the result of the search
|
||||
results = self.rdf.objects(subject, predicate)
|
||||
as_list = list(results)
|
||||
|
||||
# Don't raise exceptions, value test!
|
||||
if not as_list:
|
||||
return None
|
||||
|
||||
return as_list[0]
|
||||
|
||||
def get_objects(self, subject=None, predicate=None):
|
||||
"""Same as get_object, except returns a list of objects which
|
||||
satisfy the query rather than a single result."""
|
||||
|
||||
# Get the result of the search
|
||||
results = self.rdf.objects(subject, predicate)
|
||||
return list(results)
|
|
@ -4,13 +4,9 @@ import signal
|
|||
from zipfile import BadZipfile
|
||||
from zlib import error as zlib_error
|
||||
|
||||
from validator.typedetection import detect_type
|
||||
from validator.opensearch import detect_opensearch
|
||||
from validator.webapp import detect_webapp
|
||||
from validator.chromemanifest import ChromeManifest
|
||||
from validator.rdf import RDFException, RDFParser
|
||||
from validator.xpi import XPIManager
|
||||
from validator import decorator
|
||||
from .webapp import detect_webapp
|
||||
from .xpi import XPIManager
|
||||
from . import decorator
|
||||
|
||||
from constants import *
|
||||
|
||||
|
@ -59,9 +55,7 @@ def prepare_package(err, path, expectation=0, for_appversions=None,
|
|||
package_extension = os.path.splitext(path)[1]
|
||||
package_extension = package_extension.lower()
|
||||
|
||||
if package_extension == ".xml":
|
||||
return test_search(err, path, expectation)
|
||||
elif expectation == PACKAGE_WEBAPP:
|
||||
if expectation == PACKAGE_WEBAPP:
|
||||
return test_webapp(err, path, expectation)
|
||||
|
||||
# Test that the package is an XPI.
|
||||
|
@ -96,27 +90,6 @@ def prepare_package(err, path, expectation=0, for_appversions=None,
|
|||
return output
|
||||
|
||||
|
||||
def test_search(err, package, expectation=0):
|
||||
"Tests the package to see if it is a search provider."
|
||||
|
||||
expected_search_provider = expectation in (PACKAGE_ANY,
|
||||
PACKAGE_SEARCHPROV)
|
||||
|
||||
# If we're not expecting a search provider, warn the user and stop
|
||||
# testing it like a search provider.
|
||||
if not expected_search_provider:
|
||||
return err.warning(("main",
|
||||
"test_search",
|
||||
"extension"),
|
||||
"Unexpected file extension.")
|
||||
|
||||
# Is this a search provider?
|
||||
detect_opensearch(err, package, listed=err.get_resource("listed"))
|
||||
|
||||
if expected_search_provider and not err.failed():
|
||||
err.set_type(PACKAGE_SEARCHPROV)
|
||||
|
||||
|
||||
def test_webapp(err, package, expectation=0):
|
||||
"Tests the package to see if it is a search provider."
|
||||
|
||||
|
@ -140,11 +113,6 @@ def test_package(err, file_, name, expectation=PACKAGE_ANY,
|
|||
# Load up a new instance of an XPI.
|
||||
try:
|
||||
package = XPIManager(file_, mode="r", name=name)
|
||||
|
||||
# Test the install.rdf file to see if we can get the type that way.
|
||||
has_install_rdf = "install.rdf" in package
|
||||
if has_install_rdf:
|
||||
_load_install_rdf(err, package, expectation)
|
||||
except IOError:
|
||||
# Die on this one because the file won't open.
|
||||
return err.error(("main",
|
||||
|
@ -167,11 +135,6 @@ def test_package(err, file_, name, expectation=PACKAGE_ANY,
|
|||
"unexpected_type"),
|
||||
"Unexpected package type (found theme)")
|
||||
|
||||
# Test the install.rdf file to see if we can get the type that way.
|
||||
has_install_rdf = "install.rdf" in package
|
||||
if has_install_rdf:
|
||||
_load_install_rdf(err, package, expectation)
|
||||
|
||||
try:
|
||||
output = test_inner_package(err, package, for_appversions)
|
||||
except ValidationTimeout as ex:
|
||||
|
@ -187,129 +150,9 @@ def test_package(err, file_, name, expectation=PACKAGE_ANY,
|
|||
return output
|
||||
|
||||
|
||||
def _load_install_rdf(err, package, expectation):
|
||||
try:
|
||||
install_rdf = RDFParser(err, package.read("install.rdf"))
|
||||
except RDFException as ex:
|
||||
err.error(
|
||||
err_id=("main", "test_package", "parse_error"),
|
||||
error="Could not parse `install.rdf`.",
|
||||
description="The RDF parser was unable to parse the "
|
||||
"install.rdf file included with this add-on.",
|
||||
filename="install.rdf",
|
||||
line=ex.line())
|
||||
return
|
||||
else:
|
||||
if install_rdf.rdf is None:
|
||||
err.error(
|
||||
err_id=("main", "test_package", "cannot_parse_installrdf"),
|
||||
error="Cannot read `install.rdf`",
|
||||
description="The install.rdf file could not be parsed.",
|
||||
filename="install.rdf")
|
||||
return
|
||||
else:
|
||||
err.save_resource("has_install_rdf", True, pushable=True)
|
||||
err.save_resource("install_rdf", install_rdf, pushable=True)
|
||||
|
||||
# Load up the results of the type detection
|
||||
results = detect_type(err, install_rdf, package)
|
||||
if results is None:
|
||||
err.error(
|
||||
err_id=("main", "test_package", "undeterminable_type"),
|
||||
error="Unable to determine add-on type",
|
||||
description="The type detection algorithm could not determine "
|
||||
"the type of the add-on.")
|
||||
return
|
||||
else:
|
||||
err.set_type(results)
|
||||
|
||||
# Compare the results of the low-level type detection to
|
||||
# that of the expectation and the assumption.
|
||||
if not expectation in (PACKAGE_ANY, results):
|
||||
err.warning(("main",
|
||||
"test_package",
|
||||
"extension_type_mismatch"),
|
||||
"Extension Type Mismatch",
|
||||
["We detected that the add-on's type does not match the "
|
||||
"expected type.",
|
||||
'Type "%s" expected, found "%s")' %
|
||||
(types[expectation], types[results])])
|
||||
|
||||
|
||||
def populate_chrome_manifest(err, xpi_package):
|
||||
"Loads the chrome.manifest if it's present"
|
||||
|
||||
if "chrome.manifest" in xpi_package:
|
||||
chrome_data = xpi_package.read("chrome.manifest")
|
||||
chrome = ChromeManifest(chrome_data, "chrome.manifest")
|
||||
|
||||
chrome_recursion_buster = set()
|
||||
|
||||
# Handle the case of manifests linked from the manifest.
|
||||
def get_linked_manifest(path, from_path, from_chrome, from_triple):
|
||||
|
||||
if path in chrome_recursion_buster:
|
||||
err.warning(
|
||||
err_id=("submain", "populate_chrome_manifest",
|
||||
"recursion"),
|
||||
warning="Linked manifest recursion detected.",
|
||||
description="A chrome registration file links back to "
|
||||
"itself. This can cause a multitude of "
|
||||
"issues.",
|
||||
filename=path)
|
||||
return
|
||||
|
||||
# Make sure the manifest is properly linked
|
||||
if path not in xpi_package:
|
||||
err.notice(
|
||||
err_id=("submain", "populate_chrome_manifest", "linkerr"),
|
||||
notice="Linked manifest could not be found.",
|
||||
description=["A linked manifest file could not be found "
|
||||
"in the package.",
|
||||
"Path: %s" % path],
|
||||
filename=from_path,
|
||||
line=from_triple["line"],
|
||||
context=from_chrome.context)
|
||||
return
|
||||
|
||||
chrome_recursion_buster.add(path)
|
||||
|
||||
manifest = ChromeManifest(xpi_package.read(path), path)
|
||||
for triple in manifest.triples:
|
||||
yield triple
|
||||
|
||||
if triple["subject"] == "manifest":
|
||||
for subtriple in get_linked_manifest(
|
||||
triple["predicate"], path, manifest, triple):
|
||||
yield subtriple
|
||||
|
||||
chrome_recursion_buster.discard(path)
|
||||
|
||||
chrome_recursion_buster.add("chrome.manifest")
|
||||
|
||||
# Search for linked manifests in the base manifest.
|
||||
for extra_manifest in chrome.get_triples(subject="manifest"):
|
||||
# When one is found, add its triples to our own.
|
||||
for triple in get_linked_manifest(extra_manifest["predicate"],
|
||||
"chrome.manifest", chrome,
|
||||
extra_manifest):
|
||||
chrome.triples.append(triple)
|
||||
|
||||
chrome_recursion_buster.discard("chrome.manifest")
|
||||
|
||||
# Create a reference so we can get the chrome manifest later, but make
|
||||
# it pushable so we don't run chrome manifests in JAR files.
|
||||
err.save_resource("chrome.manifest", chrome, pushable=True)
|
||||
# Create a non-pushable reference for tests that need to access the
|
||||
# chrome manifest from within JAR files.
|
||||
err.save_resource("chrome.manifest_nopush", chrome, pushable=False)
|
||||
|
||||
|
||||
def test_inner_package(err, xpi_package, for_appversions=None):
|
||||
"Tests a package's inner content."
|
||||
|
||||
populate_chrome_manifest(err, xpi_package)
|
||||
|
||||
# Iterate through each tier.
|
||||
for tier in sorted(decorator.get_tiers()):
|
||||
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
from validator import decorator
|
||||
from validator.chromemanifest import ChromeManifest
|
||||
|
||||
|
||||
MANIFEST_URI = "https://developer.mozilla.org/en/XUL_Tutorial/Manifest_Files"
|
||||
|
||||
@decorator.register_test(tier=2, simple=True)
|
||||
def test_categories(err):
|
||||
"""Test for categories in the chrome.manifest file."""
|
||||
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
for triple in chrome.triples:
|
||||
|
||||
if (triple["subject"] == "category" and
|
||||
(triple["predicate"] in
|
||||
("JavaScript-global-constructor",
|
||||
"JavaScript-global-constructor-prototype-alias",
|
||||
"JavaScript-global-property",
|
||||
"JavaScript-global-privileged-property",
|
||||
"JavaScript-global-static-nameset",
|
||||
"JavaScript-global-dynamic-nameset",
|
||||
"JavaScript-DOM-class",
|
||||
"JavaScript-DOM-interface") or
|
||||
(triple["predicate"] == "JavaScript" and
|
||||
(triple["object"].startswith("global ") or
|
||||
triple["object"].startswith("DOM "))))):
|
||||
err.warning(("testcases_chromemanifest",
|
||||
"test_categories",
|
||||
"js_categories"),
|
||||
"Add-on should not add JavaScript categories",
|
||||
"Add-ons should not specify categories which define "
|
||||
"properties on JavaScript globals.",
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
||||
|
||||
@decorator.register_test(tier=2, simple=True)
|
||||
def test_resourcemodules(err):
|
||||
"""Flag instances of 'resource modules' in chrome.manifest."""
|
||||
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
for triple in chrome.triples:
|
||||
if (triple["subject"] == "resource" and
|
||||
triple["predicate"].startswith("modules")):
|
||||
err.error(
|
||||
err_id=("testcases_chromemanifest", "test_resourcemodules",
|
||||
"resource_modules"),
|
||||
error="Resources should not be packages in the 'modules' "
|
||||
"namespace",
|
||||
description="There should not be resources in the "
|
||||
"chrome.manifest file that are listed as "
|
||||
"'resource modules'.",
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
||||
|
||||
@decorator.register_test(tier=3, simple=True)
|
||||
def test_content_instructions(err):
|
||||
"""Flag content instructions which are not valid."""
|
||||
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
banned_namespaces = {
|
||||
"godlikea": "The 'godlikea' namespace is generated from a "
|
||||
"template and should be replaced with something "
|
||||
"unique to your add-on to avoid name conflicts."}
|
||||
|
||||
for triple in chrome.get_triples(subject="content"):
|
||||
if not triple["predicate"] or not triple["object"]:
|
||||
err.warning(
|
||||
err_id=("testcases_chromemanifest",
|
||||
"test_content_instructions", "missing_triplicates"),
|
||||
warning="`content` instruction missing information",
|
||||
description="All content instructions must have a package "
|
||||
"name and a URI to the files it describes.",
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
continue
|
||||
|
||||
if triple["predicate"] in banned_namespaces:
|
||||
err.error(
|
||||
err_id=("testcases_chromemanifest",
|
||||
"test_content_instructions", "godlikea"),
|
||||
error="Banned namespace in chrome.manifest",
|
||||
description=banned_namespaces[triple["predicate"]],
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
elif (triple["object"] != "" and
|
||||
not triple["object"].split()[0].endswith("/")):
|
||||
err.notice(
|
||||
err_id=("testcases_chromemanifest",
|
||||
"test_content_instructions", "trailing"),
|
||||
notice="Content instruction URIs must end with trailing slash",
|
||||
description="Chrome manifest content instructions must have a "
|
||||
"trailing slash on their URI. For more "
|
||||
"information, see %s." % MANIFEST_URI,
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import fnmatch
|
||||
|
||||
from validator import decorator
|
||||
from validator.constants import *
|
||||
|
||||
|
||||
@decorator.register_test(1)
|
||||
def test_conduittoolbar(err, xpi_manager=None):
|
||||
"Find and blacklist Conduit toolbars"
|
||||
|
||||
# Ignore non-extension types
|
||||
if err.detected_type in (PACKAGE_ANY,
|
||||
PACKAGE_THEME,
|
||||
PACKAGE_SEARCHPROV):
|
||||
return None
|
||||
|
||||
# Tests regarding the install.rdf file.
|
||||
if err.get_resource("has_install_rdf"):
|
||||
|
||||
#Go out and fetch the install.rdf instance object
|
||||
install = err.get_resource("install_rdf")
|
||||
|
||||
# Define a list of specifications to search for Conduit with
|
||||
parameters = {
|
||||
"http://www.conduit.com/": install.uri("homepageURL"),
|
||||
"Conduit Ltd.": install.uri("creator"),
|
||||
"More than just a toolbar.": install.uri("description")}
|
||||
|
||||
# Iterate each specification and test for it.
|
||||
for k, uri_reference in parameters.items():
|
||||
# Retrieve the value
|
||||
results = install.get_object(None, uri_reference)
|
||||
# If the value exists, test for the appropriate content
|
||||
if results == k:
|
||||
err_mesg = "Conduit value (%s) found in install.rdf" % k
|
||||
return err.warning(("testcases_conduit",
|
||||
"test_conduittoolbar",
|
||||
"detected_rdf"),
|
||||
"Detected Conduit toolbar.",
|
||||
err_mesg,
|
||||
"install.rdf")
|
||||
|
||||
# Also test for the update URL
|
||||
update_url_value = "https://ffupdate.conduit-services.com/"
|
||||
|
||||
results = install.get_object(None, install.uri("updateURL"))
|
||||
if results and results.startswith(update_url_value):
|
||||
return err.warning(("testcases_conduit",
|
||||
"test_conduittoolbar",
|
||||
"detected_updateurl"),
|
||||
"Detected Conduit toolbar.",
|
||||
"Conduit update URL found in install.rdf.",
|
||||
"install.rdf")
|
||||
|
||||
# Do some matching on the files in the package
|
||||
conduit_files = ("components/Conduit*",
|
||||
"searchplugin/conduit*")
|
||||
for file_ in xpi_manager:
|
||||
for bad_file in conduit_files:
|
||||
# If there's a matching file, it's Conduit
|
||||
if fnmatch.fnmatch(file_, bad_file):
|
||||
return err.warning(("testcases_conduit",
|
||||
"test_conduittoolbar",
|
||||
"detected_files"),
|
||||
"Detected Conduit toolbar.",
|
||||
"Conduit directory (%s) found." % bad_file)
|
||||
|
||||
# Do some tests on the chrome.manifest file if it exists
|
||||
if "chrome.manifest" in xpi_manager:
|
||||
# Grab the chrome manifest
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
|
||||
# Get all styles for customizing the toolbar
|
||||
data = chrome.get_value("style",
|
||||
"chrome://global/content/customizeToolbar.xul")
|
||||
|
||||
# If the style exists and it contains "ebtoolbarstyle"...
|
||||
if data is not None and \
|
||||
data["object"].count("ebtoolbarstyle") > 0:
|
||||
return err.warning(("testcases_conduit",
|
||||
"test_conduittoolbar",
|
||||
"detected_chrome_manifest"),
|
||||
"Detected Conduit toolbar.",
|
||||
"'ebtoolbarstyle' found in chrome.manifest",
|
||||
filename=data["filename"],
|
||||
line=data["line"],
|
||||
context=chrome.context)
|
||||
|
|
@ -3,46 +3,21 @@ import hashlib
|
|||
import re
|
||||
from StringIO import StringIO
|
||||
|
||||
from regex import run_regex_tests
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
from validator import decorator
|
||||
from validator import submain as testendpoint_validator
|
||||
from validator import unicodehelper
|
||||
import validator.testcases.markup.markuptester as testendpoint_markup
|
||||
import validator.testcases.markup.csstester as testendpoint_css
|
||||
import validator.testcases.scripting as testendpoint_js
|
||||
import validator.testcases.langpack as testendpoint_langpack
|
||||
from validator.xpi import XPIManager
|
||||
from validator.constants import *
|
||||
from ..contextgenerator import ContextGenerator
|
||||
from .. import decorator
|
||||
from .. import submain as testendpoint_validator
|
||||
from .. import unicodehelper
|
||||
import markup.markuptester as testendpoint_markup
|
||||
import markup.csstester as testendpoint_css
|
||||
import scripting as testendpoint_js
|
||||
from ..xpi import XPIManager
|
||||
from ..constants import *
|
||||
|
||||
|
||||
FLAGGED_FILES = set([".DS_Store", "Thumbs.db"])
|
||||
FLAGGED_EXTENSIONS = set([".orig", ".old", "~"])
|
||||
OSX_REGEX = re.compile("__MACOSX")
|
||||
|
||||
@decorator.register_test(tier=1)
|
||||
def test_xpcnativewrappers(err, xpi_package=None):
|
||||
"""Tests the chrome.manifest file to ensure that it doesn't contain
|
||||
xpcnativewrappers objects."""
|
||||
|
||||
# Don't even both with the test(s) if there's no chrome.manifest.
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return None
|
||||
|
||||
for triple in chrome.triples:
|
||||
# Test to make sure that the triple's subject is valid
|
||||
if triple["subject"] == "xpcnativewrappers":
|
||||
err.warning(("testcases_content",
|
||||
"test_xpcnativewrappers",
|
||||
"found_in_chrome_manifest"),
|
||||
"xpcnativewrappers not allowed in chrome.manifest",
|
||||
"chrome.manifest files are not allowed to contain "
|
||||
"xpcnativewrappers directives.",
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
||||
|
||||
@decorator.register_test(tier=2)
|
||||
def test_packed_packages(err, xpi_package=None):
|
||||
|
@ -142,7 +117,6 @@ def test_packed_packages(err, xpi_package=None):
|
|||
parser = testendpoint_markup.MarkupParser(err)
|
||||
parser.process(name, file_data,
|
||||
xpi_package.info(name)["extension"])
|
||||
run_regex_tests(file_data, err, name)
|
||||
|
||||
# Make sure the name is prefixed with a forward slash.
|
||||
prefixed_name = name if name.startswith("/") else "/%s" % name
|
||||
|
@ -168,10 +142,6 @@ def test_packed_packages(err, xpi_package=None):
|
|||
if processed is None:
|
||||
continue
|
||||
|
||||
# This is tested in test_langpack.py
|
||||
if err.detected_type == PACKAGE_LANGPACK and not processed:
|
||||
testendpoint_langpack.test_unsafe_html(err, name, file_data)
|
||||
|
||||
# This aids in creating unit tests.
|
||||
processed_files += 1
|
||||
|
||||
|
@ -240,7 +210,6 @@ def test_packed_scripts(err, xpi_package):
|
|||
else:
|
||||
# Run the standard script tests on the scripts.
|
||||
testendpoint_js.test_js_file(err, script, file_data)
|
||||
run_regex_tests(file_data, err, script, is_js=True)
|
||||
|
||||
for i in range(len(script_bundle["state"])):
|
||||
err.pop_state()
|
||||
|
@ -323,8 +292,6 @@ def _process_file(err, xpi_package, name, file_data, name_lower,
|
|||
testendpoint_js.test_js_file(err, name, file_data,
|
||||
pollutable=pollutable)
|
||||
|
||||
run_regex_tests(file_data, err, name, is_js=is_js)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,250 +0,0 @@
|
|||
import re
|
||||
|
||||
from validator import decorator
|
||||
from validator.constants import *
|
||||
|
||||
|
||||
OPTIONS_TYPE_VALUES = ("1", "2", "3")
|
||||
VERSION_PATTERN = re.compile("^[-+*.\w]{,32}$")
|
||||
|
||||
|
||||
@decorator.register_test(tier=1)
|
||||
def test_install_rdf_params(err, xpi_package=None):
|
||||
"""Tests to make sure that some of the values in install.rdf
|
||||
are not gummed up."""
|
||||
|
||||
if not err.get_resource("has_install_rdf"):
|
||||
return
|
||||
|
||||
install = err.get_resource("install_rdf")
|
||||
|
||||
# This returns for testing reasons
|
||||
return _test_rdf(err, install)
|
||||
|
||||
|
||||
def _test_rdf(err, install):
|
||||
"""Wrapper for install.rdf testing to make unit testing so much
|
||||
easier."""
|
||||
|
||||
shouldnt_exist = ("hidden", )
|
||||
if err.get_resource("listed"):
|
||||
shouldnt_exist += ("updateURL", "updateKey", )
|
||||
obsolete = ("file", "skin", "requires", )
|
||||
must_exist_once = ["id",
|
||||
"version",
|
||||
"name",
|
||||
"targetApplication"]
|
||||
|
||||
may_exist_once = ["about", # For <Description> element
|
||||
"bootstrap",
|
||||
"optionsURL",
|
||||
"aboutURL",
|
||||
"iconURL",
|
||||
"icon64URL",
|
||||
"homepageURL",
|
||||
"creator",
|
||||
"optionsType",
|
||||
"type",
|
||||
"updateInfoURL",
|
||||
"updateKey",
|
||||
"updateURL",
|
||||
"updateLink", # Banned, but if not, pass it once.
|
||||
"updateHash",
|
||||
"signature",
|
||||
"skinnable",
|
||||
"strictCompatibility",
|
||||
"unpack"] # This has other rules; CAUTION!
|
||||
|
||||
# Support a name requirement override.
|
||||
if (err.overrides and
|
||||
"ignore_empty_name" in err.overrides and
|
||||
err.overrides["ignore_empty_name"]):
|
||||
must_exist_once.remove("name")
|
||||
may_exist_once.append("name")
|
||||
|
||||
may_exist = ("targetApplication",
|
||||
"localized",
|
||||
"description",
|
||||
"creator",
|
||||
"translator",
|
||||
"contributor",
|
||||
"targetPlatform",
|
||||
"requires",
|
||||
"developer",)
|
||||
|
||||
if err.detected_type == PACKAGE_THEME or \
|
||||
(err.subpackages and
|
||||
err.subpackages[0]["detected_type"] == PACKAGE_THEME):
|
||||
must_exist_once.append("internalName")
|
||||
|
||||
top_id = install.get_root_subject()
|
||||
|
||||
for pred_raw in install.rdf.predicates(top_id, None):
|
||||
predicate = pred_raw.split("#")[-1]
|
||||
|
||||
# Mark that the unpack element has been supplied
|
||||
if predicate == "unpack":
|
||||
value = install.get_object(predicate=pred_raw)
|
||||
err.save_resource("em:unpack", value, pushable=True)
|
||||
|
||||
if predicate == "bootstrap":
|
||||
value = install.get_object(predicate=pred_raw)
|
||||
err.save_resource("em:bootstrap", value)
|
||||
|
||||
# Test if the predicate is banned
|
||||
if predicate in shouldnt_exist:
|
||||
err.error(("testcases_installrdf",
|
||||
"_test_rdf",
|
||||
"shouldnt_exist"),
|
||||
"Banned element in install.rdf",
|
||||
"""The element "%s" was found in the add-on's
|
||||
install.rdf file. It is not allowed in add-ons under
|
||||
the current configuration.""" % predicate,
|
||||
"install.rdf")
|
||||
continue
|
||||
|
||||
# Test if the predicate is obsolete
|
||||
if predicate in obsolete:
|
||||
err.notice(("testcases_installrdf",
|
||||
"_test_rdf",
|
||||
"obsolete"),
|
||||
"Obsolete element in install.rdf",
|
||||
"The element \"%s\" was found in the add-on's "
|
||||
"install.rdf file. It has not been banned, but it is "
|
||||
"no longer supported by any modern Mozilla product. "
|
||||
"Removing the element is recommended and will not "
|
||||
"break support." % predicate,
|
||||
"install.rdf")
|
||||
continue
|
||||
|
||||
# Remove the predicate from must_exist_once if it's there.
|
||||
if predicate in must_exist_once:
|
||||
|
||||
object_value = install.get_object(top_id, pred_raw)
|
||||
|
||||
# Test the predicate for specific values.
|
||||
if predicate == "id":
|
||||
_test_id(err, object_value)
|
||||
elif predicate == "version":
|
||||
_test_version(err, object_value)
|
||||
elif predicate == "name":
|
||||
_test_name(err, object_value)
|
||||
|
||||
must_exist_once.remove(predicate)
|
||||
continue
|
||||
|
||||
# Do the same for may_exist_once.
|
||||
if predicate in may_exist_once:
|
||||
|
||||
object_value = install.get_object(top_id, pred_raw)
|
||||
|
||||
if (predicate == "optionsType" and
|
||||
str(object_value) not in OPTIONS_TYPE_VALUES):
|
||||
err.warning(
|
||||
err_id=("testcases_installrdf", "_test_rdf",
|
||||
"optionsType"),
|
||||
warning="<em:optionsType> has bad value.",
|
||||
description=["The value of <em:optionsType> must be either "
|
||||
"%s." % ", ".join(OPTIONS_TYPE_VALUES),
|
||||
"Value found: %s" % object_value],
|
||||
filename="install.rdf")
|
||||
|
||||
may_exist_once.remove(predicate)
|
||||
continue
|
||||
|
||||
# If the element is safe for repetition, continue
|
||||
if predicate in may_exist:
|
||||
continue
|
||||
|
||||
# If the predicate isn't in any of the above lists, it is
|
||||
# invalid and needs to go.
|
||||
err.notice(("testcases_installrdf",
|
||||
"_test_rdf",
|
||||
"unrecognized"),
|
||||
"Unrecognized element in install.rdf",
|
||||
["An element was found in the install manifest, however it "
|
||||
"does not appear to be a part of the specification, it "
|
||||
"has been used too many times, or is not applicable to "
|
||||
"the current configuration.",
|
||||
"Detected element: <em:%s>" % predicate],
|
||||
"install.rdf")
|
||||
|
||||
# Once all of the predicates have been tested, make sure there are
|
||||
# no mandatory elements that haven't been found.
|
||||
if must_exist_once:
|
||||
missing_preds = ', '.join(must_exist_once)
|
||||
err.error(("testcases_installrdf",
|
||||
"_test_rdf",
|
||||
"missing_addon"),
|
||||
"install.rdf missing element(s).",
|
||||
["The element listed is a required element in the install "
|
||||
"manifest specification. It must be added to your addon.",
|
||||
"Missing elements: %s" % ", ".join(must_exist_once)],
|
||||
"install.rdf")
|
||||
|
||||
|
||||
def _test_id(err, value):
|
||||
"Tests an install.rdf UUID value"
|
||||
|
||||
id_pattern = re.compile("(\{[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\}|[a-z0-9-\.\+_]*\@[a-z0-9-\._]+)", re.I)
|
||||
|
||||
err.metadata["id"] = value
|
||||
|
||||
# Must be a valid UUID string.
|
||||
if not id_pattern.match(value):
|
||||
err.error(("testcases_installrdf",
|
||||
"_test_id",
|
||||
"invalid"),
|
||||
"The value of <em:id> is invalid.",
|
||||
["The values supplied for <em:id> in the install.rdf file is "
|
||||
"not a valid UUID string or email address.",
|
||||
"For help, please consult: "
|
||||
"https://developer.mozilla.org/en/install_manifests#id"],
|
||||
"install.rdf")
|
||||
|
||||
|
||||
def _test_version(err, value):
|
||||
"Tests an install.rdf version number"
|
||||
|
||||
|
||||
err.metadata["version"] = value
|
||||
|
||||
# May not be longer than 32 characters
|
||||
if len(value) > 32:
|
||||
err.error(("testcases_installrdf",
|
||||
"_test_version",
|
||||
"too_long"),
|
||||
"The value of <em:version> is too long",
|
||||
"Values supplied for <em:version> in the install.rdf file "
|
||||
"must be 32 characters or less.",
|
||||
"install.rdf")
|
||||
|
||||
# Must be a valid version number.
|
||||
if not VERSION_PATTERN.match(value):
|
||||
err.error(("testcases_installrdf",
|
||||
"_test_version",
|
||||
"invalid_format"),
|
||||
"The value of <em:version> is invalid.",
|
||||
"The values supplied for <em:version> in the install.rdf "
|
||||
"file is not a valid version string. It can only contain "
|
||||
"letters, numbers, and the symbols +*.-_.",
|
||||
"install.rdf")
|
||||
|
||||
|
||||
def _test_name(err, value):
|
||||
"Tests an install.rdf name value for trademarks."
|
||||
|
||||
ff_pattern = re.compile("(mozilla|firefox)", re.I)
|
||||
|
||||
err.metadata["name"] = value
|
||||
|
||||
if ff_pattern.match(value):
|
||||
err.warning(("testcases_installrdf",
|
||||
"_test_name",
|
||||
"trademark"),
|
||||
"Add-on has potentially illegal name.",
|
||||
"Add-on names cannot contain the Mozilla or Firefox "
|
||||
"trademarks. These names should not be contained in add-on "
|
||||
"names if at all possible.",
|
||||
"install.rdf")
|
||||
|
|
@ -6,8 +6,7 @@ import types
|
|||
import spidermonkey
|
||||
import instanceactions
|
||||
import instanceproperties
|
||||
from validator.constants import BUGZILLA_BUG, FENNEC_GUID, FIREFOX_GUID
|
||||
from validator.decorator import version_range
|
||||
from appvalidator.constants import BUGZILLA_BUG, FENNEC_GUID, FIREFOX_GUID
|
||||
from jstypes import *
|
||||
|
||||
|
||||
|
@ -450,28 +449,6 @@ def _call_create_pref(a, t, e):
|
|||
"distinct string unique to and indicative of your add-on.")
|
||||
|
||||
|
||||
def _readonly_top(t, r, rn):
|
||||
"""Handle the readonly callback for window.top."""
|
||||
t.err.notice(
|
||||
err_id=("testcases_javascript_actions",
|
||||
"_readonly_top"),
|
||||
notice="window.top is a reserved variable",
|
||||
description="The 'top' global variable is reserved and cannot be "
|
||||
"assigned any values starting with Gecko 6. Review your "
|
||||
"code for any uses of the 'top' global, and refer to "
|
||||
"%s for more information." % BUGZILLA_BUG % 654137,
|
||||
filename=t.filename,
|
||||
line=t.line,
|
||||
column=t.position,
|
||||
context=t.context,
|
||||
for_appversions={FIREFOX_GUID:
|
||||
version_range("firefox", "6.0a1", "7.0a1"),
|
||||
FENNEC_GUID:
|
||||
version_range("fennec", "6.0a1", "7.0a1")},
|
||||
compatibility_type="warning",
|
||||
tier=5)
|
||||
|
||||
|
||||
def _expression(traverser, node):
|
||||
"Evaluates an expression and returns the result"
|
||||
result = traverser._traverse_node(node["expression"])
|
||||
|
@ -597,22 +574,6 @@ def _expr_assignment(traverser, node):
|
|||
traverser._debug("ASSIGNMENT:DIRECT:GLOB_OVERWRITE %s" %
|
||||
global_overwrite)
|
||||
|
||||
if (global_overwrite and
|
||||
not traverser.is_jsm and
|
||||
readonly_value == True):
|
||||
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_actions",
|
||||
"_expr_assignment",
|
||||
"global_overwrite"),
|
||||
warning="Global variable overwrite",
|
||||
description="An attempt was made to overwrite a global "
|
||||
"variable in some JavaScript code.",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
|
||||
if isinstance(readonly_value, types.LambdaType):
|
||||
# The readonly attribute supports a lambda function that accepts
|
||||
readonly_value(t=traverser, r=right, rn=node["right"])
|
||||
|
|
|
@ -7,11 +7,7 @@ import actions
|
|||
import traverser as js_traverser
|
||||
import predefinedentities
|
||||
from jstypes import *
|
||||
from validator.constants import BUGZILLA_BUG
|
||||
from validator.compat import (FX6_DEFINITION, FX7_DEFINITION, FX8_DEFINITION,
|
||||
FX9_DEFINITION, FX11_DEFINITION, TB12_DEFINITION,
|
||||
TB13_DEFINITION)
|
||||
from validator.decorator import version_range
|
||||
from appvalidator.constants import BUGZILLA_BUG
|
||||
|
||||
# Function prototypes should implement the following:
|
||||
# wrapper : The JSWrapper instace that is being called
|
||||
|
@ -19,215 +15,6 @@ from validator.decorator import version_range
|
|||
# traverser : The current traverser object
|
||||
|
||||
|
||||
def amp_rp_bug660359(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag all calls to AddonManagerPrivate.registerProvider for incompatibility
|
||||
with Gecko 6.
|
||||
"""
|
||||
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions",
|
||||
"amp_rp_bug660359"),
|
||||
notice="Custom add-on types may not work properly in Gecko 6",
|
||||
description="This add-on appears to register custom add-on types, "
|
||||
"which are affected and may not work properly due to "
|
||||
"changes made on Gecko 6. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=595848",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX6_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def urlparser_parsepath_bug691588(wrapper, arguments, traverser):
|
||||
"""
|
||||
nsIURLParser.parsePath doesn't take paramPos/paramLen in FX9.
|
||||
"""
|
||||
if len(arguments) > 8:
|
||||
traverser.err.error(
|
||||
("testcases_javascript_call_definititions",
|
||||
"fx9_compat",
|
||||
"urlparser_691588"),
|
||||
("nsIURLParser.parsePath's signature has changed in Gecko 9."
|
||||
" See %s for more information.") % (BUGZILLA_BUG % 665706),
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=traverser.filename, line=traverser.line,
|
||||
column=traverser.position, context=traverser.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def url_param_bug691588(t):
|
||||
"""
|
||||
nsIURL.param is gone in FX9.
|
||||
"""
|
||||
t.err.error(
|
||||
err_id=("testcases_javascript_call_definititions", "fx9_compat",
|
||||
"urlparser_691588"),
|
||||
error="`nsIURL.param` has been removed in Gecko 9.",
|
||||
description="See %s for more information." % BUGZILLA_BUG % 665706,
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=t.filename,
|
||||
line=t.line,
|
||||
column=t.position,
|
||||
context=t.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def browserhistory_removepages(wrapper, arguments, traverser):
|
||||
"""
|
||||
nsIBrowserHistory.removePages takes 2 args in FX9 instead of 3.
|
||||
"""
|
||||
if len(arguments) > 2:
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_call_definititions",
|
||||
"fx9_compat", "browserhistory_removepages"),
|
||||
error="nsIBrowser.removePages' signature has changed in Gecko 9.",
|
||||
description="See %s for more information." %
|
||||
BUGZILLA_BUG % 681420,
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def browserhistory_registeropenpage(t):
|
||||
"""
|
||||
nsIBrowser.registerOpenPage is gone in Gecko 9.
|
||||
"""
|
||||
t.err.error(
|
||||
err_id=("testcases_javascript_call_definititions",
|
||||
"fx9_compat", "browserhistory_registeropenpage"),
|
||||
error="nsIBrowser.registerOpenPage has been removed in Gecko 9.",
|
||||
description="See %s for more information." % BUGZILLA_BUG % 681420,
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=t.filename, line=t.line,
|
||||
column=t.position, context=t.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def browserhistory_unregisteropenpage(t):
|
||||
"""
|
||||
nsIBrowser.unregisterOpenPage is gone in Gecko 9.
|
||||
"""
|
||||
t.err.error(
|
||||
err_id=("testcases_javascript_call_definititions",
|
||||
"fx9_compat", "browserhistory_unregisteropenpage"),
|
||||
error="nsIBrowser.unregisterOpenPage has been removed in Gecko 9.",
|
||||
description="See %s for more information." % BUGZILLA_BUG % 681420,
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=t.filename, line=t.line,
|
||||
column=t.position, context=t.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def spellcheck_savedefaultdictionary(t):
|
||||
"""
|
||||
nsIEditorSpellCheck.saveDefaultDictionary is gone in Gecko 9.
|
||||
"""
|
||||
t.err.error(
|
||||
("testcases_javascript_call_definititions",
|
||||
"fx9_compat",
|
||||
"spellcheck_savedefaultdictionary"),
|
||||
("nsIEditorSpellCheck.saveDefaultDictionary has been removed in"
|
||||
" Gecko 9. See %s for more information.") % (BUGZILLA_BUG % 678842),
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=t.filename, line=t.line,
|
||||
column=t.position, context=t.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def spellcheck_updatecurrentdictionary(wrapper, arguments, traverser):
|
||||
"""
|
||||
nsIEditorSpellCheck.UpdateCurrentDictionary takes no args in Gecko 9.
|
||||
"""
|
||||
if len(arguments) > 0:
|
||||
traverser.err.error(
|
||||
("testcases_javascript_call_definititions",
|
||||
"fx9_compat",
|
||||
"spellcheck_updatecurrentdictionary"),
|
||||
("nsIEditorSpellCheck.UpdateCurrentDictionary takes no arguments "
|
||||
"in Gecko 9. See %s for more information."
|
||||
) % (BUGZILLA_BUG % 678842),
|
||||
for_appversions=FX9_DEFINITION,
|
||||
filename=traverser.filename, line=traverser.line,
|
||||
column=traverser.position, context=traverser.context,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def xpcom_constructor(method, extend=False, mutate=False, pretraversed=False):
|
||||
"""Returns a function which wraps an XPCOM class instantiation function."""
|
||||
|
||||
def definition(wrapper, arguments, traverser):
|
||||
"""Wraps an XPCOM class instantiation function."""
|
||||
|
||||
if not arguments:
|
||||
return None
|
||||
|
||||
traverser._debug("(XPCOM Encountered)")
|
||||
|
||||
if not pretraversed:
|
||||
arguments = [traverser._traverse_node(x) for x in arguments]
|
||||
argz = arguments[0]
|
||||
|
||||
if not argz.is_global or "xpcom_map" not in argz.value:
|
||||
argz = JSWrapper(traverser=traverser)
|
||||
argz.value = {"xpcom_map": lambda: {"value": {}}}
|
||||
|
||||
traverser._debug("(Building XPCOM...)")
|
||||
|
||||
inst = traverser._build_global(method,
|
||||
copy.deepcopy(argz.value["xpcom_map"]()))
|
||||
inst.value["overwritable"] = True
|
||||
|
||||
if extend or mutate:
|
||||
# FIXME: There should be a way to get this without
|
||||
# traversing the call chain twice.
|
||||
parent = actions.trace_member(traverser, wrapper["callee"]["object"])
|
||||
|
||||
if mutate and not (parent.is_global and
|
||||
isinstance(parent.value, dict) and
|
||||
"value" in parent.value):
|
||||
# Assume that the parent object is a first class
|
||||
# wrapped native
|
||||
parent.value = inst.value
|
||||
|
||||
# FIXME: Only objects marked as global are processed
|
||||
# as XPCOM instances
|
||||
parent.is_global = True
|
||||
|
||||
if isinstance(parent.value, dict):
|
||||
if extend and mutate:
|
||||
if callable(parent.value["value"]):
|
||||
parent.value["value"] = \
|
||||
copy.deepcopy(parent.value["value"](t=traverser))
|
||||
|
||||
parent.value["value"].update(inst.value["value"])
|
||||
return parent
|
||||
|
||||
if extend:
|
||||
inst.value["value"].update(parent.value["value"])
|
||||
|
||||
if mutate:
|
||||
parent.value = inst.value
|
||||
|
||||
return inst
|
||||
definition.__name__ = "xpcom_%s" % str(method)
|
||||
return definition
|
||||
|
||||
|
||||
# Global object function definitions:
|
||||
def string_global(wrapper, arguments, traverser):
|
||||
if not arguments:
|
||||
|
@ -359,488 +146,6 @@ def math_round(wrapper, arguments, traverser):
|
|||
return JSWrapper(arg, traverser=traverser)
|
||||
|
||||
|
||||
def nsIDOMFile_deprec(wrapper, arguments, traverser):
|
||||
"""Throw a compatibility error about removed XPCOM methods."""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIDOMFile",
|
||||
"deprec"),
|
||||
notice="Deprecated nsIDOMFile methods in use.",
|
||||
description=("Your add-on uses methods that have been removed from "
|
||||
"the nsIDOMFile interface in Gecko 7. Please refer to "
|
||||
"%s for more information.") % (BUGZILLA_BUG % 661876),
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=FX7_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIJSON_deprec(wrapper, arguments, traverser):
|
||||
"""Throw a compatibility error about removed XPCOM methods."""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIJSON",
|
||||
"deprec"),
|
||||
notice="Deprecated nsIJSON methods in use.",
|
||||
description=("The encode and decode methods in nsIJSON have been "
|
||||
"deprecated in Gecko 7. You can use the methods in the "
|
||||
"global JSON object instead. See %s for more "
|
||||
"information.") %
|
||||
"https://developer.mozilla.org/En/Using_native_JSON",
|
||||
#"%s for more information.") % (BUGZILLA_BUG % 645922),
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="warning",
|
||||
for_appversions=FX7_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
def nsIImapMailFolderSink_changed(wrapper, arguments, traverser):
|
||||
"""Flag calls to nsIImapMailFolderSink for possible incompatibility with Thunderbird 6"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIImapMailFolderSink"),
|
||||
notice="Modified nsIImapMailFolderSink method in use.",
|
||||
description="This add-on appears to use nsIImapMailFolderSink.setUrlState, "
|
||||
"which may no longer work correctly due to "
|
||||
"changes made in Thunderbird 6. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=464126",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "6.0a1", "8.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
def nsIImapProtocol_removed(wrapper, arguments, traverser):
|
||||
"""Flag calls to nsIImapProtocol for incompatibility with Thunderbird 6"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIImapProtocol"),
|
||||
notice="Removed nsIImapProtocol method in use.",
|
||||
description="This add-on appears to use nsIImapProtocol.NotifyHdrsToDownload, "
|
||||
"which may no longer work correctly due to "
|
||||
"changes made in Thunderbird 6. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=464126",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "6.0a1", "8.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def document_getSelection(wrapper, arguments, traverser):
|
||||
"""Flag Gecko 8 calls to document.getSelection()."""
|
||||
|
||||
MDN_ARTICLE = "https://developer.mozilla.org/En/Window.getSelection"
|
||||
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "document_getSel"),
|
||||
notice="document.getSelection()'s return type has changed.",
|
||||
description="The return type of document.getSelection() has changed "
|
||||
"in Gecko 8. This function is deprecated, and you "
|
||||
"should be using window.getSelection() instead. See "
|
||||
"%s for more information." % MDN_ARTICLE,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=FX8_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
# The new spec returns an object.
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMsgThread_removed(wrapper, arguments, traverser):
|
||||
"""Flag calls to nsIMsgThread for incompatibility with Thunderbird 7"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgThread"),
|
||||
notice="Removed nsIMsgThread method in use.",
|
||||
description="This add-on appears to use nsIMsgThread.GetChildAt, "
|
||||
"which may no longer work correctly due to "
|
||||
"changes made in Thunderbird 7. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=617839",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "7.0a1", "8.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def mail_attachment_api(wrapper, arguments, traverser):
|
||||
"""Flag calls to the global attachment functions for incompatibility with Thunderbird 7"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "AttachmentAPI"),
|
||||
notice="Removed attachment API function in use.",
|
||||
description="This add-on appears to use a global attachment function, one of: "
|
||||
"attachmentIsEmpty, cloneAttachment, createNewAttachmentInfo "
|
||||
"detachAttachment, openAttachment or saveAttachment, "
|
||||
"which were removed in Thunderbird 7. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=657856",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "7.0a1", "8.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMsgSearchScopeTerm_removed(wrapper, arguments, traverser):
|
||||
"""Flag calls to nsIMsgSearchScopeTerm methods for incompatibility with Thunderbird 8"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgSearchScopeTerm"),
|
||||
notice="Removed nsIMsgSearchScopeTerm method in use.",
|
||||
description="This add-on appears to use nsIMsgSearchScopeTerm.mailFile or, "
|
||||
"nsIMsgSearchScopeTerm.inputStream, both of which have been removed"
|
||||
"as part of changes made in Thunderbird 8. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=668700",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "8.0a1", "9.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def gComposeBundle_removed(wrapper, arguments, traverser):
|
||||
"""Flag uses of gComposeBundle for incompatibility with Thunderbird 9"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "gComposeBundle"),
|
||||
notice="Removed gComposeBundle global variable in use.",
|
||||
description="This add-on appears to use gComposeBundle which has been removed "
|
||||
"as part of changes made in Thunderbird 9. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=670639",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "9.0a1", "10.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def TB9FocusFunctions_removed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag calls to WhichPaneHasFocus and FocusOnFirstAttachment
|
||||
for incompatibility with Thunderbird 9
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "WhichPaneHasFocus"),
|
||||
notice="Removed WhichPaneHasFocus or FocusOnFirstAttachment function in use.",
|
||||
description="This add-on appears to use WhichPaneHasFocus "
|
||||
"or FocusOnFirstAttachment which have been removed "
|
||||
"as part of changes made in Thunderbird 9. For more information, "
|
||||
"please refer to "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=581932",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "9.0a1", "10.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def TB10Function_removed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag calls to MsgDeleteMessageFromMessageWindow and
|
||||
goToggleSplitter for incompatibility with Thunderbird 10
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "MsgDeleteMessageFromMessageWindow"),
|
||||
notice="Removed MsgDeleteMessageFromMessageWindow or goToggleSplitter function in use.",
|
||||
description="This add-on appears to use MsgDeleteMessageFromMessageWindow "
|
||||
"or goToggleSplitter which have been removed "
|
||||
"as part of changes made in Thunderbird 10. For more information, "
|
||||
"please refer to https://bugzilla.mozilla.org/show_bug.cgi?id=702201 and "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=609245",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "10.0a1", "11.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def TB10Function_renamed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag calls to AddMessageComposeOfflineObserver and
|
||||
RemoveMessageComposeOfflineObserver for incompatibility with Thunderbird 10
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "AddMessageComposeOfflineObserver"),
|
||||
notice="Removed AddMessageComposeOfflineObserver or goToggleSplitter function in use.",
|
||||
description="This add-on appears to use AddMessageComposeOfflineObserver or "
|
||||
"RemoveMessageComposeOfflineObserver which have been renamed to "
|
||||
"AddMessageComposeOfflineQuitObserver and RemoveMessageComposeOfflineQuitObserver "
|
||||
"respectively as part of changes made in Thunderbird 10. For more information, "
|
||||
"please refer to https://bugzilla.mozilla.org/show_bug.cgi?id=682581",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "10.0a1", "11.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMsgQuote_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag calls to nsIMsgQuote.quoteMessage for incompatibility with Thunderbird 11
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgQuote"),
|
||||
notice="Altered nsIMsgQuote.quoteMessage function in use.",
|
||||
description="This add-on appears to use nsIMsgQuote.quoteMessage which had the argument aOrigHdr"
|
||||
"added as part of changes made in Thunderbird 11. For more information, "
|
||||
"please refer to https://bugzilla.mozilla.org/show_bug.cgi?id=351109",
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "11.0a1", "12.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIComm4xProfile_removed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag use of nsIComm4xProfile for incompatibility with Thunderbird 11
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIComm4xProfile"),
|
||||
notice="Removed nsIComm4xProfile interface in use.",
|
||||
description="This add-on appears to use nsIComm4xProfile which was "
|
||||
"removed as part of changes made in Thunderbird 11. For "
|
||||
"more information, please refer to %s." %
|
||||
BUGZILLA_BUG % 689437,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "11.0a1", "12.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMailtoUrl_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag calls to nsIMailtoUrl.GetMessageContents for incompatibility with
|
||||
Thunderbird 11.
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgQuote"),
|
||||
notice="Altered nsIMsgQuote.quoteMessage function in use.",
|
||||
description="This add-on appears to use nsIMailtoUrl."
|
||||
"GetMessageContents which was changed to"
|
||||
"nsIMailtoUrl.getMessageContents (lower case g) as part "
|
||||
"of Thunderbird 11. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 711980,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions={'{3550f703-e582-4d05-9a08-453d09bdfdc6}':
|
||||
version_range("thunderbird", "11.0a1", "12.0a1")},
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMsgFolder_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag use of nsIMsgFolder.offlineStoreOutputStream for incompatibility with
|
||||
Thunderbird 12.
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgFolder"),
|
||||
notice="Altered nsIMsgFolder.offlineStoreOutputStream attr in use.",
|
||||
description="This add-on appears to use nsIMsgFolder."
|
||||
"offlineStoreOutputStream which was replaced with "
|
||||
"method getOfflineStoreOutputStream(in nsIMsgDBHdr aHdr) "
|
||||
"in Thunderbird 12. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 402392,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=TB12_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def nsIMsgDatabase_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag use of nsIMsgDatabase related methods for incompatibility with
|
||||
Thunderbird 12.
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgDatabase"),
|
||||
notice="Altered nsIDatabase methods in use.",
|
||||
description="This add-on appears to use nsIMsgDatabase::Open, "
|
||||
"nsIMsgDBService::openMailDBFromFile, or "
|
||||
"nsIMsgOutputStream.folderStream which have been changed "
|
||||
"in Thunderbird 12. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 402392,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=TB12_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def TB12_nsIImapProtocol_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag use of nsIImapProtocol::Initialize and
|
||||
nsIImapIncomingServer::GetImapConnectionAndLoadUrl for incompatibility
|
||||
with Thunderbird 12.
|
||||
"""
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIImapProtocol"),
|
||||
notice="Altered nsIImapProtocol or IncomingServer methods in use.",
|
||||
description="This add-on uses nsIImapProtocol::Initialize or "
|
||||
"nsIImapIncomingServer::GetImapConnectionAndLoadUrl "
|
||||
"which had parameters removed "
|
||||
"in Thunderbird 12. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 704707,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=TB12_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
|
||||
def TB13_nsIMsgLocalMailFolder_changed(wrapper, arguments, traverser):
|
||||
"""
|
||||
Flag use of nsIMsgLocalMailFolder::addMessage and addMessageBatch
|
||||
with Thunderbird 13.
|
||||
"""
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgLocalMailFolder"),
|
||||
warning="Altered nsIMsgLocalMailFolder methods in use.",
|
||||
description="This add-on uses nsIMsgLocalMailFolder::addMessage or "
|
||||
"nsIMsgLocalMailFolder::addMessageBatch "
|
||||
"which had their return values altered "
|
||||
"in Thunderbird 13. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 647699,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=TB13_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
def TB13_nsIMsgNewsFolder_changed(wrapper, arguments, traverser):
|
||||
"""Flag use of several nsIMsgNewsFolder methods with Thunderbird 13."""
|
||||
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_calldefinitions", "nsIMsgNewsFolder"),
|
||||
warning="Altered nsIMsgNewsFolder methods in use.",
|
||||
description="This add-on uses nsIMsgNewsFolder::getGroupPasswordWithUI"
|
||||
" getGroupUsernameWithUI(), forgetGroupUsername() "
|
||||
"or forgetGroupPassword() which were removed "
|
||||
"in Thunderbird 13. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 201750,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=TB13_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
return JSWrapper(JSObject(), traverser=traverser, dirty=True)
|
||||
|
||||
def requestAnimationFrame(wrapper, arguments, traverser):
|
||||
"""
|
||||
As of FX11, requestAnimationFrame should be called with at least one
|
||||
parameter.
|
||||
"""
|
||||
if arguments:
|
||||
return
|
||||
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_js_actions", "requestAnimationFrame", "no_args"),
|
||||
warning="requestAnimationFrame now requires one parameter",
|
||||
description="The requestAnimationFrame function now requires one "
|
||||
"parameter and can't be called without any arguments. "
|
||||
"See %s for more information." % BUGZILLA_BUG % 704171,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
compatibility_type="error",
|
||||
for_appversions=FX11_DEFINITION,
|
||||
tier=5)
|
||||
|
||||
|
||||
def js_wrap(wrapper, arguments, traverser):
|
||||
"""Return the wrapped variant of an unwrapped JSObject."""
|
||||
if not arguments:
|
||||
|
@ -879,32 +184,3 @@ def js_unwrap(wrapper, arguments, traverser):
|
|||
obj.value.is_unwrapped = True
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def open_in_chrome_context(uri, method, traverser):
|
||||
if not uri.is_literal():
|
||||
traverser.err.notice(
|
||||
err_id=("js", "instanceactions", "%s_nonliteral" % method),
|
||||
notice="`%s` called with non-literal parameter." % method,
|
||||
description="Calling `%s` with variable parameters can result in "
|
||||
"potential security vulnerabilities if the variable "
|
||||
"contains a remote URI. Consider using `window.open` "
|
||||
"with the `chrome=no` flag." % method,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
|
||||
remote_url = re.compile(r"^(https?|ftp|data):(//)?", re.I)
|
||||
uri = unicode(uri.get_literal_value())
|
||||
if uri.startswith("//") or remote_url.match(uri):
|
||||
traverser.err.warning(
|
||||
err_id=("js", "instanceactions", "%s_remote_uri" % method),
|
||||
warning="`%s` called with non-local URI." % method,
|
||||
description="Calling `%s` with a non-local URI will result in the "
|
||||
"dialog being opened with chrome privileges." % method,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
from call_definitions import open_in_chrome_context
|
||||
from validator.compat import FX10_DEFINITION, FX14_DEFINITION, TB14_DEFINITION
|
||||
from validator.constants import BUGZILLA_BUG
|
||||
from appvalidator.constants import BUGZILLA_BUG
|
||||
|
||||
|
||||
ENTITIES = {}
|
||||
|
@ -26,179 +24,4 @@ def entity(name, result=None):
|
|||
return {"value": return_wrap}
|
||||
|
||||
|
||||
def deprecated_entity(name, version, message, bug, status="deprecated",
|
||||
compat_type="error"):
|
||||
def wrap(traverser):
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_entity_values", name),
|
||||
warning="`%s` has been %s." % (name, status),
|
||||
description=[message,
|
||||
"See %s for more information." % BUGZILLA_BUG % bug],
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=version,
|
||||
compatibility_type=compat_type,
|
||||
tier=5)
|
||||
register_entity(name)(wrap)
|
||||
|
||||
DEP_IHF_MESSAGE = ("The `importHTMLFromFile` and `importHTMLFromURI` functions "
|
||||
"have been removed from the `nsIPlacesImportExportService` "
|
||||
"interface. You can use the equivalent functions in the "
|
||||
"`BookmarkHTMLUtils` module instead.")
|
||||
deprecated_entity(name="importHTMLFromFile", version=FX14_DEFINITION,
|
||||
message=DEP_IHF_MESSAGE, bug=482911)
|
||||
deprecated_entity(name="importHTMLFromURI", version=FX14_DEFINITION,
|
||||
message=DEP_IHF_MESSAGE, bug=482911)
|
||||
|
||||
|
||||
@register_entity("document.xmlEncoding")
|
||||
def xmlEncoding(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values", "xmlEncoding"),
|
||||
error="xmlEncoding removed in Gecko 10.",
|
||||
description='The "xmlEncoding" property has been removed. See %s for '
|
||||
'more information.' % BUGZILLA_BUG % 687426,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("document.xmlVersion")
|
||||
def xmlVersion(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values", "xmlVersion"),
|
||||
error="xmlVersion removed in Gecko 10.",
|
||||
description='The "xmlVersion" property has been removed. See %s for '
|
||||
'more information.' % BUGZILLA_BUG % 687426,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("document.xmlStandalone")
|
||||
def xmlStandalone(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values", "xmlStandalone"),
|
||||
error="xmlStandalone removed in Gecko 10.",
|
||||
description='The "xmlStandalone" property has been removed. See %s for '
|
||||
'more information.' % BUGZILLA_BUG % 687426,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("nsIDOMNSHTMLElement")
|
||||
def nsIDOMNSHTMLElement(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values",
|
||||
"nsIDOMNSHTMLFrameElement"),
|
||||
error="nsIDOMNSHTMLElement interface removed in Gecko 10.",
|
||||
description='The "nsIDOMNSHTMLElement" interface has been removed. '
|
||||
'You can use nsIDOMHTMLFrameElement instead. See %s for '
|
||||
'more information.' % BUGZILLA_BUG % 684821,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("nsIDOMNSHTMLFrameElement")
|
||||
def nsIDOMNSHTMLFrameElement(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values",
|
||||
"nsIDOMNSHTMLFrameElement"),
|
||||
error="nsIDOMNSHTMLFrameElement interface removed in Gecko 10.",
|
||||
description='The "nsIDOMNSHTMLFrameElement" interface has been '
|
||||
'removed. You can use nsIDOMHTMLFrameElement instead. See '
|
||||
'%s for more information.' % BUGZILLA_BUG % 677085,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("nsIBrowserHistory.lastPageVisited")
|
||||
def nsIBrowserHistory(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_entity_values",
|
||||
"nsIBrowserHistory_lastPageVisited"),
|
||||
error="lastPageVisited property has been removed in Gecko 10.",
|
||||
description='The "lastPageVisited" property has been removed. See %s '
|
||||
'for more information.' % BUGZILLA_BUG % 691524,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("nsIDOMHTMLDocument")
|
||||
def queryCommandText(traverser):
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_entity_values",
|
||||
"nsIDOMHTMLDocument"),
|
||||
warning="`queryCommandText` and `execCommandShowHelp` removed.",
|
||||
description="The `queryCommandText` and `execCommandShowHelp` methods "
|
||||
"have been removed from the `nsIDOMHTMLDocument` interface "
|
||||
"in Gecko 14. See %s for more information." %
|
||||
BUGZILLA_BUG % 742261,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX14_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
@register_entity("nsIWindowWatcher.openWindow")
|
||||
def nsIWindowWatcher_openWindow(traverser):
|
||||
def on_open(wrapper, arguments, traverser):
|
||||
if not arguments:
|
||||
return
|
||||
uri = traverser._traverse_node(arguments[0])
|
||||
open_in_chrome_context(uri, "nsIWindowWatcher.openWindow", traverser)
|
||||
|
||||
return {"return": on_open}
|
||||
|
||||
# Thunderbird 14 IDL changes
|
||||
@register_entity("nsIMsgPluggableStore.copyMessages")
|
||||
def nsIMsgPluggableStore_copyMessages(traverser):
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_entity_values",
|
||||
"nsIMsgPluggableStore_copyMessages"),
|
||||
notice="Altered nsIMsgPluggableStore.copyMessages method in use.",
|
||||
description="This add-on uses nsIMsgPluggableStore.copyMessages "
|
||||
"which was changed "
|
||||
"in Thunderbird 14. For more information, please refer to "
|
||||
"%s." % BUGZILLA_BUG % 738651,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=TB14_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
# No entities for now.
|
||||
|
|
|
@ -13,10 +13,9 @@ node
|
|||
import types
|
||||
|
||||
import actions
|
||||
from validator.compat import FX10_DEFINITION, FX14_DEFINITION
|
||||
from validator.constants import BUGZILLA_BUG
|
||||
from jstypes import *
|
||||
from instanceproperties import _set_HTML_property
|
||||
from appvalidator.constants import BUGZILLA_BUG
|
||||
from .jstypes import *
|
||||
from .instanceproperties import _set_HTML_property
|
||||
|
||||
|
||||
def createElement(args, traverser, node, wrapper):
|
||||
|
@ -49,36 +48,6 @@ def createElementNS(args, traverser, node, wrapper):
|
|||
_create_variable_element(traverser)
|
||||
|
||||
|
||||
def QueryInterface(args, traverser, node, wrapper):
|
||||
"""Handles QueryInterface calls"""
|
||||
|
||||
if not args:
|
||||
return
|
||||
|
||||
from call_definitions import xpcom_constructor
|
||||
return xpcom_constructor("QueryInterface", True, True)(
|
||||
wrapper=node,
|
||||
arguments=args,
|
||||
traverser=traverser)
|
||||
|
||||
def getInterface(args, traverser, node, wrapper):
|
||||
"""Handles getInterface calls"""
|
||||
|
||||
# This really only needs to be handled for nsIInterfaceRequestor
|
||||
# intarfaces, but as it's fair for code to assume that that
|
||||
# interface has already been queried and methods with this name
|
||||
# are unlikely to behave differently, we just process it for all
|
||||
# objects.
|
||||
|
||||
if not args:
|
||||
return
|
||||
|
||||
from call_definitions import xpcom_constructor
|
||||
return xpcom_constructor("getInterface")(
|
||||
wrapper=node,
|
||||
arguments=args,
|
||||
traverser=traverser)
|
||||
|
||||
def _create_script_tag(traverser):
|
||||
"""Raises a warning that the dev is creating a script tag"""
|
||||
traverser.err.warning(
|
||||
|
@ -113,6 +82,19 @@ def _create_variable_element(traverser):
|
|||
context=traverser.context)
|
||||
|
||||
|
||||
def insertAdjacentHTML(args, traverser, node, wrapper):
|
||||
"""
|
||||
Perfrom the same tests on content inserted into the DOM via
|
||||
insertAdjacentHTML as we otherwise would for content inserted via the
|
||||
various innerHTML/outerHTML properties.
|
||||
"""
|
||||
if not args or len(args) < 2:
|
||||
return
|
||||
|
||||
content = traverser._traverse_node(args[1])
|
||||
_set_HTML_property("insertAdjacentHTML", content, traverser)
|
||||
|
||||
|
||||
def setAttribute(args, traverser, node, wrapper):
|
||||
"""This ensures that setAttribute calls don't set on* attributes"""
|
||||
|
||||
|
@ -136,103 +118,7 @@ def setAttribute(args, traverser, node, wrapper):
|
|||
context=traverser.context)
|
||||
|
||||
|
||||
def nsIDOMFile_deprec(args, traverser, node, wrapper):
|
||||
"""A wrapper for call_definitions.nsIDOMFile_deprec."""
|
||||
from call_definitions import nsIDOMFile_deprec as cd_nsIDOMFile_deprec
|
||||
cd_nsIDOMFile_deprec(None, [], traverser)
|
||||
|
||||
|
||||
def insertAdjacentHTML(args, traverser, node, wrapper):
|
||||
"""
|
||||
Perfrom the same tests on content inserted into the DOM via
|
||||
insertAdjacentHTML as we otherwise would for content inserted via the
|
||||
various innerHTML/outerHTML properties.
|
||||
"""
|
||||
if not args or len(args) < 2:
|
||||
return
|
||||
|
||||
content = traverser._traverse_node(args[1])
|
||||
_set_HTML_property("insertAdjacentHTML", content, traverser)
|
||||
|
||||
|
||||
def isSameNode(args, traverser, node, wrapper):
|
||||
"""Raise an error when an add-on uses node.isSameNode(foo)."""
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_instanceactions", "isSameNode"),
|
||||
error="isSameNode function has been removed in Gecko 10.",
|
||||
description='The "isSameNode" function has been removed. You can use '
|
||||
'the === operator as an alternative. See %s for more '
|
||||
'information.' % BUGZILLA_BUG % 687400,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def openDialog(args, traverser, node, wrapper):
|
||||
"""Raise an error if the first argument is a remote URL."""
|
||||
if not args:
|
||||
return
|
||||
uri = traverser._traverse_node(args[0])
|
||||
from call_definitions import open_in_chrome_context
|
||||
open_in_chrome_context(uri, "openDialog", traverser)
|
||||
|
||||
|
||||
def replaceWholeText(args, traverser, node, wrapper):
|
||||
"""Raise an error when an add-on uses node.replaceWholeText(foo)."""
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_instanceactions", "replaceWholeText"),
|
||||
error="replaceWholeText function has been removed in Gecko 10.",
|
||||
description='The "replaceWholeText" function has been removed. See '
|
||||
'%s for more information.' % BUGZILLA_BUG % 683482,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def PageMod(args, traverser, node, wrapper):
|
||||
"""
|
||||
This is the function that is called in Jetpack to modify the contents of a
|
||||
page with a "content script". This function needs to analyze he first
|
||||
parameter. If it is an object and if that object contains a "contentScript"
|
||||
string, that string needs to be passed to the validator.testcases.scripting
|
||||
library for testing as its own JS script file.
|
||||
"""
|
||||
|
||||
if not args:
|
||||
return
|
||||
|
||||
pm_properties = traverser._traverse_node(args[0])
|
||||
if not pm_properties.has_property("contentScript"):
|
||||
return
|
||||
|
||||
content_script = pm_properties.get(traverser, "contentScript")
|
||||
content_script = content_script.get_literal_value()
|
||||
if not isinstance(content_script, (str, unicode)):
|
||||
return
|
||||
|
||||
import validator.testcases.scripting as sub_scripting
|
||||
sub_scripting.test_js_file(
|
||||
traverser.err, traverser.filename, content_script,
|
||||
line=traverser.line, context=traverser.context)
|
||||
|
||||
|
||||
INSTANCE_DEFINITIONS = {"createElement": createElement,
|
||||
"createElementNS": createElementNS,
|
||||
"getAsBinary": nsIDOMFile_deprec,
|
||||
"getAsDataURL": nsIDOMFile_deprec,
|
||||
"getInterface": getInterface,
|
||||
"insertAdjacentHTML": insertAdjacentHTML,
|
||||
"isSameNode": isSameNode,
|
||||
"openDialog": openDialog,
|
||||
"PageMod": PageMod,
|
||||
"QueryInterface": QueryInterface,
|
||||
"replaceWholeText": replaceWholeText,
|
||||
"setAttribute": setAttribute}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import re
|
||||
import types
|
||||
|
||||
from validator.compat import FX10_DEFINITION, FX13_DEFINITION
|
||||
from validator.constants import BUGZILLA_BUG
|
||||
from appvalidator.constants import BUGZILLA_BUG
|
||||
import jstypes
|
||||
|
||||
|
||||
|
@ -46,8 +45,7 @@ def _set_HTML_property(function, new_value, traverser):
|
|||
# Everything checks out, but we still want to pass it through
|
||||
# the markup validator. Turn off strict mode so we don't get
|
||||
# warnings about malformed HTML.
|
||||
from validator.testcases.markup.markuptester import \
|
||||
MarkupParser
|
||||
from ..markup.markuptester import MarkupParser
|
||||
parser = MarkupParser(traverser.err, strict=False, debug=True)
|
||||
parser.process(traverser.filename, literal_value, "xul")
|
||||
|
||||
|
@ -85,74 +83,8 @@ def set_on_event(new_value, traverser):
|
|||
context=traverser.context)
|
||||
|
||||
|
||||
def get_isElementContentWhitespace(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_instanceproperties", "get_iECW"),
|
||||
error="isElementContentWhitespace property removed in Gecko 10.",
|
||||
description='The "isElementContentWhitespace" property has been '
|
||||
'removed. See %s for more information.' %
|
||||
BUGZILLA_BUG % 687422,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def startendMarker(*args):
|
||||
traverser = args[0] if len(args) == 1 else args[1]
|
||||
traverser.err.notice(
|
||||
err_id=("testcases_javascript_instanceproperties",
|
||||
"get_startendMarker"),
|
||||
notice="`_startMarker` and `_endMarker` changed in Gecko 13",
|
||||
description="The `_startMarker` and `_endMarker` variables have "
|
||||
"changed in a backward-incompatible way in Gecko 13. They "
|
||||
"are now element references instead of numeric indices. "
|
||||
"See %s for more information." % BUGZILLA_BUG % 731563,
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX13_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
|
||||
|
||||
def _get_xml(name):
|
||||
"""Handle all of the xml* compatibility problems introduced in Gecko 10."""
|
||||
bugs = {"xmlEncoding": 687426,
|
||||
"xmlStandalone": 693154,
|
||||
"xmlVersion": 693162}
|
||||
def wrapper(traverser):
|
||||
traverser.err.error(
|
||||
err_id=("testcases_javascript_instanceproperties", "_get_xml",
|
||||
name),
|
||||
error="%s has been removed in Gecko 10" % name,
|
||||
description='The "%s" property has been removed. See %s for more '
|
||||
'information.' % (name, BUGZILLA_BUG % bugs[name]),
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context,
|
||||
for_appversions=FX10_DEFINITION,
|
||||
compatibility_type="error",
|
||||
tier=5)
|
||||
return {"get": wrapper}
|
||||
|
||||
|
||||
OBJECT_DEFINITIONS = {"_endMarker": {"get": startendMarker,
|
||||
"set": startendMarker},
|
||||
"_startMarker": {"get": startendMarker,
|
||||
"set": startendMarker},
|
||||
"innerHTML": {"set": set_innerHTML},
|
||||
"outerHTML": {"set": set_outerHTML},
|
||||
"isElementContentWhitespace":
|
||||
{"get": get_isElementContentWhitespace},
|
||||
"xmlEncoding": _get_xml("xmlEncoding"),
|
||||
"xmlStandalone": _get_xml("xmlStandalone"),
|
||||
"xmlVersion": _get_xml("xmlVersion"),}
|
||||
OBJECT_DEFINITIONS = {"innerHTML": {"set": set_innerHTML},
|
||||
"outerHTML": {"set": set_outerHTML},}
|
||||
|
||||
|
||||
def get_operation(mode, property):
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import types
|
||||
import instanceproperties
|
||||
|
||||
from validator.constants import JETPACK_URI_URL
|
||||
|
||||
recursion_buster = []
|
||||
|
||||
|
||||
class JSObject(object):
|
||||
"""
|
||||
Mimics a JS object (function) and is capable of serving as an active
|
||||
|
@ -178,24 +177,6 @@ class JSWrapper(object):
|
|||
if value == self.value:
|
||||
return
|
||||
|
||||
# We want to obey the permissions of global objects
|
||||
if (self.is_global and
|
||||
(not traverser or not traverser.is_jsm) and
|
||||
(isinstance(self.value, dict) and
|
||||
("overwritable" not in self.value or
|
||||
self.value["overwritable"] == False))):
|
||||
traverser.err.warning(("testcases_javascript_jstypes",
|
||||
"JSWrapper_set_value",
|
||||
"global_overwrite"),
|
||||
"Global overwrite",
|
||||
"An attempt to overwrite a global variable "
|
||||
"was made in some JS code.",
|
||||
traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
return self
|
||||
|
||||
if isinstance(value, (bool, str, int, float, long, unicode)):
|
||||
self.inspect_literal(value)
|
||||
value = JSLiteral(value)
|
||||
|
@ -365,26 +346,8 @@ class JSWrapper(object):
|
|||
|
||||
self.traverser._debug("INSPECTING: %s" % value)
|
||||
|
||||
if isinstance(value, (str, unicode)):
|
||||
# TODO(basta): Testing for jetpack should be easier.
|
||||
if ("is_jetpack" in self.traverser.err.metadata and
|
||||
value.startswith("resource://") and
|
||||
"-data/" in value):
|
||||
# Since Jetpack files are ignored, this should not be scanning
|
||||
# anything inside the jetpack directories.
|
||||
self.traverser.err.warning(
|
||||
err_id=("javascript_js_jstypes", "jswrapper",
|
||||
"jetpack_abs_uri"),
|
||||
warning="Absolute URIs in Jetpack 1.4 are disallowed",
|
||||
description=["As of Jetpack 1.4, absolute URIs are no "
|
||||
"longer allowed within add-ons.",
|
||||
"See %s for more information." %
|
||||
JETPACK_URI_URL],
|
||||
filename=self.traverser.filename,
|
||||
line=self.traverser.line,
|
||||
column=self.traverser.position,
|
||||
context=self.traverser.context,
|
||||
compatibility_type="error")
|
||||
# This is a no-op for now.
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
"""Returns a textual version of the object."""
|
||||
|
|
|
@ -3,7 +3,7 @@ import math
|
|||
import actions
|
||||
from actions import _get_as_str
|
||||
import call_definitions
|
||||
from call_definitions import xpcom_constructor as xpcom_const, python_wrap
|
||||
from call_definitions import python_wrap
|
||||
from entity_values import entity
|
||||
from jstypes import JSWrapper
|
||||
|
||||
|
@ -48,277 +48,10 @@ BANNED_PREF_REGEXPS = [
|
|||
CONTENT_DOCUMENT = None
|
||||
|
||||
|
||||
INTERFACES = {
|
||||
u"nsICategoryManager":
|
||||
{"value":
|
||||
{u"addCategoryEntry":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
("Bootstrapped add-ons may not create persistent "
|
||||
"category entries"
|
||||
if a and len(a) > 3 and t(a[3]).get_literal_value()
|
||||
else
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to cleanup any added category entries "
|
||||
"at shutdown")}}},
|
||||
u"nsIAccessibleRetrieval":
|
||||
{"dangerous":
|
||||
"Using the nsIAccessibleRetrieval interface causes significant "
|
||||
"performance degradation in Gecko. It should only be used in "
|
||||
"accessibility-related add-ons."},
|
||||
u"nsIBrowserSearchService":
|
||||
{"value":
|
||||
{u"currentEngine": {"readonly": True},
|
||||
u"defaultEngine": {"readonly": True}}},
|
||||
|
||||
u"nsIComm4xProfile":
|
||||
{"return": call_definitions.nsIComm4xProfile_removed},
|
||||
u"nsIComponentRegistrar":
|
||||
{"value":
|
||||
{u"autoRegister":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Bootstrapped add-ons may not register "
|
||||
"chrome manifest files"},
|
||||
u"registerFactory":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to cleanup any component registrations "
|
||||
"at shutdown"}}},
|
||||
u"nsIDOMNSHTMLElement": entity("nsIDOMNSHTMLElement"),
|
||||
u"nsIDOMNSHTMLFrameElement": entity("nsIDOMNSHTMLFrameElement"),
|
||||
u"nsIImapIncomingServer":
|
||||
{"value":
|
||||
{u"GetImapConnectionAndLoadUrl":
|
||||
{"return": call_definitions.TB12_nsIImapProtocol_changed}}},
|
||||
u"nsIImapMailFolderSink":
|
||||
{"value":
|
||||
{u"setUrlState":
|
||||
{"return": call_definitions.nsIImapMailFolderSink_changed}}},
|
||||
u"nsIImapProtocol":
|
||||
{"value":
|
||||
{u"NotifyHdrsToDownload":
|
||||
{"return": call_definitions.nsIImapProtocol_removed},
|
||||
u"Initialize":
|
||||
{"return": call_definitions.TB12_nsIImapProtocol_changed}}},
|
||||
u"nsIJSON":
|
||||
{"value":
|
||||
{u"encode":
|
||||
{"return": call_definitions.nsIJSON_deprec},
|
||||
u"decode":
|
||||
{"return": call_definitions.nsIJSON_deprec}}},
|
||||
u"nsIMailtoUrl":
|
||||
{"value":
|
||||
{u"GetMessageContents":
|
||||
{"return": call_definitions.nsIMailtoUrl_changed}}},
|
||||
u"nsIMsgDBService":
|
||||
{"value":
|
||||
{u"openMailDBFromFile":
|
||||
{"return": call_definitions.nsIMsgDatabase_changed}}},
|
||||
u"nsIMsgDatabase":
|
||||
{"value":
|
||||
{u"Open":
|
||||
{"return": call_definitions.nsIMsgDatabase_changed}}},
|
||||
u"nsIMsgFolder":
|
||||
{"value":
|
||||
{u"offlineStoreOutputStream":
|
||||
{"value": call_definitions.nsIMsgFolder_changed}}},
|
||||
u"nsIMsgLocalMailFolder":
|
||||
{"value":
|
||||
{u"addMessage":
|
||||
{"return": call_definitions.TB13_nsIMsgLocalMailFolder_changed},
|
||||
u"addMessageBatch":
|
||||
{"return": call_definitions.TB13_nsIMsgLocalMailFolder_changed}}},
|
||||
u"nsIMsgNewsFolder":
|
||||
{"value":
|
||||
{u"getGroupPasswordWithUI":
|
||||
{"return": call_definitions.TB13_nsIMsgNewsFolder_changed},
|
||||
u"getGroupUsernameWithUI":
|
||||
{"return": call_definitions.TB13_nsIMsgNewsFolder_changed},
|
||||
u"forgetGroupUsername":
|
||||
{"return": call_definitions.TB13_nsIMsgNewsFolder_changed},
|
||||
u"forgetGroupPassword":
|
||||
{"return": call_definitions.TB13_nsIMsgNewsFolder_changed}}},
|
||||
u"nsIMsgPluggableStore":
|
||||
{"value":
|
||||
{u"copyMessages": entity("nsIMsgPluggableStore.copyMessages")}},
|
||||
u"nsIMsgOutputStream":
|
||||
{"value":
|
||||
{u"folderStream":
|
||||
{"value": call_definitions.nsIMsgDatabase_changed}}},
|
||||
u"nsIMsgQuote":
|
||||
{"value":
|
||||
{u"quoteMessage":
|
||||
{"return": call_definitions.nsIMsgQuote_changed}}},
|
||||
u"nsIMsgSearchScopeTerm":
|
||||
{"value":
|
||||
{u"mailFile":
|
||||
{"return": call_definitions.nsIMsgSearchScopeTerm_removed},
|
||||
u"inputStream":
|
||||
{"return": call_definitions.nsIMsgSearchScopeTerm_removed}}},
|
||||
u"nsIMsgThread":
|
||||
{"value":
|
||||
{u"GetChildAt":
|
||||
{"return": call_definitions.nsIMsgThread_removed}}},
|
||||
u"nsIObserverService":
|
||||
{"value":
|
||||
{u"addObserver":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to remove any added observers "
|
||||
"at shutdown"}}},
|
||||
u"nsIResProtocolHandler":
|
||||
{"value":
|
||||
{u"setSubstitution":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
a and \
|
||||
len(a) > 1 and \
|
||||
t(a[1]).get_literal_value() and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to cleanup any added resource substitutions "
|
||||
"at shutdown"}}},
|
||||
u"nsIStringBundleService":
|
||||
{"value":
|
||||
{u"createStringBundle":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to flush the string bundle cache at shutdown"},
|
||||
u"createExtensibleBundle":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to flush the string bundle cache at shutdown"}}},
|
||||
u"nsIStyleSheetService":
|
||||
{"value":
|
||||
{u"loadAndRegisterSheet":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to unregister any registered stylesheets "
|
||||
"at shutdown"}}},
|
||||
u"nsIWindowMediator":
|
||||
{"value":
|
||||
{"registerNotification":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to remove any added observers "
|
||||
"at shutdown"}}},
|
||||
u"nsIWindowWatcher":
|
||||
{"value":
|
||||
{u"addListener":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
e.get_resource("em:bootstrap") and \
|
||||
"Authors of bootstrapped add-ons must take care "
|
||||
"to remove any added observers "
|
||||
"at shutdown"},
|
||||
u"openWindow": entity("nsIWindowWatcher.openWindow")}},
|
||||
u"nsIURLParser":
|
||||
{"value":
|
||||
{u"parsePath":
|
||||
{"return": call_definitions.urlparser_parsepath_bug691588}}},
|
||||
u"nsIURL":
|
||||
{"value":
|
||||
{u"param":
|
||||
{"value": call_definitions.url_param_bug691588}}},
|
||||
u"nsIBrowserHistory":
|
||||
{"value":
|
||||
{u"lastPageVisited": entity("nsIBrowserHistory.lastPageVisited"),
|
||||
u"removePages":
|
||||
{"return": call_definitions.browserhistory_removepages},
|
||||
u"registerOpenPage":
|
||||
{"value": call_definitions.browserhistory_registeropenpage},
|
||||
u"unregisterOpenPage":
|
||||
{"value":
|
||||
call_definitions.browserhistory_unregisteropenpage},
|
||||
}
|
||||
},
|
||||
u"nsIEditorSpellCheck":
|
||||
{"value":
|
||||
{u"UpdateCurrentDictionary":
|
||||
{"return":
|
||||
call_definitions.spellcheck_updatecurrentdictionary},
|
||||
u"saveDefaultDictionary":
|
||||
{"value":
|
||||
call_definitions.spellcheck_savedefaultdictionary}}},
|
||||
u"nsIPlacesImportExportService":
|
||||
{"value":
|
||||
{u"importHTMLFromFile": entity("importHTMLFromFile"),
|
||||
u"importHTMLFromURI": entity("importHTMLFromURI"),}},
|
||||
u"nsIDOMHTMLDocument":
|
||||
{"value":
|
||||
{u"queryCommandText": entity("nsIDOMHTMLDocument"),
|
||||
u"execCommandShowHelp": entity("nsIDOMHTMLDocument")}},
|
||||
}
|
||||
|
||||
INTERFACE_ENTITIES = {u"nsIXMLHttpRequest":
|
||||
{"xpcom_map":
|
||||
lambda: GLOBAL_ENTITIES["XMLHttpRequest"]},
|
||||
u"nsIProcess": {"dangerous": True},
|
||||
u"nsIDOMGeoGeolocation": {"dangerous": True},
|
||||
u"nsIX509CertDB": {"dangerous": True}}
|
||||
for interface in INTERFACES:
|
||||
def construct(interface):
|
||||
def wrap():
|
||||
return INTERFACES[interface]
|
||||
return wrap
|
||||
INTERFACE_ENTITIES[interface] = {"xpcom_map": construct(interface)}
|
||||
|
||||
#import pdb; pdb.set_trace()
|
||||
|
||||
|
||||
def build_quick_xpcom(method, interface, traverser):
|
||||
"""A shortcut to quickly build XPCOM objects on the fly."""
|
||||
constructor = xpcom_const(method, pretraversed=True)
|
||||
interface_obj = traverser._build_global(
|
||||
name=method,
|
||||
entity={"xpcom_map": lambda: INTERFACES[interface]})
|
||||
object = constructor(None, [interface_obj], traverser)
|
||||
if isinstance(object, JSWrapper):
|
||||
object = object.value
|
||||
return object
|
||||
|
||||
|
||||
# GLOBAL_ENTITIES is also representative of the `window` object.
|
||||
GLOBAL_ENTITIES = {
|
||||
u"window": {"value": lambda t: {"value": GLOBAL_ENTITIES}},
|
||||
u"null": {"literal": lambda t: JSWrapper(None, traverser=t)},
|
||||
u"Cc": {"readonly": False,
|
||||
"value":
|
||||
lambda t: GLOBAL_ENTITIES["Components"]["value"]["classes"]},
|
||||
u"Ci": {"readonly": False,
|
||||
"value":
|
||||
lambda t: GLOBAL_ENTITIES["Components"]["value"]["interfaces"]},
|
||||
|
||||
u"Cu": {"readonly": False,
|
||||
"value":
|
||||
lambda t: GLOBAL_ENTITIES["Components"]["value"]["utils"]},
|
||||
u"Services":
|
||||
{"value": {u"scriptloader": {"dangerous": True},
|
||||
u"wm":
|
||||
{"value":
|
||||
lambda t: build_quick_xpcom("getService",
|
||||
"nsIWindowMediator",
|
||||
t)},
|
||||
u"ww":
|
||||
{"value":
|
||||
lambda t: build_quick_xpcom("getService",
|
||||
"nsIWindowWatcher",
|
||||
t)}}},
|
||||
|
||||
u"document":
|
||||
{"value":
|
||||
|
@ -339,8 +72,6 @@ GLOBAL_ENTITIES = {
|
|||
not a or
|
||||
unicode(t(a[0]).get_literal_value()).lower() ==
|
||||
"script"},
|
||||
u"getSelection":
|
||||
{"return": call_definitions.document_getSelection},
|
||||
u"loadOverlay":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
|
@ -355,27 +86,6 @@ GLOBAL_ENTITIES = {
|
|||
u"setTimeout": {"dangerous": actions._call_settimeout},
|
||||
u"setInterval": {"dangerous": actions._call_settimeout},
|
||||
|
||||
u"requestAnimationFrame": {"return":
|
||||
call_definitions.requestAnimationFrame},
|
||||
|
||||
# mail Attachment API Functions
|
||||
u"createNewAttachmentInfo": {"return": call_definitions.mail_attachment_api},
|
||||
u"saveAttachment": {"return": call_definitions.mail_attachment_api},
|
||||
u"attachmentIsEmpty": {"return": call_definitions.mail_attachment_api},
|
||||
u"openAttachment": {"return": call_definitions.mail_attachment_api},
|
||||
u"detachAttachment": {"return": call_definitions.mail_attachment_api},
|
||||
u"cloneAttachment": {"return": call_definitions.mail_attachment_api},
|
||||
u"FocusOnFirstAttachment": {"return": call_definitions.TB9FocusFunctions_removed},
|
||||
|
||||
u"gComposeBundle": {"return": call_definitions.gComposeBundle_removed},
|
||||
u"WhichPaneHasFocus": {"return": call_definitions.TB9FocusFunctions_removed},
|
||||
|
||||
# Thunderbird 10 global functions changed/removed
|
||||
u"MsgDeleteMessageFromMessageWindow": {"return": call_definitions.TB10Function_removed},
|
||||
u"goToggleSplitter": {"return": call_definitions.TB10Function_removed},
|
||||
u"AddMessageComposeOfflineObserver": {"return": call_definitions.TB10Function_renamed},
|
||||
u"RemoveMessageComposeOfflineObserver": {"return": call_definitions.TB10Function_renamed},
|
||||
|
||||
u"encodeURI": {"readonly": True},
|
||||
u"decodeURI": {"readonly": True},
|
||||
u"encodeURIComponent": {"readonly": True},
|
||||
|
@ -479,46 +189,6 @@ GLOBAL_ENTITIES = {
|
|||
{"return": python_wrap(math.tan, [("num", 0)])},
|
||||
}},
|
||||
|
||||
u"netscape":
|
||||
{"value":
|
||||
{u"security":
|
||||
{"value":
|
||||
{u"PrivilegeManager":
|
||||
{"value":
|
||||
{u"enablePrivilege":
|
||||
{"dangerous": True}}}}}}},
|
||||
|
||||
u"navigator":
|
||||
{"value": {u"wifi": {"dangerous": True},
|
||||
u"geolocation": {"dangerous": True}}},
|
||||
|
||||
u"Components":
|
||||
{"readonly": True,
|
||||
"value":
|
||||
{u"classes":
|
||||
{"xpcom_wildcard": True,
|
||||
"value":
|
||||
{u"createInstance":
|
||||
{"return": xpcom_const("createInstance")},
|
||||
u"getService":
|
||||
{"return": xpcom_const("getService")}}},
|
||||
"utils":
|
||||
{"value": {u"evalInSandbox":
|
||||
{"dangerous": True},
|
||||
u"import":
|
||||
{"dangerous":
|
||||
lambda a, t, e:
|
||||
a and
|
||||
_get_as_str(t(a[0]).get_literal_value())
|
||||
.count("ctypes.jsm")}}},
|
||||
u"interfaces": {"value": INTERFACE_ENTITIES}}},
|
||||
u"extensions": {"dangerous": True},
|
||||
u"xpcnativewrappers": {"dangerous": True},
|
||||
|
||||
u"AddonManagerPrivate":
|
||||
{"value":
|
||||
{u"registerProvider": {"return": call_definitions.amp_rp_bug660359}}},
|
||||
|
||||
u"XMLHttpRequest":
|
||||
{"value":
|
||||
{u"open":
|
||||
|
@ -544,7 +214,6 @@ GLOBAL_ENTITIES = {
|
|||
u"innerWidth": {"readonly": False},
|
||||
u"width": {"readonly": False},
|
||||
u"height": {"readonly": False},
|
||||
u"top": {"readonly": actions._readonly_top},
|
||||
|
||||
u"content":
|
||||
{"context": "content",
|
||||
|
@ -570,12 +239,6 @@ GLOBAL_ENTITIES = {
|
|||
{"value":
|
||||
lambda t: {"value": GLOBAL_ENTITIES}},
|
||||
|
||||
u"XPCNativeWrapper":
|
||||
{"value":
|
||||
{u"unwrap":
|
||||
{"return": call_definitions.js_unwrap}},
|
||||
"return": call_definitions.js_wrap},
|
||||
|
||||
# Preference creation in pref defaults files
|
||||
u"pref": {"dangerous": actions._call_create_pref},
|
||||
u"user_pref": {"dangerous": actions._call_create_pref},
|
||||
|
|
|
@ -6,9 +6,9 @@ import subprocess
|
|||
import tempfile
|
||||
from cStringIO import StringIO
|
||||
|
||||
from validator.constants import SPIDERMONKEY_INSTALLATION
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
import validator.unicodehelper as unicodehelper
|
||||
from appvalidator.constants import SPIDERMONKEY_INSTALLATION
|
||||
from appvalidator.contextgenerator import ContextGenerator
|
||||
import appvalidator.unicodehelper as unicodehelper
|
||||
|
||||
JS_ESCAPE = re.compile("\\\\+[ux]", re.I)
|
||||
|
||||
|
@ -23,8 +23,7 @@ def get_tree(code, err=None, filename=None, shell=None):
|
|||
code = prepare_code(code, err, filename)
|
||||
|
||||
try:
|
||||
tree = _get_tree(code,
|
||||
shell if shell else SPIDERMONKEY_INSTALLATION)
|
||||
tree = _get_tree(code, shell or SPIDERMONKEY_INSTALLATION)
|
||||
return tree
|
||||
except JSReflectException as exc:
|
||||
str_exc = str(exc).strip("'\"")
|
||||
|
@ -61,7 +60,7 @@ def get_tree(code, err=None, filename=None, shell=None):
|
|||
|
||||
|
||||
class JSReflectException(Exception):
|
||||
"An exception to indicate that tokenization has failed"
|
||||
"""An exception to indicate that tokenization has failed."""
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
@ -77,7 +76,8 @@ class JSReflectException(Exception):
|
|||
|
||||
|
||||
def prepare_code(code, err, filename):
|
||||
"Prepares code for tree generation"
|
||||
"""Prepares code for tree generation."""
|
||||
|
||||
# Acceptable unicode characters still need to be stripped. Just remove the
|
||||
# slash: a character is necessary to prevent bad identifier errors
|
||||
code = JS_ESCAPE.sub("u", code)
|
||||
|
@ -87,7 +87,7 @@ def prepare_code(code, err, filename):
|
|||
|
||||
|
||||
def _get_tree(code, shell=SPIDERMONKEY_INSTALLATION):
|
||||
"Returns an AST tree of the JS passed in `code`."
|
||||
"""Returns an AST tree of the JS passed in `code`."""
|
||||
|
||||
if not code:
|
||||
return None
|
||||
|
|
|
@ -2,31 +2,25 @@ import copy
|
|||
import re
|
||||
import types
|
||||
|
||||
import validator.testcases.javascript.actions as actions
|
||||
from validator.testcases.javascript.jstypes import *
|
||||
from validator.testcases.javascript.nodedefinitions import DEFINITIONS
|
||||
from validator.testcases.javascript.predefinedentities import \
|
||||
GLOBAL_ENTITIES, BANNED_IDENTIFIERS
|
||||
from . import actions
|
||||
from .jstypes import *
|
||||
from .nodedefinitions import DEFINITIONS
|
||||
from .predefinedentities import GLOBAL_ENTITIES, BANNED_IDENTIFIERS
|
||||
|
||||
|
||||
DEBUG = False
|
||||
IGNORE_POLLUTION = False
|
||||
POLLUTION_COMPONENTS_PATH = re.compile(r"/?components/.*\.jsm?")
|
||||
POLLUTION_EXCEPTIONS = set(["Cc", "Ci", "Cu", ])
|
||||
|
||||
|
||||
class Traverser:
|
||||
"Traverses the AST Tree and determines problems with a chunk of JS."
|
||||
|
||||
def __init__(self, err, filename, start_line=0, context=None,
|
||||
is_jsm=False):
|
||||
def __init__(self, err, filename, start_line=0, context=None):
|
||||
self.err = err
|
||||
self.is_jsm = is_jsm
|
||||
|
||||
self.contexts = []
|
||||
self.block_contexts = []
|
||||
self.filename = filename
|
||||
self.start_line = start_line
|
||||
self.polluted = False
|
||||
self.line = 1 # Line number
|
||||
self.position = 0 # Column number
|
||||
self.context = context
|
||||
|
@ -73,39 +67,6 @@ class Traverser:
|
|||
if DEBUG:
|
||||
self.err.final_context = self.contexts[0]
|
||||
|
||||
if self.pollutable:
|
||||
# Ignore anything in the components/ directory
|
||||
if POLLUTION_COMPONENTS_PATH.match(self.filename):
|
||||
return
|
||||
|
||||
# This performs the namespace pollution test.
|
||||
final_globals = copy.deepcopy(self.contexts[0].data)
|
||||
for global_name in self.contexts[0].data:
|
||||
if global_name in POLLUTION_EXCEPTIONS:
|
||||
del final_globals[global_name]
|
||||
|
||||
global_context_size = len(final_globals)
|
||||
self._debug("Final context size: %d" % global_context_size)
|
||||
|
||||
if (global_context_size > 3 and not self.is_jsm and
|
||||
not "is_jetpack" in self.err.metadata and
|
||||
not self.err.get_resource("em:bootstrap") == "true"):
|
||||
self.err.warning(
|
||||
err_id=("testcases_javascript_traverser",
|
||||
"run",
|
||||
"namepsace_pollution"),
|
||||
warning="JavaScript namespace pollution",
|
||||
description=["Your add-on contains a large number of "
|
||||
"global variables, which can conflict "
|
||||
"with other add-ons. For more "
|
||||
"information, see "
|
||||
"http://blog.mozilla.com/addons/2009/01/16/"
|
||||
"firefox-extensions-global-namespace-pollution/"
|
||||
", or use JavaScript modules.",
|
||||
"List of entities: %s" %
|
||||
", ".join(self.contexts[0].data.keys())],
|
||||
filename=self.filename)
|
||||
|
||||
def _traverse_node(self, node):
|
||||
"Finds a node's internal blocks and helps manage state."
|
||||
|
||||
|
|
|
@ -1,265 +0,0 @@
|
|||
from collections import defaultdict
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
|
||||
import validator.decorator as decorator
|
||||
from validator.constants import PACKAGE_EXTENSION
|
||||
from validator.version import Version
|
||||
from content import FLAGGED_FILES
|
||||
|
||||
|
||||
SAFE_FILES = (".jpg", ".ico", ".png", ".gif", ".txt")
|
||||
|
||||
EMPTY_FILE_SHA256SUMS = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
|
||||
@decorator.register_test(tier=1, expected_type=PACKAGE_EXTENSION)
|
||||
def inspect_jetpack(err, xpi_package, allow_old_sdk=False):
|
||||
"""
|
||||
If the add-on is a Jetpack extension, its contents should be tested to
|
||||
ensure that none of the Jetpack libraries have been tampered with.
|
||||
"""
|
||||
|
||||
jetpack_triggers = ("bootstrap.js", "harness-options.json")
|
||||
|
||||
# Make sure this is a Jetpack add-on.
|
||||
if not all(trigger in xpi_package for trigger in jetpack_triggers):
|
||||
return
|
||||
|
||||
try:
|
||||
harnessoptions = json.loads(xpi_package.read("harness-options.json"))
|
||||
except ValueError:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"bad_harness-options.json"),
|
||||
warning="harness-options.json is not decodable JSON",
|
||||
description="The harness-options.json file is not decodable as "
|
||||
"valid JSON data.",
|
||||
filename="harness-options.json")
|
||||
return
|
||||
|
||||
err.metadata["is_jetpack"] = True
|
||||
|
||||
# Test the harness-options file for the mandatory values.
|
||||
mandatory_elements = ("sdkVersion", "manifest", "jetpackID")
|
||||
missing_elements = []
|
||||
for element in mandatory_elements:
|
||||
if element not in harnessoptions:
|
||||
missing_elements.append(element)
|
||||
|
||||
if missing_elements:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"harness-options_missing_elements"),
|
||||
warning="Elements are missing from harness-options.json",
|
||||
description=["The harness-options.json file seems to be missing "
|
||||
"elements. It may have been tampered with or is "
|
||||
"corrupt.",
|
||||
"Missing elements: %s" % ", ".join(missing_elements)],
|
||||
filename="harness-options.json")
|
||||
return
|
||||
|
||||
# Save the SDK version information to the metadata.
|
||||
sdk_version = harnessoptions["sdkVersion"]
|
||||
err.metadata["jetpack_sdk_version"] = sdk_version
|
||||
|
||||
# Check that the version number isn't a redacted version.
|
||||
if sdk_version in ("1.4", "1.4.1", "1.4.2", ):
|
||||
err.error(
|
||||
err_id=("testcases_jetpack", "inspect_jetpack",
|
||||
"redacted_version"),
|
||||
error="Unsupported version of Add-on SDK",
|
||||
description="Versions 1.4, 1.4.1, and 1.4.2 of the add-on SDK may "
|
||||
"cause issues with data loss in some modules. You "
|
||||
"should upgrade the SDK to at least 1.4.3 in order to "
|
||||
"avoid these issues.")
|
||||
|
||||
# If we don't have a list of pretested files already, save a blank list.
|
||||
# Otherwise, use the existing list.
|
||||
pretested_files = err.get_resource("pretested_files")
|
||||
if not pretested_files:
|
||||
err.save_resource("pretested_files", [])
|
||||
pretested_files = []
|
||||
|
||||
# Read the jetpack data file in.
|
||||
jetpack_data = open(os.path.join(os.path.dirname(__file__),
|
||||
"jetpack_data.txt"))
|
||||
# Parse the jetpack data into something useful.
|
||||
jetpack_hash_table = defaultdict(dict)
|
||||
latest_jetpack = None
|
||||
for path, version_str, hash in map(str.split, jetpack_data):
|
||||
version = Version(version_str)
|
||||
if version.is_release and (not latest_jetpack or
|
||||
version > latest_jetpack):
|
||||
latest_jetpack = version
|
||||
jetpack_hash_table[hash][version_str] = path
|
||||
|
||||
if not allow_old_sdk and Version(sdk_version) != latest_jetpack:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack", "inspect_jetpack",
|
||||
"outdated_version"),
|
||||
warning="Outdated version of Add-on SDK",
|
||||
description="You are using version %s of the Add-on SDK, "
|
||||
"which is outdated. Please upgrade to version "
|
||||
"%s and repack your add-on"
|
||||
% (sdk_version, latest_jetpack))
|
||||
|
||||
# Prepare a place to store mentioned hashes.
|
||||
found_hashes = set()
|
||||
|
||||
loaded_modules = []
|
||||
tested_files = {}
|
||||
file_hashes = {}
|
||||
unknown_files = []
|
||||
mandatory_module_elements = ("moduleName", "packageName", "requirements",
|
||||
"sectionName", "docsSHA256", "jsSHA256")
|
||||
|
||||
# Iterate each loaded module and perform a sha256 hash on the files.
|
||||
for uri, module in harnessoptions["manifest"].items():
|
||||
|
||||
# Make sure the module is a resource:// URL
|
||||
if uri.startswith(("http://", "https://", "ftp://")):
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"irregular_module_location"),
|
||||
warning="Irregular Jetpack module location",
|
||||
description=["A Jetpack module is referenced with a remote "
|
||||
"URI.",
|
||||
"Referenced URI: %s" % uri],
|
||||
filename="harness-options.json")
|
||||
continue
|
||||
|
||||
# Make sure all of the mandatory elements are present.
|
||||
if not all(el in module for el in mandatory_module_elements):
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"irregular_module_elements"),
|
||||
warning="Irregular Jetpack module elements",
|
||||
description=["A Jetpack module in harness-options.json is "
|
||||
"missing some of its required JSON elements.",
|
||||
"Module: %s" % uri],
|
||||
filename="harness-options.json")
|
||||
continue
|
||||
|
||||
# Strip off the resource:// if it exists
|
||||
if uri.startswith("resource://"):
|
||||
uri = uri[11:]
|
||||
zip_path = "resources/%s" % uri.replace("@", "-at-")
|
||||
|
||||
# Check the zipname element if it exists.
|
||||
if zip_path not in xpi_package:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"missing_jetpack_module"),
|
||||
warning="Missing Jetpack module",
|
||||
description=["A Jetpack module listed in harness-options.json "
|
||||
"could not be found in the add-on.",
|
||||
"Path: %s" % zip_path],
|
||||
filename="harness-options.json")
|
||||
continue
|
||||
|
||||
blob_hash = hashlib.sha256(xpi_package.read(zip_path)).hexdigest()
|
||||
file_hashes[zip_path] = blob_hash
|
||||
|
||||
# Make sure that the module's hash matches what the manifest says.
|
||||
if blob_hash != module["jsSHA256"]:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"mismatched_checksum"),
|
||||
warning="Jetpack module hash mismatch",
|
||||
description=["A file in the Jetpack add-on does not match the "
|
||||
"corresponding hash listed in harness-options"
|
||||
".json.",
|
||||
"Module: %s" % zip_path,
|
||||
"Hashes: %s/%s" % (blob_hash, module["jsSHA256"])],
|
||||
filename=zip_path)
|
||||
|
||||
# We aren't going to keep track of anything that isn't an official
|
||||
# Jetpack file.
|
||||
if module["jsSHA256"] not in jetpack_hash_table:
|
||||
continue
|
||||
|
||||
# Keep track of all of the valid modules that were loaded.
|
||||
loaded_modules.append("".join([module["packageName"], "-",
|
||||
module["sectionName"], "/",
|
||||
module["moduleName"], ".js"]))
|
||||
|
||||
# Save information on what was matched.
|
||||
tested_files[zip_path] = jetpack_hash_table[module["jsSHA256"]]
|
||||
pretested_files.append(zip_path)
|
||||
|
||||
# Iterate the rest of the files in the package for testing.
|
||||
for filename in xpi_package:
|
||||
|
||||
# Skip files we've already identified.
|
||||
if (filename in tested_files or
|
||||
filename == "harness-options.json"):
|
||||
continue
|
||||
|
||||
# Skip safe files.
|
||||
if (filename.lower().endswith(SAFE_FILES) or
|
||||
filename in FLAGGED_FILES):
|
||||
continue
|
||||
|
||||
blob_hash = (file_hashes.get(filename, None) or
|
||||
hashlib.sha256(xpi_package.read(filename)).hexdigest())
|
||||
file_hashes[filename] = blob_hash
|
||||
|
||||
if blob_hash not in jetpack_hash_table:
|
||||
unknown_files.append(filename)
|
||||
continue
|
||||
else:
|
||||
tested_files[filename] = jetpack_hash_table[blob_hash]
|
||||
pretested_files.append(filename)
|
||||
|
||||
# Mark the hashes we actually find as being present.
|
||||
if blob_hash in found_hashes:
|
||||
found_hashes.discard(blob_hash)
|
||||
|
||||
for zip_path, versions in tested_files.items():
|
||||
if sdk_version in versions:
|
||||
tested_files[zip_path] = sdk_version, versions[sdk_version]
|
||||
else:
|
||||
# This isn't the version it claims to be. Go with the latest
|
||||
# Jetpack version we know about that contains it.
|
||||
version = str(max(map(Version, versions.keys())))
|
||||
tested_files[zip_path] = version, versions[version]
|
||||
|
||||
if (file_hashes[zip_path] not in EMPTY_FILE_SHA256SUMS and
|
||||
not zip_path.endswith("/")):
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack",
|
||||
"inspect_jetpack",
|
||||
"mismatched_version"),
|
||||
warning="Jetpack module version mismatch",
|
||||
description=["A file in the Jetpack add-on does not match "
|
||||
"the SDK version specified in harness-options"
|
||||
".json.",
|
||||
"Module: %s" % zip_path,
|
||||
"Versions: %s/%s" % (sdk_version, version)],
|
||||
filename=zip_path)
|
||||
|
||||
# We've got hashes left over
|
||||
if found_hashes:
|
||||
err.warning(
|
||||
err_id=("testcases_jetpack", "inspect_jetpack", "extra_hashes"),
|
||||
warning="Extra hashes registered in harness-options.",
|
||||
description=["This Jetpack add-on registers modules in the "
|
||||
"harness-options.json file that do not exist in the "
|
||||
"package.",
|
||||
"Hashes: %s" % ", ".join(found_hashes)],
|
||||
filename="harness-options.json")
|
||||
|
||||
# Store the collected information in the output metadata.
|
||||
err.metadata["jetpack_loaded_modules"] = loaded_modules
|
||||
err.metadata["jetpack_identified_files"] = tested_files
|
||||
err.metadata["jetpack_unknown_files"] = unknown_files
|
||||
|
||||
err.save_resource("pretested_files", pretested_files)
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,88 +0,0 @@
|
|||
import types
|
||||
from StringIO import StringIO
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
|
||||
try:
|
||||
from HTMLParser import HTMLParser
|
||||
except ImportError: # pragma: no cover
|
||||
from html.parser import HTMLParser
|
||||
|
||||
|
||||
class DTDParser(object):
|
||||
"Parses and serializes DTD files. This is useful for L10n tests."
|
||||
|
||||
def __init__(self, dtd):
|
||||
"""
|
||||
Creation of DTD parsers can be done based on a local file
|
||||
(provided as a string to the path), or directly (in memory as a
|
||||
StringIO object).
|
||||
"""
|
||||
|
||||
self.entities = {}
|
||||
self.items = []
|
||||
|
||||
data = ""
|
||||
if isinstance(dtd, types.StringTypes):
|
||||
with open(dtd) as dtd_instance:
|
||||
data = dtd_instance.read()
|
||||
elif isinstance(dtd, file):
|
||||
data = dtd.read()
|
||||
elif isinstance(dtd, StringIO):
|
||||
data = dtd.getvalue()
|
||||
|
||||
self._parse(data)
|
||||
|
||||
# Create a context for the file
|
||||
self.context = ContextGenerator(data)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.entities)
|
||||
|
||||
def _parse(self, data):
|
||||
"Parses the DTD data and stores it in an aggregate format."
|
||||
|
||||
split = data.split("\n")
|
||||
parser = DTDXMLParser()
|
||||
# Feed the DTD file in line-by-line.
|
||||
for line in split:
|
||||
line += "\n"
|
||||
try:
|
||||
parser.feed(line)
|
||||
except:
|
||||
parser = DTDXMLParser()
|
||||
else:
|
||||
if parser.out_buffer:
|
||||
for name, value, line in parser.out_buffer:
|
||||
self.entities[name] = value
|
||||
self.items.append((name, value, line))
|
||||
parser.clear_buffer()
|
||||
|
||||
|
||||
class DTDXMLParser(HTMLParser):
|
||||
"Parses the individual XML entities in a DTD document."
|
||||
|
||||
def __init__(self):
|
||||
HTMLParser.__init__(self)
|
||||
self.out_buffer = []
|
||||
|
||||
def unknown_decl(self, decl):
|
||||
"Handles non-DOCTYPE SGML declarations in *ML documents."
|
||||
|
||||
decl = decl.strip()
|
||||
split_decl = decl.split()
|
||||
|
||||
if not split_decl[0] == "ENTITY" or len(split_decl) < 3:
|
||||
# Interestingly enough, it legitimately IS an unknown
|
||||
# declaration. Funny thing, you know?
|
||||
# TODO: Log an error?
|
||||
return
|
||||
|
||||
self.out_buffer.append((split_decl[1],
|
||||
split_decl[2].strip('\'"'),
|
||||
self.getpos()[0])) # Pos 0 is the line no.
|
||||
|
||||
def clear_buffer(self):
|
||||
"Clears the return buffer."
|
||||
self.out_buffer = []
|
||||
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import types
|
||||
from StringIO import StringIO
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
|
||||
|
||||
class PropertiesParser(object):
|
||||
"""
|
||||
Parses and serializes .properties files. Even though you can pretty
|
||||
much do this in your sleep, it's still useful for L10n tests.
|
||||
"""
|
||||
|
||||
def __init__(self, dtd):
|
||||
"""
|
||||
Properties parsers can initialized based on a file path
|
||||
(provided as a string to the path), or directly (in memory as a
|
||||
StringIO object).
|
||||
"""
|
||||
|
||||
self.entities = {}
|
||||
self.items = []
|
||||
|
||||
if isinstance(dtd, types.StringTypes):
|
||||
data = open(dtd).read()
|
||||
elif isinstance(dtd, StringIO):
|
||||
data = dtd.getvalue()
|
||||
elif isinstance(dtd, file):
|
||||
data = dtd.read()
|
||||
|
||||
# Create a context!
|
||||
self.context = ContextGenerator(data)
|
||||
|
||||
split_data = data.split("\n")
|
||||
line_buffer = None
|
||||
line_number = 0
|
||||
for line in split_data:
|
||||
|
||||
# Increment the line number
|
||||
line_number += 1
|
||||
|
||||
# Clean things up
|
||||
clean_line = line.strip()
|
||||
if not clean_line:
|
||||
continue
|
||||
if clean_line.startswith("#"):
|
||||
continue
|
||||
|
||||
# It's a line that wraps
|
||||
if clean_line.count("=") == 0:
|
||||
if line_buffer:
|
||||
line_buffer[-1] += clean_line
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
|
||||
if line_buffer:
|
||||
# This line terminates a wrapped line
|
||||
self.entities[line_buffer[0].strip()] = \
|
||||
line_buffer[1].strip()
|
||||
self.items.append((line_buffer[0].strip(),
|
||||
line_buffer[1].strip(),
|
||||
line_number))
|
||||
|
||||
line_buffer = clean_line.split("=", 1)
|
||||
|
||||
# Handle any left-over wrapped line data
|
||||
if line_buffer:
|
||||
self.entities[line_buffer[0].strip()] = \
|
||||
line_buffer[1].strip()
|
||||
self.items.append((line_buffer[0].strip(),
|
||||
line_buffer[1].strip(),
|
||||
line_number))
|
||||
|
||||
def __len__(self):
|
||||
return len(self.entities)
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
import os
|
||||
import fastchardet
|
||||
import fnmatch
|
||||
from StringIO import StringIO
|
||||
|
||||
from validator import decorator
|
||||
from validator.xpi import XPIManager
|
||||
from validator.chromemanifest import ChromeManifest
|
||||
from validator.constants import *
|
||||
|
||||
import validator.testcases.l10n.dtd as dtd
|
||||
import validator.testcases.l10n.properties as properties
|
||||
|
||||
|
||||
# The threshold that determines the number of entities that must not be
|
||||
# missing from the package.
|
||||
L10N_THRESHOLD = 0.35
|
||||
L10N_SIMILAR_THRESHOLD = 0.9 # For en_US/en_GB kind of stuff
|
||||
|
||||
# Only warn about unchanged entities longer than this number of characters.
|
||||
L10N_LENGTH_THRESHOLD = 3
|
||||
|
||||
# To avoid noise, this value ensures that the percent of unchanged entities
|
||||
# is not inflated due to small numbers of entities.
|
||||
L10N_MIN_ENTITIES = 18
|
||||
|
||||
LOCALE_CACHE = {}
|
||||
|
||||
|
||||
def _list_locales(err, xpi_package=None):
|
||||
"Returns a raw list of locales from chrome.manifest"
|
||||
|
||||
chrome = None
|
||||
if xpi_package is not None:
|
||||
# Handle a reference XPI
|
||||
chrome = ChromeManifest(xpi_package.read("chrome.manifest"),
|
||||
"chrome.manifest")
|
||||
else:
|
||||
# Handle the current XPI
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return None
|
||||
|
||||
pack_locales = chrome.get_triples("locale")
|
||||
return list(pack_locales)
|
||||
|
||||
def _get_locales(err, xpi_package=None, locales=None):
|
||||
"Returns a list of locales from the chrome.manifest file."
|
||||
|
||||
if locales is None:
|
||||
locales = _list_locales(err, xpi_package)
|
||||
if not locales:
|
||||
return None
|
||||
|
||||
processed_locales = {}
|
||||
for locale in locales:
|
||||
locale_jar = locale["object"].split()
|
||||
|
||||
location = locale_jar[-1]
|
||||
|
||||
# Locales can be bundled in JARs
|
||||
jarred = location.startswith("jar:")
|
||||
if jarred:
|
||||
# We just care about the JAR path
|
||||
location = location[4:]
|
||||
split_location = location.split("!", 2)
|
||||
# Ignore malformed JAR URIs.
|
||||
if len(split_location) < 2:
|
||||
continue
|
||||
|
||||
path, location = split_location
|
||||
|
||||
locale_desc = {"predicate": locale["predicate"],
|
||||
"target": location,
|
||||
"name": locale_jar[0],
|
||||
"jarred": jarred}
|
||||
if jarred:
|
||||
locale_desc["path"] = path
|
||||
|
||||
locale_name = "%s:%s" % (locale["predicate"], locale_jar[0])
|
||||
if locale_name not in processed_locales:
|
||||
processed_locales[locale_name] = locale_desc
|
||||
|
||||
return processed_locales
|
||||
|
||||
|
||||
def _get_locale_manager(err, xpi_package, description,
|
||||
no_cache=False):
|
||||
"Returns the XPIManager object for a locale"
|
||||
|
||||
if not description["jarred"]:
|
||||
return xpi_package
|
||||
|
||||
path = description["path"]
|
||||
|
||||
if path in LOCALE_CACHE and not no_cache:
|
||||
return LOCALE_CACHE[path]
|
||||
|
||||
if path not in xpi_package:
|
||||
# TODO: Pass the filename of the triple's manifest.
|
||||
err.warning(("testcases_l10ncompleteness",
|
||||
"_get_locale_manager",
|
||||
"manager_absent"),
|
||||
"Listed locale does not exist",
|
||||
["A locale JAR is listed in chrome.manifest, but it could "
|
||||
"not be located. Check the spelling and capitalization "
|
||||
"of the path.",
|
||||
"Missing JAR: %s" % path],
|
||||
filename="chrome.manifest")
|
||||
return None
|
||||
jar = StringIO(xpi_package.read(path))
|
||||
locale = XPIManager(jar, mode="r", name=path)
|
||||
|
||||
if not no_cache:
|
||||
LOCALE_CACHE[path] = locale
|
||||
return locale
|
||||
|
||||
|
||||
@decorator.register_test(tier=4)
|
||||
def test_xpi(err, xpi_package):
|
||||
"""Tests an XPI (or JAR, really) for L10n completeness"""
|
||||
|
||||
# Skip over incompatible (or unnecessary) package types.
|
||||
if (err.detected_type != PACKAGE_EXTENSION or
|
||||
err.is_nested_package()):
|
||||
return None
|
||||
|
||||
# Don't even both with the test(s) if there's no chrome.manifest.
|
||||
if "chrome.manifest" not in xpi_package:
|
||||
return None
|
||||
|
||||
raw_locales = _list_locales(err)
|
||||
|
||||
# We need at least a reference and a target.
|
||||
num_locales = len(raw_locales)
|
||||
if num_locales < 2:
|
||||
if num_locales == 0:
|
||||
# TODO: Pass the filename of the triple's manifest.
|
||||
err.notice(("testcases_l10ncompleteness",
|
||||
"test_xpi",
|
||||
"no_locales"),
|
||||
"Add-on cannot be localized",
|
||||
"The add-on doesn't have any locale entries in its "
|
||||
"chrome.manifest file, making it difficult to "
|
||||
"localize.",
|
||||
filename="chrome.manifest")
|
||||
return
|
||||
|
||||
locales = _get_locales(err, None, raw_locales)
|
||||
|
||||
# Use the first locale by default
|
||||
ref_lang = locales.values()[0]["name"]
|
||||
# Try to find en-US, as this is where the majority of users is.
|
||||
if ref_lang != "en-US":
|
||||
for name, locale in locales.items():
|
||||
if locale["name"] == "en-US":
|
||||
ref_lang = "en-US"
|
||||
break
|
||||
|
||||
references = {}
|
||||
for locale_name, locale in locales.items():
|
||||
if locale["name"] == ref_lang:
|
||||
references[locale_name] = locale
|
||||
|
||||
references_locales = {}
|
||||
for ref_name, reference in references.items():
|
||||
references_locales[ref_name] = \
|
||||
_get_locale_manager(err, xpi_package, reference)
|
||||
|
||||
def get_reference(locale):
|
||||
for r_name, r in references.items():
|
||||
if r["predicate"] == locale["predicate"]:
|
||||
return r_name, r
|
||||
return None, None
|
||||
|
||||
# Loop through the locales and test the valid ones.
|
||||
for name, locale in locales.items():
|
||||
# Ignore the reference locale
|
||||
if name in references:
|
||||
continue
|
||||
|
||||
ref_name, reference = get_reference(locale)
|
||||
|
||||
# If we can't find a reference for the current namespace, bail.
|
||||
if reference is None or ref_name is None:
|
||||
continue
|
||||
|
||||
target_locale = _get_locale_manager(err,
|
||||
xpi_package,
|
||||
locale)
|
||||
# If we can't find the target locale, just bail.
|
||||
if target_locale is None:
|
||||
continue
|
||||
|
||||
# Isolate each of the target locales' results.
|
||||
results = _compare_packages(references_locales[ref_name],
|
||||
target_locale,
|
||||
reference["target"],
|
||||
locale["target"])
|
||||
similar = reference["name"].startswith(locale["name"].split("-")[0])
|
||||
_aggregate_results(err, results, locale, similar)
|
||||
|
||||
# Clear the cache at the end of the test
|
||||
L10N_CACHE = {}
|
||||
|
||||
|
||||
@decorator.register_test(tier=4, expected_type=PACKAGE_LANGPACK)
|
||||
def test_lp_xpi(err, xpi_package):
|
||||
"Tests a language pack for L10n completeness"
|
||||
|
||||
# Don't even both with the test(s) if there's no chrome.manifest.
|
||||
if "chrome.manifest" not in xpi_package:
|
||||
return None
|
||||
|
||||
locales = _get_locales(err);
|
||||
|
||||
# Get the reference packages.
|
||||
references = []
|
||||
support_references = err.get_resource("supports")
|
||||
if not support_references:
|
||||
references.append("firefox")
|
||||
err.info(("testcases_l10ncompleteness",
|
||||
"test_lp_xpi",
|
||||
"missing_app_support"),
|
||||
"Supported app missing in localization completeness.",
|
||||
"While testing in localization comleteness, a list of "
|
||||
"supported applications for the language pack was not found. "
|
||||
"This is likely because there are no listed "
|
||||
"<em:targetApplication> elements in the install.rdf file.")
|
||||
else:
|
||||
for support in support_references:
|
||||
ref_xpi = XPIManager(os.path.join(os.path.dirname(__file__),
|
||||
"langpacks/%s.xpi" % support))
|
||||
ref_xpi.app_name = support
|
||||
reference_locales = _get_locales(None, ref_xpi)
|
||||
|
||||
references.append((ref_xpi, reference_locales))
|
||||
|
||||
# Iterate each supported reference package
|
||||
for (ref_xpi, ref_locales) in references:
|
||||
# Iterate each locale in each supported reference package
|
||||
ref_pack = _get_locale_manager(err,
|
||||
ref_xpi,
|
||||
{"path": "en-US.jar",
|
||||
"jarred": True},
|
||||
no_cache=True)
|
||||
for ref_locale_name in ref_locales:
|
||||
ref_locale = ref_locales[ref_locale_name]
|
||||
ref_predicate = ref_locale["predicate"]
|
||||
corresp_locales = [locales[name] for name
|
||||
in locales
|
||||
if locales[name]["predicate"] == ref_predicate]
|
||||
# If we found no matching locale, then it's missing from the pack
|
||||
if not corresp_locales:
|
||||
err.warning(("testcases_l10ncompleteness",
|
||||
"test_lp_xpi",
|
||||
"find_corresponding_locale"),
|
||||
"Could not find corresponding locale",
|
||||
["A locale was found in the reference package, "
|
||||
"however it was not found in the target package.",
|
||||
"Missing locale: %s" % ref_predicate],
|
||||
filename="chrome.manifest")
|
||||
continue
|
||||
|
||||
target_locale = corresp_locales[0]
|
||||
target_pack = _get_locale_manager(err,
|
||||
xpi_package,
|
||||
target_locale)
|
||||
if target_pack is None:
|
||||
continue
|
||||
|
||||
results = _compare_packages(reference=ref_pack,
|
||||
target=target_pack,
|
||||
ref_base=ref_locale["target"],
|
||||
locale_base=target_locale["target"])
|
||||
|
||||
# Report the findings after each supported app's locale
|
||||
_aggregate_results(err, results, target_locale)
|
||||
|
||||
# Clear the cache at the end of the test
|
||||
LOCALE_CACHE = {}
|
||||
|
||||
|
||||
def _compare_packages(reference, target, ref_base="", locale_base=""):
|
||||
"Compares two L10n-compatible packages to one another."
|
||||
|
||||
tar_files = target.package_contents()
|
||||
|
||||
results = []
|
||||
total_entities = 0
|
||||
|
||||
ref_base = ref_base.lstrip("/")
|
||||
locale_base = locale_base.lstrip("/")
|
||||
|
||||
l10n_docs = ("dtd", "properties", "xhtml", "ini", "inc")
|
||||
parsable_docs = ("dtd", "properties")
|
||||
|
||||
for name in reference:
|
||||
|
||||
entity_count = 0
|
||||
|
||||
# Skip directory entries.
|
||||
if name.endswith("/"): # pragma: no cover
|
||||
continue
|
||||
|
||||
# Ignore files not considered reference files.
|
||||
if ref_base and not name.startswith(ref_base):
|
||||
continue
|
||||
|
||||
extension = name.split(".")[-1]
|
||||
if extension not in l10n_docs:
|
||||
continue
|
||||
parsable = extension in parsable_docs
|
||||
|
||||
if parsable:
|
||||
ref_doc = _parse_l10n_doc(name,
|
||||
reference.read(name),
|
||||
no_encoding=True)
|
||||
else:
|
||||
ref_doc = ()
|
||||
|
||||
tar_name = locale_base + name[len(ref_base):]
|
||||
if tar_name not in tar_files:
|
||||
results.append({"type": "missing_files",
|
||||
"entities": len(ref_doc),
|
||||
"filename": tar_name})
|
||||
continue
|
||||
|
||||
if not parsable:
|
||||
continue
|
||||
|
||||
tar_doc = _parse_l10n_doc(tar_name, target.read(tar_name))
|
||||
|
||||
if not tar_doc.expected_encoding:
|
||||
results.append({"type": "unexpected_encoding",
|
||||
"filename": tar_name,
|
||||
"expected_encoding": tar_doc.suitable_encoding})
|
||||
|
||||
missing_entities = []
|
||||
unchanged_entities = []
|
||||
|
||||
for rname, rvalue, rline in ref_doc.items:
|
||||
entity_count += 1
|
||||
|
||||
if rname not in tar_doc.entities:
|
||||
missing_entities.append(rname)
|
||||
continue
|
||||
|
||||
if (not rname.startswith("pref.timezone.") and
|
||||
rvalue == tar_doc.entities[rname] and
|
||||
len(rvalue) > L10N_LENGTH_THRESHOLD and
|
||||
not fnmatch.fnmatch(rvalue, "http*://*")):
|
||||
|
||||
unchanged_entities.append((rname, rline))
|
||||
continue
|
||||
|
||||
if missing_entities:
|
||||
results.append({"type": "missing_entities",
|
||||
"entities": len(missing_entities),
|
||||
"filename": tar_name,
|
||||
"missing_entities": missing_entities})
|
||||
if unchanged_entities:
|
||||
results.append({"type": "unchanged_entity",
|
||||
"entities": len(unchanged_entities),
|
||||
"filename": tar_name,
|
||||
"unchanged_entities": unchanged_entities})
|
||||
|
||||
results.append({"type": "file_entity_count",
|
||||
"filename": tar_name,
|
||||
"entities": entity_count})
|
||||
|
||||
total_entities += entity_count
|
||||
|
||||
results.append({"type": "total_entities",
|
||||
"entities": total_entities})
|
||||
return results
|
||||
|
||||
|
||||
def _parse_l10n_doc(name, doc, no_encoding=False):
|
||||
"Parses an L10n document."
|
||||
|
||||
extension = name.split(".")[-1].lower()
|
||||
|
||||
handlers = {"dtd": dtd.DTDParser,
|
||||
"properties": properties.PropertiesParser}
|
||||
# These are expected encodings for the various files.
|
||||
handler_formats = ("ASCII", "UTF_8")
|
||||
if extension not in handlers:
|
||||
return None
|
||||
|
||||
wrapper = StringIO(doc)
|
||||
loc_doc = handlers[extension](wrapper)
|
||||
|
||||
# Allow the parse to specify files to skip for encoding checks
|
||||
if not no_encoding:
|
||||
encoding = fastchardet.detect(doc)["encoding"].upper()
|
||||
loc_doc.expected_encoding = encoding in handler_formats
|
||||
loc_doc.suitable_encoding = handler_formats
|
||||
|
||||
return loc_doc
|
||||
|
||||
|
||||
def _aggregate_results(err, results, locale, similar=False, base="en-US"):
|
||||
"""Compiles the errors and warnings in the L10n results list into
|
||||
error bundler errors and warnings."""
|
||||
|
||||
total_entities = 0
|
||||
unchanged_entities = 0
|
||||
unchanged_entity_list = {}
|
||||
entity_count = {}
|
||||
unexpected_encodings = []
|
||||
|
||||
for ritem in results:
|
||||
if "filename" in ritem:
|
||||
rfilename = ritem["filename"]
|
||||
|
||||
rtype = ritem["type"]
|
||||
if rtype == "missing_files":
|
||||
err.warning(("testcases_l10ncompleteness",
|
||||
"_aggregate_results",
|
||||
"missing_file"),
|
||||
"Missing translation file",
|
||||
["Localizations must include a translated copy of each "
|
||||
"file in the reference locale. The required files may "
|
||||
"vary from target application to target application.",
|
||||
"Missing translation file: %s" % rfilename],
|
||||
locale["target"])
|
||||
elif rtype == "missing_entities":
|
||||
err.warning(("testcases_l10ncompleteness",
|
||||
"_aggregate_results",
|
||||
"missing_translation_entity"),
|
||||
"Missing translation entity",
|
||||
["Localizations must include a translated copy of each "
|
||||
"entity from each file in the reference locale. The "
|
||||
"required files may vary from target application to "
|
||||
"target application.",
|
||||
"Missing Entities: %s" %
|
||||
", ".join(ritem["missing_entities"])],
|
||||
[locale["target"], rfilename])
|
||||
elif rtype == "unchanged_entity":
|
||||
filename = ritem["filename"]
|
||||
if not filename in unchanged_entity_list:
|
||||
unchanged_entity_list[filename] = {"count": 0,
|
||||
"entities": []}
|
||||
unchanged = unchanged_entity_list[filename]
|
||||
unchanged["count"] += ritem["entities"]
|
||||
unchanged["entities"].extend(ritem["unchanged_entities"])
|
||||
elif rtype == "total_entities":
|
||||
total_entities += ritem["entities"]
|
||||
elif rtype == "file_entity_count":
|
||||
entity_count[ritem["filename"]] = ritem["entities"]
|
||||
elif rtype == "unexpected_encoding":
|
||||
unexpected_encodings.append(
|
||||
(ritem["filename"],
|
||||
", ".join(ritem["expected_encoding"])))
|
||||
|
||||
# Determine the locale's filename argument in advance since we'll use it
|
||||
# for every message
|
||||
locale_filename = [locale["target"]]
|
||||
if locale["jarred"]:
|
||||
locale_filename.append(locale["path"].lstrip("/"))
|
||||
|
||||
agg_unchanged = []
|
||||
if not similar:
|
||||
unchanged_percentage = L10N_THRESHOLD
|
||||
else:
|
||||
unchanged_percentage = L10N_SIMILAR_THRESHOLD
|
||||
for name, count in entity_count.items():
|
||||
if name not in unchanged_entity_list or \
|
||||
count == 0:
|
||||
continue
|
||||
|
||||
unchanged = unchanged_entity_list[name]
|
||||
total_adjusted = max(count, L10N_MIN_ENTITIES)
|
||||
percentage = float(unchanged["count"]) / float(total_adjusted)
|
||||
if percentage >= unchanged_percentage:
|
||||
agg_unchanged.append(
|
||||
"%s: %d/%d entities unchanged (%s) at %d percent" %
|
||||
(name,
|
||||
unchanged["count"],
|
||||
count,
|
||||
", ".join(["%s (%d)" % (e, line)
|
||||
for e, line
|
||||
in unchanged["entities"]]),
|
||||
percentage * 100))
|
||||
|
||||
if agg_unchanged:
|
||||
err.notice(("testcases_l10ncompleteness",
|
||||
"_aggregate_results",
|
||||
"unchnged_entities"),
|
||||
"Unchanged translation entities",
|
||||
["Localizations must include a translated copy of each "
|
||||
"entity from each file in the reference locale. These "
|
||||
"translations SHOULD differ from the localized text in "
|
||||
"the reference package.",
|
||||
agg_unchanged],
|
||||
locale_filename)
|
||||
|
||||
if unexpected_encodings:
|
||||
# Compile all of the encoding errors into one nice warning.
|
||||
compilation = ["Detected files:"]
|
||||
for target in unexpected_encodings:
|
||||
compilation.append("%s\n (expected: %s)" % target)
|
||||
|
||||
err.warning(("testcases_l10ncompleteness",
|
||||
"_aggregate_results",
|
||||
"unexpected_encodings"),
|
||||
"Unexpected encodings in locale files",
|
||||
["Localization files were encountered that used encodings "
|
||||
"that are not characteristic of those types of files.",
|
||||
"\n".join(compilation),
|
||||
"Localization files with the wrong encoding can cause "
|
||||
"issues with locales that include non-ASCII characters."],
|
||||
locale_filename)
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
import fnmatch
|
||||
import re
|
||||
|
||||
from validator import decorator
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
from validator.constants import PACKAGE_LANGPACK
|
||||
|
||||
BAD_LINK = '(href|src)=["\'](?!chrome:\/\/)(([a-z]*:)?\/\/|data:)'
|
||||
|
||||
|
||||
@decorator.register_test(tier=2, expected_type=PACKAGE_LANGPACK)
|
||||
def test_langpack_manifest(err, xpi_package=None):
|
||||
"""Tests the chrome.manifest files in the package for
|
||||
compliance with the standard language pack triples."""
|
||||
|
||||
# Don't even both with the test(s) if there's no chrome.manifest.
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
for triple in chrome.triples:
|
||||
subject = triple["subject"]
|
||||
# Test to make sure that the triple's subject is valid
|
||||
if subject not in ("locale", "override", "manifest"):
|
||||
err.warning(("testcases_langpack",
|
||||
"test_langpack_manifest",
|
||||
"invalid_subject"),
|
||||
"Invalid chrome.manifest subject",
|
||||
["chrome.manifest files in language packs are only "
|
||||
"allowed to contain items that are prefixed with "
|
||||
"'locale', 'manifest', or 'override'. Other values "
|
||||
"are not allowed.",
|
||||
"Invalid subject: %s" % subject],
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
||||
if subject == "override":
|
||||
object_ = triple["object"]
|
||||
predicate = triple["predicate"]
|
||||
|
||||
pattern = "chrome://*/locale/*"
|
||||
|
||||
if not fnmatch.fnmatch(object_, pattern) or \
|
||||
not fnmatch.fnmatch(predicate, pattern):
|
||||
err.warning(("testcases_langpack",
|
||||
"test_langpack_manifest",
|
||||
"invalid_override"),
|
||||
"Invalid chrome.manifest object/predicate.",
|
||||
"'override' entry does not match '%s'" % pattern,
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
||||
|
||||
# This function is called by content.py
|
||||
def test_unsafe_html(err, filename, data):
|
||||
"Tests for unsafe HTML tags in language pack files."
|
||||
|
||||
context = ContextGenerator(data)
|
||||
|
||||
unsafe_pttrn = re.compile('<(script|embed|object)', re.I)
|
||||
|
||||
match = unsafe_pttrn.search(data)
|
||||
if match:
|
||||
line = context.get_line(match.start())
|
||||
err.warning(("testcases_langpack",
|
||||
"test_unsafe_html",
|
||||
"unsafe_content_html"),
|
||||
"Unsafe HTML found in language pack files.",
|
||||
"Language packs are not allowed to contain scripts, "
|
||||
"embeds, or other executable code in the language "
|
||||
"definition files.",
|
||||
filename,
|
||||
line=line,
|
||||
context=context)
|
||||
|
||||
remote_pttrn = re.compile(BAD_LINK, re.I)
|
||||
|
||||
match = remote_pttrn.search(data)
|
||||
if match:
|
||||
line = context.get_line(match.start())
|
||||
err.warning(("testcases_langpack",
|
||||
"test_unsafe_html",
|
||||
"unsafe_content_link"),
|
||||
"Unsafe remote resource found in language pack.",
|
||||
"Language packs are not allowed to contain references to "
|
||||
"remote resources.",
|
||||
filename,
|
||||
line=line,
|
||||
context=context)
|
||||
|
||||
|
Двоичные данные
appvalidator/testcases/langpacks/fennec.xpi
Двоичные данные
appvalidator/testcases/langpacks/fennec.xpi
Двоичный файл не отображается.
Двоичные данные
appvalidator/testcases/langpacks/firefox.xpi
Двоичные данные
appvalidator/testcases/langpacks/firefox.xpi
Двоичный файл не отображается.
Двоичные данные
appvalidator/testcases/langpacks/mozilla.xpi
Двоичные данные
appvalidator/testcases/langpacks/mozilla.xpi
Двоичный файл не отображается.
Двоичные данные
appvalidator/testcases/langpacks/seamonkey.xpi
Двоичные данные
appvalidator/testcases/langpacks/seamonkey.xpi
Двоичный файл не отображается.
Двоичные данные
appvalidator/testcases/langpacks/sunbird.xpi
Двоичные данные
appvalidator/testcases/langpacks/sunbird.xpi
Двоичный файл не отображается.
Двоичные данные
appvalidator/testcases/langpacks/thunderbird.xpi
Двоичные данные
appvalidator/testcases/langpacks/thunderbird.xpi
Двоичный файл не отображается.
|
@ -2,7 +2,7 @@ import re
|
|||
import fnmatch
|
||||
import cssutils
|
||||
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
from appvalidator.contextgenerator import ContextGenerator
|
||||
|
||||
BAD_URL_PAT = "url\(['\"]?(?!(chrome:|resource:))(\/\/|(ht|f)tps?:\/\/|data:)[a-z0-9\/\-\.#]*['\"]?\)"
|
||||
BAD_URL = re.compile(BAD_URL_PAT, re.I)
|
||||
|
@ -76,44 +76,6 @@ def _run_css_tests(err, tokens, filename, line_start=0, context=None):
|
|||
print line + line_start
|
||||
continue
|
||||
|
||||
# Save the last descriptor for reference.
|
||||
if tok_type == "IDENT":
|
||||
last_descriptor = value.lower()
|
||||
|
||||
elif tok_type == "URI":
|
||||
|
||||
# If we hit a URI after -moz-binding, we may have a
|
||||
# potential security issue.
|
||||
if last_descriptor == "-moz-binding":
|
||||
# We need to make sure the URI is not remote.
|
||||
if BAD_URL.match(value):
|
||||
err.warning(("testcases_markup_csstester",
|
||||
"_run_css_tests",
|
||||
"-moz-binding_external"),
|
||||
"Cannot reference external scripts.",
|
||||
"-moz-binding cannot reference external "
|
||||
"scripts in CSS. This is considered to be a "
|
||||
"security issue. The script file must be "
|
||||
"placed in the /content/ directory of the "
|
||||
"package.",
|
||||
filename,
|
||||
line=line + line_start,
|
||||
context=context.get_context(line))
|
||||
|
||||
elif tok_type == "HASH":
|
||||
# Search for interference with the identity box.
|
||||
if value == "#identity-box":
|
||||
identity_box_mods.append(str(line + line_start))
|
||||
|
||||
if identity_box_mods:
|
||||
err.warning(("testcases_markup_csstester",
|
||||
"_run_css_tests",
|
||||
"identity_box"),
|
||||
"Modification to identity box.",
|
||||
["The identity box (#identity-box) is a sensitive piece "
|
||||
"of the interface and should not be modified.",
|
||||
"Lines: %s" % ", ".join(identity_box_mods)],
|
||||
filename)
|
||||
if unicode_errors:
|
||||
err.info(("testcases_markup_csstester",
|
||||
"test_css_file",
|
||||
|
|
|
@ -6,13 +6,12 @@ try:
|
|||
except ImportError: # pragma: no cover
|
||||
import html.parser as htmlparser
|
||||
|
||||
import validator.testcases.scripting as scripting
|
||||
import validator.unicodehelper as unicodehelper
|
||||
from validator.testcases.markup import csstester
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
from validator.constants import *
|
||||
from validator.compat import FX6_DEFINITION
|
||||
from validator.decorator import version_range
|
||||
from .. import scripting
|
||||
import appvalidator.unicodehelper as unicodehelper
|
||||
from . import csstester
|
||||
from appvalidator.contextgenerator import ContextGenerator
|
||||
from appvalidator.constants import *
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
@ -28,10 +27,6 @@ DOM_MUTATION_HANDLERS = (
|
|||
"ondomcharacterdatamodified", "ondomelementnamechanged",
|
||||
"ondomnodeinserted", "ondomnodeinsertedintodocument", "ondomnoderemoved",
|
||||
"ondomnoderemovedfromdocument", "ondomsubtreemodified", )
|
||||
UNSAFE_THEME_XBL = ("constructor", "destructor", "field", "getter",
|
||||
"implementation", "setter", )
|
||||
|
||||
GENERIC_IDS = ("string-bundle", "strings", )
|
||||
|
||||
|
||||
class MarkupParser(htmlparser.HTMLParser):
|
||||
|
@ -50,7 +45,6 @@ class MarkupParser(htmlparser.HTMLParser):
|
|||
self.xml_state = []
|
||||
self.xml_line_stack = []
|
||||
self.xml_buffer = []
|
||||
self.xbl = False
|
||||
|
||||
self.reported = set()
|
||||
self.found_scripts = set() # A set of script URLs in the doc.
|
||||
|
@ -197,202 +191,11 @@ class MarkupParser(htmlparser.HTMLParser):
|
|||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
# Test for banned XBL in themes.
|
||||
if self.err.detected_type == PACKAGE_THEME:
|
||||
if tag.startswith("xbl:"):
|
||||
self.xbl = True
|
||||
tag = tag[4:]
|
||||
|
||||
# Find XBL elements.
|
||||
if self.xbl:
|
||||
if tag in UNSAFE_THEME_XBL:
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"unsafe_theme_xbl_element"),
|
||||
warning="Banned XBL element in theme.",
|
||||
description=["Certain XBL elements are disallowed in "
|
||||
"themes.",
|
||||
"Element: <xbl:%s>" % tag],
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
elif (tag == "property" and
|
||||
any(a[0] in (u"onset", u"onget") for a in attrs)):
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"theme_xbl_property"),
|
||||
warning="Themes are not allowed to use XBL properties",
|
||||
description="XBL properties cannot be used in themes.",
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
# Test for banned elements in language pack and theme markup.
|
||||
if self.err.detected_type in (PACKAGE_LANGPACK, PACKAGE_THEME):
|
||||
if (tag in UNSAFE_TAGS or
|
||||
(self.err.detected_type == PACKAGE_THEME and
|
||||
tag in UNSAFE_THEME_TAGS)):
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"unsafe_langpack_theme"),
|
||||
warning="Unsafe tag for add-on type",
|
||||
description=["A tag in your markup has been marked as "
|
||||
"being potentially unsafe. Consider "
|
||||
"alternate means of accomplishing what the "
|
||||
"code executed by this tag performs.",
|
||||
'Tag "%s" is disallowed.' % tag],
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
if DEBUG: # pragma: no cover
|
||||
print "Unsafe Tag ------"
|
||||
|
||||
# Make sure all src/href attributes are local
|
||||
for attr in attrs:
|
||||
if attr[0].lower() in ("src", "href") and \
|
||||
not self._is_url_local(attr[1].lower()):
|
||||
self.err.warning(("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"remote_src_href"),
|
||||
"src/href attributes must be local.",
|
||||
"Language packs require that all src and "
|
||||
"href attributes are relative URLs.",
|
||||
self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
if tag == "prefwindow":
|
||||
# Flag <prefwindow> elements without IDs.
|
||||
|
||||
if not any((key == "id") for key, val in attrs):
|
||||
self.err.warning(
|
||||
err_id=("markup", "starttag", "prefwindow_id"),
|
||||
warning="`<prefwindow>` elements must have IDs.",
|
||||
description="`<prefwindow>` elements without `id` "
|
||||
"attributes cause errors to be reported "
|
||||
"in the error console and prevent "
|
||||
"persistence of certain properties of the "
|
||||
"dialog.",
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
elif tag in ("iframe", "browser") and self.extension == "xul":
|
||||
# Bork if XUL iframe has no type attribute
|
||||
|
||||
type_ = None
|
||||
src = None
|
||||
for attr in attrs:
|
||||
attr_name = attr[0].lower()
|
||||
if attr_name == "type":
|
||||
type_ = attr[1].lower()
|
||||
elif attr_name == "src":
|
||||
src = attr[1].lower()
|
||||
|
||||
# We say it's true by default to catch elements that are
|
||||
# type="chrome" without an src="" attribute.
|
||||
remote_src = True
|
||||
if isinstance(src, types.StringTypes):
|
||||
remote_src = not self._is_url_local(src)
|
||||
|
||||
if (type_ and
|
||||
not (type_ in SAFE_IFRAME_TYPES or
|
||||
not remote_src)):
|
||||
self.err.warning(("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"iframe_type_unsafe"),
|
||||
"iframe/browser missing 'type' attribute",
|
||||
"All iframe and browser elements must have "
|
||||
"either a valid `type` attribute or a `src` "
|
||||
"attribute that points to a local file.",
|
||||
self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
elif ((not type_ or
|
||||
type_ not in SAFE_IFRAME_TYPES) and
|
||||
remote_src):
|
||||
self.err.warning(("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"iframe_type_unsafe"),
|
||||
"Typeless iframes/browsers must be local.",
|
||||
"iframe and browser elements that lack a type "
|
||||
"attribute must always have src attributes "
|
||||
"that reference local resources.",
|
||||
self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
elif tag == "script" and self.extension == "xul":
|
||||
# Per the Addon Validator Spec (v2), scripts in XUL
|
||||
# must not be remote.
|
||||
|
||||
src = None
|
||||
for attr in attrs:
|
||||
if attr[0].lower() == "src":
|
||||
src = attr[1].lower()
|
||||
break
|
||||
|
||||
if src:
|
||||
if not self._is_url_local(src):
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"banned_remote_scripts"),
|
||||
warning="Scripts must not be remote in XUL",
|
||||
description="In XUL, <script> tags must not be "
|
||||
"referenced to script files that are "
|
||||
"hosted remotely.",
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
else:
|
||||
self.found_scripts.add(src)
|
||||
|
||||
# Find CSS and JS attributes and handle their values like they
|
||||
# would otherwise be handled by the standard parser flow.
|
||||
for attr in attrs:
|
||||
attr_name, attr_value = attr[0].lower(), attr[1]
|
||||
|
||||
if (attr_name == "xmlns:xbl" and
|
||||
attr_value == "http://www.mozilla.org/xbl"):
|
||||
self.xbl = True
|
||||
|
||||
# Test that an absolute URI isn't referenced in Jetpack 1.4.
|
||||
if (self.is_jetpack and
|
||||
attr_value.startswith("resource://") and
|
||||
"-data/" in attr_value):
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag", "jetpack_abs_uri"),
|
||||
warning="Absolute URI referenced in Jetpack 1.4",
|
||||
description=["As of Jetpack 1.4, absolute URIs are no "
|
||||
"longer allowed within add-ons.",
|
||||
"See %s for more information." %
|
||||
JETPACK_URI_URL],
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context,
|
||||
compatibility_type="error")
|
||||
|
||||
if (self.err.detected_type == PACKAGE_THEME and
|
||||
attr_value.startswith(("data:", "javascript:"))):
|
||||
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"theme_attr_prefix"),
|
||||
warning="Attribute contains banned prefix",
|
||||
description=["A mark element's attribute contains a "
|
||||
"prefix which is not allowed in themes.",
|
||||
"Attribute: %s" % attr_name],
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
if attr_name == "style":
|
||||
csstester.test_css_snippet(self.err,
|
||||
self.filename,
|
||||
|
@ -420,47 +223,6 @@ class MarkupParser(htmlparser.HTMLParser):
|
|||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
elif (self.extension == "xul" and
|
||||
attr_name in ("insertbefore", "insertafter") and
|
||||
any((id in attr_value) for id in ("menu_pageSource",
|
||||
"menu_pageinspect",
|
||||
"javascriptConsole",
|
||||
"webConsole"))):
|
||||
self.err.notice(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag",
|
||||
"incompatible_menu_items"),
|
||||
notice="Menu item has been moved",
|
||||
description="Your add-on has an overlay that uses the "
|
||||
"insertbefore or insertafter attribute "
|
||||
"pointing to menuitems that have been moved "
|
||||
"to a different menu item. Your overlay items "
|
||||
"may appear in unexpected locations because "
|
||||
"of this. See "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=653221"
|
||||
" for more information.",
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context,
|
||||
for_appversions=FX6_DEFINITION,
|
||||
compatibility_type="warning")
|
||||
|
||||
|
||||
# Test for generic IDs
|
||||
if attr_name == "id" and attr_value in GENERIC_IDS:
|
||||
self.err.warning(
|
||||
err_id=("testcases_markup_markuptester",
|
||||
"handle_starttag", "generic_ids"),
|
||||
warning="Overlay contains generically-named IDs",
|
||||
description="An overlay is using a generically-named ID "
|
||||
"that could cause compatibility problems with "
|
||||
"other add-ons. Add-ons must namespace all IDs "
|
||||
"in the overlay, in the same way that "
|
||||
"JavaScript objects must be namespaced.",
|
||||
filename=self.filename,
|
||||
line=self.line,
|
||||
context=self.context)
|
||||
|
||||
# When the dev forgets their <!-- --> on a script tag, bad
|
||||
# things happen.
|
||||
if "script" in self.xml_state and tag != "script":
|
||||
|
@ -627,9 +389,6 @@ class MarkupParser(htmlparser.HTMLParser):
|
|||
|
||||
def _is_url_local(self, url):
|
||||
|
||||
if url.startswith("chrome://"):
|
||||
return True
|
||||
|
||||
pattern = re.compile("(ht|f)tps?://")
|
||||
|
||||
return not pattern.match(url)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from fnmatch import fnmatch as fnm
|
||||
|
||||
from validator.constants import (FF4_MIN, FIREFOX_GUID, FENNEC_GUID,
|
||||
THUNDERBIRD_GUID as TB_GUID, ANDROID_GUID,
|
||||
PACKAGE_DICTIONARY, )
|
||||
import validator.decorator as decorator
|
||||
from validator.decorator import version_range
|
||||
from ..constants import (FIREFOX_GUID, FENNEC_GUID,
|
||||
THUNDERBIRD_GUID as TB_GUID, ANDROID_GUID,
|
||||
PACKAGE_DICTIONARY, )
|
||||
from .. import decorator
|
||||
|
||||
# Detect blacklisted files based on their extension.
|
||||
blacklisted_extensions = ("dll", "exe", "dylib", "so",
|
||||
|
@ -125,40 +124,6 @@ def test_godlikea(err, xpi_package):
|
|||
filename="chrome/godlikea.jar")
|
||||
|
||||
|
||||
@decorator.register_test(
|
||||
tier=5,
|
||||
versions={FIREFOX_GUID: version_range("firefox", FF4_MIN),
|
||||
TB_GUID: version_range("thunderbird", "3.3a4pre"),
|
||||
FENNEC_GUID: version_range("fennec", "4.0"),
|
||||
ANDROID_GUID: version_range("android", "11.0a1"),})
|
||||
def test_compatibility_binary(err, xpi_package):
|
||||
"""
|
||||
Flags only binary content as being incompatible with future app releases.
|
||||
"""
|
||||
|
||||
description = ("Add-ons with binary components must have their "
|
||||
"compatibility manually adjusted. Please test your add-on "
|
||||
"against the new version before updating your maxVersion.")
|
||||
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
for triple in chrome.triples:
|
||||
if triple["subject"] == "binary-component":
|
||||
err.metadata["binary_components"] = True
|
||||
err.notice(
|
||||
err_id=("testcases_packagelayout",
|
||||
"test_compatibility_binary",
|
||||
"disallowed_file_type"),
|
||||
notice="Flagged file type found",
|
||||
description=["A file (%s) was registered as a binary "
|
||||
"component." % triple["predicate"],
|
||||
description],
|
||||
filename=triple["predicate"],
|
||||
compatibility_type="error")
|
||||
|
||||
|
||||
@decorator.register_test(tier=1)
|
||||
def test_layout_all(err, xpi_package):
|
||||
"Tests the well-formedness of extensions."
|
||||
|
@ -226,88 +191,6 @@ def test_emunpack(err, xpi_package):
|
|||
tier=1)
|
||||
return
|
||||
|
||||
# Covers bug 551714
|
||||
|
||||
# This only applies to FF4
|
||||
if not err.get_resource("ff4"):
|
||||
return
|
||||
|
||||
for file_ in xpi_package:
|
||||
if file_.endswith(".jar"):
|
||||
err.notice(("testcases_packagelayout",
|
||||
"test_emunpack",
|
||||
"should_be_false"),
|
||||
"Add-on contains JAR files, no <em:unpack>",
|
||||
"The add-on contains JAR files and does not set "
|
||||
"<em:unpack> to 'true'. This can result in "
|
||||
"performance issues in add-ons that target Gecko "
|
||||
"4. It is recommended that you consider no longer "
|
||||
"using JAR files to package your chrome files.",
|
||||
filename="install.rdf",
|
||||
tier=1)
|
||||
return
|
||||
|
||||
|
||||
@decorator.register_test(tier=1, expected_type=3)
|
||||
def test_dictionary_layout(err, xpi_package=None):
|
||||
"""Ensures that dictionary packages contain the necessary
|
||||
components and that there are no other extraneous files lying
|
||||
around."""
|
||||
|
||||
# Define rules for the structure.
|
||||
mandatory_files = [
|
||||
"install.rdf",
|
||||
"dictionaries/*.aff",
|
||||
"dictionaries/*.dic"]
|
||||
whitelisted_files = [
|
||||
"install.js",
|
||||
"icon.png",
|
||||
"icon64.png",
|
||||
"dictionaries/*.aff", # List again because there must >0
|
||||
"dictionaries/*.dic",
|
||||
"chrome.manifest",
|
||||
"chrome/*"]
|
||||
whitelisted_extensions = ("txt",)
|
||||
|
||||
test_layout(err, xpi_package, mandatory_files,
|
||||
whitelisted_files, whitelisted_extensions,
|
||||
"dictionary")
|
||||
|
||||
|
||||
@decorator.register_test(tier=1, expected_type=4)
|
||||
def test_langpack_layout(err, xpi_package=None):
|
||||
"""Ensures that language packs only contain exactly what they
|
||||
need and nothing more. Otherwise, somebody could sneak something
|
||||
sneaking into them."""
|
||||
|
||||
# Define rules for the structure.
|
||||
mandatory_files = [
|
||||
"install.rdf",
|
||||
"chrome.manifest"]
|
||||
whitelisted_files = ["chrome/*.jar"]
|
||||
whitelisted_extensions = ("manifest", "rdf", "jar", "dtd",
|
||||
"properties", "xhtml", "css")
|
||||
|
||||
test_layout(err, xpi_package, mandatory_files,
|
||||
whitelisted_files, whitelisted_extensions,
|
||||
"language pack")
|
||||
|
||||
|
||||
@decorator.register_test(tier=1, expected_type=2)
|
||||
def test_theme_layout(err, xpi_package=None):
|
||||
"""Ensures that themes only contain exactly what they need and
|
||||
nothing more. Otherwise, somebody could sneak something sneaking
|
||||
into them."""
|
||||
|
||||
# Define rules for the structure.
|
||||
mandatory_files = [
|
||||
"install.rdf",
|
||||
"chrome.manifest"]
|
||||
whitelisted_files = ["chrome/*.jar"]
|
||||
|
||||
test_layout(err, xpi_package, mandatory_files,
|
||||
whitelisted_files, None, "theme")
|
||||
|
||||
|
||||
def test_layout(err, xpi, mandatory, whitelisted,
|
||||
white_extensions=None, pack_type="Unknown Addon"):
|
||||
|
|
|
@ -1,860 +0,0 @@
|
|||
import re
|
||||
|
||||
from validator.constants import BUGZILLA_BUG
|
||||
from validator.compat import (FX4_DEFINITION, FX5_DEFINITION, FX6_DEFINITION,
|
||||
FX7_DEFINITION, FX8_DEFINITION, FX9_DEFINITION,
|
||||
FX11_DEFINITION, FX12_DEFINITION, FX13_DEFINITION,
|
||||
FX14_DEFINITION,
|
||||
TB7_DEFINITION, TB10_DEFINITION, TB11_DEFINITION,
|
||||
TB12_DEFINITION, TB13_DEFINITION, TB14_DEFINITION)
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
|
||||
|
||||
registered_regex_tests = []
|
||||
|
||||
|
||||
NP_WARNING = "Network preferences may not be modified."
|
||||
EUP_WARNING = "Extension update settings may not be modified."
|
||||
NSINHS_LINK = ("https://developer.mozilla.org/en/XPCOM_Interface_Reference"
|
||||
"/nsINavHistoryService")
|
||||
TB7_LINK = "https://developer.mozilla.org/en/Thunderbird_7_for_developers"
|
||||
|
||||
|
||||
def run_regex_tests(document, err, filename, context=None, is_js=False):
|
||||
"""Run all of the regex-based JS tests."""
|
||||
|
||||
if context is None:
|
||||
context = ContextGenerator(document)
|
||||
|
||||
# Run all of the registered tests.
|
||||
for cls in registered_regex_tests:
|
||||
if not cls.applicable(err, filename, document):
|
||||
continue
|
||||
t = cls(err, document, filename, context)
|
||||
# Run standard tests.
|
||||
for test in t.tests():
|
||||
test()
|
||||
# Run tests that would otherwise be guarded by `is_js`.
|
||||
if is_js:
|
||||
for test in t.js_tests():
|
||||
test()
|
||||
|
||||
|
||||
def register_generator(cls):
|
||||
registered_regex_tests.append(cls)
|
||||
return cls
|
||||
|
||||
|
||||
class RegexTestGenerator(object):
|
||||
"""
|
||||
This stubs out a test generator object. By decorating inheritors of this
|
||||
class with `@register_generator`, the regex tests will be run on any files
|
||||
that are passed to `run_regex_tests()`.
|
||||
"""
|
||||
|
||||
def __init__(self, err, document, filename, context):
|
||||
self.err = err
|
||||
self.document = document
|
||||
self.filename = filename
|
||||
self.context = context
|
||||
|
||||
self.app_versions_fallback = None
|
||||
|
||||
@classmethod
|
||||
def applicable(cls, err, filename, document):
|
||||
"""Return whether the tests apply to the current file."""
|
||||
return True # Default to always applicable.
|
||||
|
||||
def tests(self):
|
||||
"""Override this with a generator that produces the regex tests."""
|
||||
return []
|
||||
|
||||
def js_tests(self):
|
||||
"""
|
||||
Override this with a generator that produces regex tests that are
|
||||
exclusive to JavaScript.
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_test(self, pattern, title, message, log_function=None,
|
||||
compat_type=None, app_versions=None, flags=0):
|
||||
"""
|
||||
Return a function that, when called, will log a compatibility warning
|
||||
or error.
|
||||
"""
|
||||
app_versions = app_versions or self.app_versions_fallback
|
||||
log_function = log_function or self.err.warning
|
||||
def wrapper():
|
||||
matched = False
|
||||
for match in re.finditer(pattern, self.document, flags):
|
||||
log_function(
|
||||
("testcases_regex", "generic", "_generated"),
|
||||
title,
|
||||
message,
|
||||
filename=self.filename,
|
||||
line=self.context.get_line(match.start()),
|
||||
context=self.context,
|
||||
compatibility_type=compat_type,
|
||||
for_appversions=app_versions,
|
||||
tier=self.err.tier if app_versions is None else 5)
|
||||
matched = True
|
||||
return matched
|
||||
|
||||
return wrapper
|
||||
|
||||
def get_test_bug(self, bug, pattern, title, message, **kwargs):
|
||||
"""Helper function to mix in a bug number."""
|
||||
message = [message,
|
||||
"See bug %s for more information." % BUGZILLA_BUG % bug]
|
||||
return self.get_test(pattern, title, message, **kwargs)
|
||||
|
||||
|
||||
@register_generator
|
||||
class GenericRegexTests(RegexTestGenerator):
|
||||
"""Test for generic, banned patterns in a document being scanned."""
|
||||
|
||||
def tests(self):
|
||||
# globalStorage.(.+)password test removed for bug 752740
|
||||
|
||||
yield self.get_test(
|
||||
r"launch\(\)",
|
||||
"`launch()` disallowed",
|
||||
"Use of `launch()` is disallowed because of restrictions on "
|
||||
"`nsIFile` and `nsILocalFile`. If the code does not use "
|
||||
"those namespaces, consider using a different function name.")
|
||||
|
||||
yield self.get_test(
|
||||
r"resource://services-sync",
|
||||
"Sync services objects are not intended to be re-used",
|
||||
"The Sync services objects are not intended to be re-used, and "
|
||||
"they often change in ways that break add-ons. It is strongly "
|
||||
"recommended that you do not rely on them.")
|
||||
|
||||
|
||||
@register_generator
|
||||
class CategoryRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
These tests will flag JavaScript category registration. Category
|
||||
registration is not permitted in add-ons.
|
||||
|
||||
Added from bug 635423
|
||||
"""
|
||||
|
||||
# This generates the regular expressions for all combinations of JS
|
||||
# categories. Note that all of them begin with "JavaScript". Capitalization
|
||||
# matters.
|
||||
PATTERNS = map(lambda r: '''"%s"|'%s'|%s''' % (r, r, r.replace(" ", "-")),
|
||||
["JavaScript %s" % pattern for pattern in
|
||||
("global constructor",
|
||||
"global constructor prototype alias",
|
||||
"global property",
|
||||
"global privileged property",
|
||||
"global static nameset",
|
||||
"global dynamic nameset",
|
||||
"DOM class",
|
||||
"DOM interface", )])
|
||||
|
||||
def js_tests(self):
|
||||
for pattern in self.PATTERNS:
|
||||
yield self.get_test(
|
||||
pattern,
|
||||
"Potential JavaScript category registration",
|
||||
"Add-ons should not register JavaScript categories. It "
|
||||
"appears that a JavaScript category was registered via a "
|
||||
"script to attach properties to JavaScript globals. This "
|
||||
"is not allowed.")
|
||||
|
||||
|
||||
@register_generator
|
||||
class DOMMutationRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
These regex tests will test that DOM mutation events are not used. These
|
||||
events have extreme performance penalties associated with them and are
|
||||
currently deprecated.
|
||||
|
||||
Added from bug 642153
|
||||
"""
|
||||
|
||||
EVENTS = ("DOMAttrModified", "DOMAttributeNameChanged",
|
||||
"DOMCharacterDataModified", "DOMElementNameChanged",
|
||||
"DOMNodeInserted", "DOMNodeInsertedIntoDocument",
|
||||
"DOMNodeRemoved", "DOMNodeRemovedFromDocument",
|
||||
"DOMSubtreeModified", )
|
||||
|
||||
def js_tests(self):
|
||||
for event in self.EVENTS:
|
||||
yield self.get_test(
|
||||
event,
|
||||
"DOM mutation event use prohibited",
|
||||
"DOM mutation events are flagged because of their "
|
||||
"deprecated status as well as their extreme inefficiency. "
|
||||
"Consider using a different event.")
|
||||
|
||||
|
||||
@register_generator
|
||||
class PasswordsInPrefsRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
These regex tests will ensure that the developer is not storing passwords
|
||||
in the `/defaults/preferences` JS files.
|
||||
|
||||
Added from bug 647109
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def applicable(cls, err, filename, document):
|
||||
return bool(re.match(r"defaults/preferences/.+\.js", filename))
|
||||
|
||||
def js_tests(self):
|
||||
yield self.get_test(
|
||||
"password",
|
||||
"Passwords may be stored in `defaults/preferences/*.js`",
|
||||
"Storing passwords in the preferences JavaScript files is "
|
||||
"insecure. The Login Manager should be used insted.")
|
||||
|
||||
|
||||
@register_generator
|
||||
class BannedPrefRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
These regex tests will find whether banned preference branches are being
|
||||
referenced from outside preference JS files.
|
||||
|
||||
Added from bug 676815
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def applicable(cls, err, filename, document):
|
||||
return not filename.startswith("defaults/preferences/")
|
||||
|
||||
def tests(self):
|
||||
from javascript.predefinedentities import (BANNED_PREF_BRANCHES,
|
||||
BANNED_PREF_REGEXPS)
|
||||
for pattern in BANNED_PREF_REGEXPS:
|
||||
yield self.get_test(
|
||||
r"[\"']" + pattern,
|
||||
"Potentially unsafe preference branch referenced",
|
||||
"Extensions should not alter preferences matching /%s/."
|
||||
% pattern)
|
||||
|
||||
for branch in BANNED_PREF_BRANCHES:
|
||||
yield self.get_test(
|
||||
branch.replace(r".", r"\."),
|
||||
"Potentially unsafe preference branch referenced",
|
||||
"Extensions should not alter preferences in the `%s` "
|
||||
"preference branch" % branch)
|
||||
|
||||
|
||||
@register_generator
|
||||
class ChromePatternRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
Test that an Add-on SDK (Jetpack) add-on doesn't use interfaces that are
|
||||
not part of the SDK.
|
||||
|
||||
Added from bugs 689340, 731109
|
||||
"""
|
||||
|
||||
def tests(self):
|
||||
# We want to re-wrap the test because if it detects something, we're
|
||||
# going to set the `requires_chrome` metadata value to `True`.
|
||||
def rewrap():
|
||||
wrapper = self.get_test(
|
||||
r"(?<![\'\"])require\s*\(\s*[\'\"]"
|
||||
r"(chrome|window-utils|observer-service)[\'\"]\s*\)",
|
||||
"Usage of non-SDK interface",
|
||||
"This SDK-based add-on uses interfaces that aren't part "
|
||||
"of the SDK.")
|
||||
if wrapper():
|
||||
self.err.metadata["requires_chrome"] = True
|
||||
|
||||
yield rewrap
|
||||
|
||||
|
||||
@register_generator
|
||||
class JSPrototypeExtRegexTests(RegexTestGenerator):
|
||||
"""
|
||||
These regex tests will ensure that the developer is not modifying the
|
||||
prototypes of the global JS types.
|
||||
|
||||
Added from bug 696172
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def applicable(cls, err, filename, document):
|
||||
return not (filename.endswith(".jsm") or "EXPORTED_SYMBOLS" in document)
|
||||
|
||||
def js_tests(self):
|
||||
yield self.get_test(
|
||||
r"(String|Object|Number|Date|RegExp|Function|Boolean|Array|"
|
||||
r"Iterator)\.prototype(\.[a-zA-Z0-9]+|\[.+\]) =",
|
||||
"JS prototype extension",
|
||||
"It appears that an extension of a built-in JS type was made. "
|
||||
"This is not allowed for security and compatibility reasons.",
|
||||
flags=re.I)
|
||||
|
||||
|
||||
class CompatRegexTestHelper(RegexTestGenerator):
|
||||
"""
|
||||
A helper that makes it easier to stay DRY. This will automatically check
|
||||
for applicability against the value set as the app_versions_fallback.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CompatRegexTestHelper, self).__init__(*args, **kwargs)
|
||||
self.app_versions_fallback = self.VERSION
|
||||
|
||||
@classmethod
|
||||
def applicable(cls, err, filename, document):
|
||||
return err.supports_version(cls.VERSION)
|
||||
|
||||
|
||||
DEP_INTERFACE = "Deprecated interface in use"
|
||||
DEP_INTERFACE_DESC = ("This add-on uses `%s`, which is deprecated in Gecko %d "
|
||||
"and should not be used.")
|
||||
|
||||
@register_generator
|
||||
class Gecko5RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 5 updates."""
|
||||
|
||||
VERSION = FX5_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test(
|
||||
r"navigator\.language",
|
||||
"`navigator.language` may not behave as expected.",
|
||||
"JavaScript code was found that references `navigator."
|
||||
"language`, which will no longer indicate that language of "
|
||||
"the application's UI. To maintain existing functionality, "
|
||||
"`general.useragent.locale` should be used in place of "
|
||||
"`navigator.language`.", compat_type="error",
|
||||
log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko6RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 6 updates."""
|
||||
|
||||
VERSION = FX6_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
interfaces = {"nsIDOMDocumentTraversal": 655514,
|
||||
"nsIDOMDocumentRange": 655513,
|
||||
"IWeaveCrypto": 651596}
|
||||
for interface, bug in interfaces.items():
|
||||
yield self.get_test_bug(
|
||||
bug=bug,
|
||||
pattern=interface,
|
||||
title=DEP_INTERFACE,
|
||||
message=DEP_INTERFACE_DESC % (interface, 6),
|
||||
compat_type="error")
|
||||
|
||||
yield self.get_test_bug(
|
||||
614181, r"app\.update\.timer",
|
||||
"`app.update.timer` is incompatible with Gecko 6",
|
||||
"The `app.update.timer` preference is being replaced by the "
|
||||
"`app.update.timerMinimumDelay` preference in Gecko 6.",
|
||||
compat_type="error")
|
||||
|
||||
def js_tests(self):
|
||||
yield self.get_test_bug(
|
||||
656433, r"['\"](javascript|data):",
|
||||
"`javascript:`/`data:` URIs may be incompatible with Gecko 6",
|
||||
"Loading `javascript:` and `data:` URIs through the location "
|
||||
"bar may no longer work as expected in Gecko 6. If you load "
|
||||
"these types of URIs, please test your add-on using the "
|
||||
"latest builds of the applications that it targets.",
|
||||
compat_type="warning", log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko7RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 7 updates."""
|
||||
|
||||
VERSION = FX7_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
interfaces = {"nsIDOMDocumentStyle": 658904,
|
||||
"nsIDOMNSDocument": 658906,
|
||||
"nsIDOM3TypeInfo": 660539,
|
||||
"nsIDOM3Node": 659053}
|
||||
for interface, bug in interfaces.items():
|
||||
yield self.get_test_bug(
|
||||
bug=bug,
|
||||
pattern=interface,
|
||||
title=DEP_INTERFACE,
|
||||
message=DEP_INTERFACE_DESC % (interface, 7),
|
||||
compat_type="error")
|
||||
|
||||
yield self.get_test_bug(
|
||||
633266, "nsINavHistoryObserver",
|
||||
"`nsINavHistoryObserver` interface has changed in Gecko 7",
|
||||
"The `nsINavHistoryObserver` interface has change in Gecko 7. "
|
||||
"Most function calls now require a GUID parameter. Please "
|
||||
"refer to %s for more information." % NSINHS_LINK,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
yield self.get_test_bug(
|
||||
617539, "nsIMarkupDocumentViewer_MOZILLA_2_0_BRANCH",
|
||||
"`_MOZILLA_2_0_BRANCH` has been merged in Gecko 7",
|
||||
"The `_MOZILLA_2_0_BRANCH` interfaces have been merged out. "
|
||||
"You should now use the namespace without the "
|
||||
"`_MOZILLA_2_0_BRANCH` suffix.", compat_type="warning")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko8RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 8 updates."""
|
||||
|
||||
VERSION = FX8_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
interfaces = {"nsISelection2": 672536,
|
||||
"nsISelection3": 672536}
|
||||
for interface, bug in interfaces.items():
|
||||
yield self.get_test_bug(
|
||||
bug=bug,
|
||||
pattern=interface,
|
||||
title=DEP_INTERFACE,
|
||||
message=DEP_INTERFACE_DESC % (interface, 8),
|
||||
compat_type="error")
|
||||
|
||||
NSIDWI_MDN = ("https://developer.mozilla.org/en/"
|
||||
"XPCOM_Interface_Reference/nsIDOMWindow")
|
||||
yield self.get_test(
|
||||
"nsIDOMWindowInternal", DEP_INTERFACE,
|
||||
"The `nsIDOMWindowInternal` interface has been deprecated in "
|
||||
"Gecko 8. You can use the `nsIDOMWindow` interface instead. "
|
||||
"See %s for more information." % NSIDWI_MDN,
|
||||
compat_type="warning")
|
||||
|
||||
ISO8601_MDC = ("https://developer.mozilla.org/en/JavaScript/Reference/"
|
||||
"Global_Objects/Date")
|
||||
yield self.get_test(
|
||||
"ISO8601DateUtils",
|
||||
"`ISO8601DateUtils.jsm` was removed in Gecko 8",
|
||||
"The `ISO8601DateUtils object is no longer available in Gecko "
|
||||
"8. You can use the normal `Date` object instead. See %s"
|
||||
"for more information." % ISO8601_MDC,
|
||||
compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko9RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 8 updates."""
|
||||
|
||||
VERSION = FX9_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
679971, r"navigator\.taintEnabled",
|
||||
"`navigator.taintEnabled` was removed in Gecko 9",
|
||||
"The `taintEnabled` function is no longer available in Gecko "
|
||||
"9. Since this function was only used for browser detection "
|
||||
"and this doesn't belong in extension code, it should be "
|
||||
"removed if possible.", compat_type="warning")
|
||||
|
||||
chrome_context_props = [r"\.nodePrincipal", r"\.documentURIObject",
|
||||
r"\.baseURIObject", ]
|
||||
for property in chrome_context_props:
|
||||
yield self.get_test_bug(
|
||||
660233, property,
|
||||
"`%s` only available in chrome contexts" % property,
|
||||
"The `%s` property is no longer accessible from untrusted "
|
||||
"scripts as of Gecko 9." % property,
|
||||
compat_type="warning", log_function=self.err.notice)
|
||||
|
||||
yield self.get_test_bug(
|
||||
568971, r"nsIGlobalHistory3", DEP_INTERFACE,
|
||||
DEP_INTERFACE_DESC % ("nsIGlobalHistory3", 9),
|
||||
compat_type="warning")
|
||||
|
||||
# geo.wifi.* warnings
|
||||
for pattern in ["geo.wifi.uri", r"geo.wifi.protocol", ]:
|
||||
yield self.get_test_bug(
|
||||
689252, pattern.replace(".", r"\."),
|
||||
"`%s` removed in Gecko 9" % pattern,
|
||||
"The `geo.wifi.*` preferences are no longer created by "
|
||||
"default in Gecko 9. Reading them without testing for "
|
||||
"their presence can result in unexpected errors.",
|
||||
compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko11RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 11 updates."""
|
||||
|
||||
VERSION = FX11_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
700490, "nsICharsetResolver", DEP_INTERFACE,
|
||||
DEP_INTERFACE_DESC % ("nsICharsetResolver", 11),
|
||||
compat_type="error")
|
||||
|
||||
yield self.get_test_bug(
|
||||
701875, r"omni\.jar",
|
||||
"`omni.jar` renamed to `omni.ja` in Gecko 11",
|
||||
"This add-on references `omni.jar`, which was renamed to "
|
||||
"`omni.ja`. You should avoid referencing this file directly, "
|
||||
"and at least update this reference for any versions that "
|
||||
"support Gecko 11 and above.", compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko12RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 12 updates."""
|
||||
|
||||
VERSION = FX12_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
675221, "nsIProxyObjectManager", DEP_INTERFACE,
|
||||
"This add-on uses nsIProxyObjectManager, which was removed in "
|
||||
"Gecko 12.", compat_type="error")
|
||||
yield self.get_test_bug(
|
||||
713825, "documentCharsetInfo",
|
||||
"Deprecated JavaScript property",
|
||||
"documentCharsetInfo has been deprecated in Gecko 12 and "
|
||||
"should no longer be used.", compat_type="error")
|
||||
yield self.get_test_bug(
|
||||
711838, "nsIJetpack(Service)?", DEP_INTERFACE,
|
||||
"This add-on uses the Jetpack service, which was deprecated "
|
||||
"long ago and is no longer included in Gecko 12. Please "
|
||||
"update your add-on to use a more recent version of the "
|
||||
"Add-ons SDK.", compat_type="error")
|
||||
|
||||
# `chromemargin`; bug 735876
|
||||
yield self.get_test_bug(
|
||||
735876, "chromemargin",
|
||||
"`chromemargin` attribute changed in Gecko 12",
|
||||
"This add-on uses the chromemargin attribute, which after "
|
||||
"Gecko 12 will not work in the same way with values other "
|
||||
"than 0 or -1.", compat_type="error",
|
||||
log_function=self.err.notice)
|
||||
|
||||
@register_generator
|
||||
class Gecko13RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 13 updates."""
|
||||
|
||||
VERSION = FX13_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
613588, "nsILivemarkService", DEP_INTERFACE,
|
||||
"This add-on uses nsILivemarkService, which has been "
|
||||
"deprecated in Gecko 13. Some of its functions may not work "
|
||||
"as expected. mozIAsyncLivemarks should be used instead.",
|
||||
compat_type="error")
|
||||
yield self.get_test_bug(
|
||||
718255, "nsIPrefBranch2", DEP_INTERFACE,
|
||||
"This add-on uses nsIPrefBranch2, which has been merged into "
|
||||
"nsIPrefBranch in Gecko 13. Once you drop support for old "
|
||||
"versions of Gecko, you should stop using nsIPrefBranch2. You "
|
||||
"can use the == operator as an alternative.",
|
||||
compat_type="warning")
|
||||
yield self.get_test_bug(
|
||||
650784, "nsIScriptableUnescapeHTML", DEP_INTERFACE,
|
||||
"This add-on uses nsIScriptableUnescapeHTML, which has been "
|
||||
"deprecated in Gecko 13 in favor of the nsIParserUtils "
|
||||
"interface. While it will continue to work for the foreseeable "
|
||||
"future, it is recommended that you change your code to use "
|
||||
"nsIParserUtils as soon as possible.",
|
||||
compat_type="warning", log_function=self.err.notice)
|
||||
yield self.get_test_bug(
|
||||
672507, "nsIAccessNode", DEP_INTERFACE,
|
||||
"The `nsIAccessNode` interface has been merged into "
|
||||
"`nsIAccessible`. You should use that interface instead.",
|
||||
compat_type="error")
|
||||
|
||||
GLOBALSTORAGE_URL = ("https://developer.mozilla.org/en/XUL_School/"
|
||||
"Local_Storage")
|
||||
yield self.get_test_bug(
|
||||
687579, "globalStorage",
|
||||
"`globalStorage` removed in Gecko 13",
|
||||
"As of Gecko 13, the `globalStorage` object has been removed. "
|
||||
"See %s for alternatives." % GLOBALSTORAGE_URL,
|
||||
compat_type="error")
|
||||
|
||||
yield self.get_test_bug(
|
||||
702639, "excludeItemsIfParentHasAnnotation",
|
||||
"`excludeItemsIfParentHasAnnotation` no longer supported",
|
||||
"The `excludeItemsIfParentHasAnnotation` query option is no "
|
||||
"longer supported, as of Gecko 13.", compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Gecko14RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for Gecko 14 updates."""
|
||||
|
||||
VERSION = FX14_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
BLOB_BUILDER_DEP = "`BlobBuilder` and `MozBlobBuilder` are deprecated."
|
||||
|
||||
yield self.get_test_bug(
|
||||
736687, "(Moz)?BlobBuilder", BLOB_BUILDER_DEP,
|
||||
"The `BlobBuilder` and `MozBlobBuilder` objects are now "
|
||||
"deprecated. You should use the `Blob` constructor instead.",
|
||||
compat_type="warning")
|
||||
|
||||
yield self.get_test_bug(
|
||||
682360, "nsILocalFile",
|
||||
"`nsILocalFile` should be replaced with `nsIFile`.",
|
||||
"Starting with Gecko 14, `nsILocalFile` inherits all functions "
|
||||
"and attributes from `nsIFile`, meaning that you no longer "
|
||||
"need to use `nsILocalFile`. If your add-on doesn't support "
|
||||
"versions older than 14, you should use `nsIFile` instead of "
|
||||
"`nsILocalFile`.", compat_type="warning")
|
||||
|
||||
yield self.get_test_bug(
|
||||
737133, "onFaviconDataAvailable",
|
||||
"`onFaviconDataAvailable` renamed to `onComplete`.",
|
||||
"The `onFaviconDataAvailable` function has been renamed to "
|
||||
"`onComplete`. Also note that the function behaves slightly "
|
||||
"differently now.", compat_type="error")
|
||||
|
||||
GUID_LINK = ("http://blog.bonardo.net/2012/02/16/"
|
||||
"add-ons-devs-heads-up-we-are-killing-old-bookmarks-guids")
|
||||
yield self.get_test(
|
||||
"(getItemGUID|setItemGUID|getItemIdForGUID)",
|
||||
"`getItemGUID`, `setItemGUID`, and `getItemIdForGUID` were "
|
||||
"removed.",
|
||||
"Item GUIDs have been dropped from the Bookmarks Service. See "
|
||||
"%s for more information." % GUID_LINK, compat_type="error")
|
||||
|
||||
yield self.get_test_bug(
|
||||
737841, "redirectsMode",
|
||||
"`redirectsMode` removed from `nsINavHistoryQueryOptions`",
|
||||
"The `redirectsMode` option has been removed from the "
|
||||
"`nsINavHistoryQueryOptions` interface. Error visits are no "
|
||||
"longer stored in the history, so it is no longer necessary.",
|
||||
compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird7RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 7 update."""
|
||||
|
||||
VERSION = TB7_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
621213, r"resource:///modules/dictUtils.js",
|
||||
"`dictUtils.js` was removed in Thunderbird 7",
|
||||
"The `dictUtils.js` file is no longer available in "
|
||||
"Thunderbird as of version 7. You can use `Dict.jsm` "
|
||||
"instead.", compat_type="error")
|
||||
ab_patterns = [r"rdf:addressdirectory",
|
||||
r"GetResource\(.*?\)\s*\.\s*"
|
||||
r"QueryInterface\(.*?nsIAbDirectory\)",]
|
||||
for pattern in ab_patterns:
|
||||
yield self.get_test_bug(
|
||||
621213, pattern,
|
||||
"The address book does not use RDF in Thunderbird 7",
|
||||
"The address book was changed to use a look up table in "
|
||||
"Thunderbird 7. See %s for details." % TB7_LINK,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird10RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 10 update."""
|
||||
|
||||
VERSION = TB10_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
700220, r"gDownloadManagerStrings",
|
||||
"`gDownloadManagerStrings` removed in Thunderbird 10",
|
||||
"The `gDownloadManagerStrings` global is no longer available "
|
||||
"in Thunderbird 10.", compat_type="error")
|
||||
yield self.get_test_bug(
|
||||
539997, r"nsTryToClose.js",
|
||||
"`nsTryToClose.js` removed in Thunderbird 10",
|
||||
"The `nsTryToClose.js` file is no longer available as of "
|
||||
"Thunderbird 10.", compat_type="error")
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird11RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 11 update."""
|
||||
|
||||
VERSION = TB11_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
39121, r"specialFoldersDeletionAllowed",
|
||||
"`specialFoldersDeletionAllowed` removed in Thunderbird 11",
|
||||
"The `specialFoldersDeletionAllowed` global was removed in "
|
||||
"Thunderbird 11.", compat_type="error",
|
||||
log_function=self.err.notice)
|
||||
|
||||
patterns = {r"newToolbarCmd\.(label|tooltip)": 694027,
|
||||
r"openToolbarCmd\.(label|tooltip)": 694027,
|
||||
r"saveToolbarCmd\.(label|tooltip)": 694027,
|
||||
r"publishToolbarCmd\.(label|tooltip)": 694027,
|
||||
r"messengerWindow\.title": 701671,
|
||||
r"folderContextSearchMessages\.(label|accesskey)": 652555,
|
||||
r"importFromSeamonkey2\.(label|accesskey)": 689437,
|
||||
r"comm4xMailImportMsgs\.properties": 689437,
|
||||
r"specialFolderDeletionErr": 39121,
|
||||
r"sourceNameSeamonkey": 689437,
|
||||
r"sourceNameOExpress": 689437,
|
||||
r"sourceNameOutlook": 689437,
|
||||
r"failedDuplicateAccount": 709020,}
|
||||
for pattern, bug in patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"Some code matched the pattern `%s`, which has been "
|
||||
"flagged as having changed in Thunderbird 11." % pattern,
|
||||
compat_type="error")
|
||||
|
||||
js_patterns = {r"onViewToolbarCommand": 644169,
|
||||
r"nsContextMenu": 680192,
|
||||
r"MailMigrator\.migrateMail": 712395,
|
||||
r"AddUrlAttachment": 708982,
|
||||
r"makeFeedObject": 705504,
|
||||
r"deleteFeed": 705504, }
|
||||
for pattern, bug in js_patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"Some code matched the JavaScript function `%s`, which has "
|
||||
"been flagged as having changed, removed, or deprecated "
|
||||
"in Thunderbird 11." % pattern,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird12RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 12 update."""
|
||||
|
||||
VERSION = TB12_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
yield self.get_test_bug(
|
||||
717240, r"EdImage(Map|MapHotSpot|MapShapes|Overlay)\.js",
|
||||
"`EdImage*.js` removed in Thunderbird 12",
|
||||
"`EdImageMap.js`, `EdImageMapHotSpot.js`, "
|
||||
"`EdImageMapShapes.js`, and `EdImageMapOverlay.js` were "
|
||||
"removed in Thunderbird 12.", compat_type="error",
|
||||
log_function=self.err.notice)
|
||||
|
||||
patterns = {"editImageMapButton\.(label|tooltip)": 717240,
|
||||
"haveSmtp[1-3]\.suffix2": 346306,
|
||||
"EditorImage(Map|MapHotSpot)\.dtd": 717240,
|
||||
"subscribe-errorInvalidOPMLFile": 307629, }
|
||||
for pattern, bug in patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"Some code matched the pattern `%s`, which has been "
|
||||
"flagged as having changed in Thunderbird 12." % pattern,
|
||||
compat_type="error")
|
||||
|
||||
js_patterns = {r"TextEditorOnLoad": 695842,
|
||||
r"Editor(OnLoad|Startup|Shutdown|CanClose)": 695842,
|
||||
r"gInsertNewIMap": 717240,
|
||||
r"editImageMap": 717240,
|
||||
r"(SelectAll|MessageHas)Attachments": 526998, }
|
||||
for pattern, bug in js_patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"Some code matched the JavaScript function `%s`, which has "
|
||||
"been flagged as having changed, removed, or deprecated "
|
||||
"in Thunderbird 12." % pattern,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird13RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 13 update."""
|
||||
|
||||
VERSION = TB13_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
# String changes for add-ons depending on Thunderbird localizations
|
||||
patterns = {"searchAllMessages\.(label|keyLabel2)": 728897,
|
||||
"username(Desc|Label|SmtpDesc|SmtpLabel)\.(label|accesskey)": 71008,
|
||||
"loginTitle\.label": 71008,
|
||||
"smtpServer(Desc|Label)\.(label|accesskey)": 71008,
|
||||
"incomingServer(NameDesc|Label)\.(label|accesskey)": 71008,
|
||||
"serverTitle\.label": 71008,
|
||||
"location\.label": 716706,
|
||||
"feed-properties\.dtd": 716706,}
|
||||
for pattern, bug in patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed labels in use",
|
||||
"Some string matched the pattern `%s`, which has been "
|
||||
"flagged as having changed in Thunderbird 13." % pattern,
|
||||
compat_type="error")
|
||||
|
||||
js_patterns = {r"webSearchProvider\.js": 733802,
|
||||
r"general\.useragent\.extra\.thunderbird": 726942,
|
||||
r"gPermissionManager\._updatePermissions": 728810,
|
||||
r"(gPromptService|gHeaderParser)": 722187,
|
||||
r"setup(Cc|Bcc)Textbox": 208628,
|
||||
r"onBiffMinChange": 532391,
|
||||
r"(server|login)Page(Validate|Unload|Init)": 71008,
|
||||
r"feed-properties\.js": 716706,
|
||||
r"gFeedSubscriptionsWindow\.(init|uninit)": 716706,
|
||||
r"openComposeWindowForRSSArticle": 716706,
|
||||
r"nsOfflineStartupModule\.getBundle": 722168,
|
||||
r"GetOnlineDelimiter": 301714, }
|
||||
for pattern, bug in js_patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"A javascript function matched the pattern `%s`, which has "
|
||||
"been flagged as having changed, removed, or deprecated "
|
||||
"in Thunderbird 13." % pattern,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
|
||||
|
||||
@register_generator
|
||||
class Thunderbird14RegexTests(CompatRegexTestHelper):
|
||||
"""Regex tests for the Thunderbird 14 update."""
|
||||
|
||||
VERSION = TB14_DEFINITION
|
||||
|
||||
def tests(self):
|
||||
# String changes for add-ons depending on Thunderbird localizations
|
||||
patterns = {r"spellCheck(IgnoreWord|NoSuggestions|AddToDictionary)\."
|
||||
"(label|accesskey)": 735986,
|
||||
r"account(Title|SettingsDesc)\.label": 340324,
|
||||
r"messageStorage\.label": 340324,
|
||||
r"emptyTrashOnExit\.(label|accesskey)": 340324,
|
||||
r"local(Path|FolderPicker)\.label": 340324,
|
||||
r"browseFolder\.(label|accesskey)": 340324,
|
||||
r"replyNewsgroupCmd\.(label|accesskey)": 718342,
|
||||
r"contextReplyNewsgroup\.(label|accesskey)": 718342,
|
||||
r"junkLog(Info|)\.(title|label)": 323159,
|
||||
r"enableJunkLogging\.(label|accesskey)": 323159,}
|
||||
for pattern, bug in patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed labels in use",
|
||||
"Some string matched the pattern `%s`, which has been "
|
||||
"flagged as having changed in Thunderbird 14." % pattern,
|
||||
compat_type="error")
|
||||
|
||||
js_patterns = {r"feed-subscriptions\.js": 737115,
|
||||
r"(\b|\()msgComposeService": 739051,
|
||||
r"(\b|\()IsCanSearchMessagesEnabled": 537378,
|
||||
r"(\b|\()(g|cv)(Prefs|IOService|HeaderParser)": 733496,
|
||||
r"(\b|\()(nsPrefBranch|CollapseSectionSeparators)": 713277,
|
||||
r"(\b|\()g(PrefBranch|MailSession)": 736870,
|
||||
r"(\b|\()gIncomingServer": 340324,
|
||||
r"(\b|\()getPromptService": 732807,
|
||||
r"mailnews\.display\.html_sanitizer\.allowed_tags": 650776,
|
||||
r"downloadheaders\.js": 732811,
|
||||
r"(\b|\()kSmallCommit": 747102,}
|
||||
for pattern, bug in js_patterns.items():
|
||||
yield self.get_test_bug(
|
||||
bug, pattern,
|
||||
"Removed, renamed, or changed methods in use",
|
||||
"A javascript function matched the pattern `%s`, which has "
|
||||
"been flagged as having changed, removed, or deprecated "
|
||||
"in Thunderbird 14." % pattern,
|
||||
compat_type="error", log_function=self.err.notice)
|
||||
|
|
@ -3,12 +3,11 @@ import subprocess
|
|||
import tempfile
|
||||
from cStringIO import StringIO
|
||||
|
||||
import validator.testcases.javascript.traverser as traverser
|
||||
from validator.testcases.javascript.spidermonkey import get_tree, \
|
||||
JSReflectException
|
||||
from validator.constants import PACKAGE_THEME, SPIDERMONKEY_INSTALLATION
|
||||
from validator.contextgenerator import ContextGenerator
|
||||
from validator.textfilter import *
|
||||
import javascript.traverser as traverser
|
||||
from javascript.spidermonkey import get_tree, JSReflectException
|
||||
from appvalidator.constants import PACKAGE_THEME, SPIDERMONKEY_INSTALLATION
|
||||
from ..contextgenerator import ContextGenerator
|
||||
from ..textfilter import *
|
||||
|
||||
|
||||
JS_ESCAPE = re.compile(r"\\+[ux]", re.I)
|
||||
|
@ -21,16 +20,6 @@ def test_js_file(err, filename, data, line=0, context=None, pollutable=False):
|
|||
err.get_resource("SPIDERMONKEY") is None: # Default value is False
|
||||
return
|
||||
|
||||
if err.detected_type == PACKAGE_THEME:
|
||||
err.warning(
|
||||
err_id=("testcases_scripting",
|
||||
"test_js_file",
|
||||
"theme_js"),
|
||||
warning="JS run from theme",
|
||||
description="Themes should not contain executable code.",
|
||||
filename=filename,
|
||||
line=line)
|
||||
|
||||
before_tier = None
|
||||
# Set the tier to 4 (Security Tests)
|
||||
if err is not None:
|
||||
|
@ -51,9 +40,7 @@ def test_js_file(err, filename, data, line=0, context=None, pollutable=False):
|
|||
if context is None:
|
||||
context = ContextGenerator(data)
|
||||
|
||||
t = traverser.Traverser(err, filename, line, context=context,
|
||||
is_jsm=filename.endswith(".jsm") or
|
||||
"EXPORTED_SYMBOLS" in data)
|
||||
t = traverser.Traverser(err, filename, line, context=context)
|
||||
t.pollutable = pollutable
|
||||
t.run(tree)
|
||||
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
import validator.constants
|
||||
from validator import decorator
|
||||
from validator.constants import PACKAGE_DICTIONARY, FF4_MIN, APPLICATIONS
|
||||
|
||||
APP_VERSIONS_URL = "Please check the list of valid versions at: "\
|
||||
"https://addons.mozilla.org/en-US/firefox/pages/appversions/"
|
||||
|
||||
|
||||
@decorator.register_test(tier=1)
|
||||
def test_targetedapplications(err, xpi_package=None):
|
||||
"""Tests to make sure that the targeted applications in the
|
||||
install.rdf file are legit and that any associated files (I'm
|
||||
looking at you, SeaMonkey) are where they need to be."""
|
||||
|
||||
install = err.get_resource("install_rdf")
|
||||
if not install:
|
||||
if err.supported_versions is None:
|
||||
err.supported_versions = {}
|
||||
return
|
||||
|
||||
APPROVED_APPLICATIONS = validator.constants.APPROVED_APPLICATIONS
|
||||
|
||||
# Search through the install.rdf document for the SeaMonkey
|
||||
# GUID string.
|
||||
ta_predicate = install.uri("targetApplication")
|
||||
ta_guid_predicate = install.uri("id")
|
||||
ta_min_ver = install.uri("minVersion")
|
||||
ta_max_ver = install.uri("maxVersion")
|
||||
|
||||
used_targets = []
|
||||
all_supported_versions = {}
|
||||
|
||||
# Isolate all of the bnodes referring to target applications
|
||||
for target_app in install.get_objects(None, ta_predicate):
|
||||
|
||||
# Get the GUID from the target application
|
||||
for ta_guid in install.get_objects(target_app,
|
||||
ta_guid_predicate):
|
||||
|
||||
used_targets.append(ta_guid)
|
||||
|
||||
found_guid = False
|
||||
for (guid, key) in [(x["guid"], y) for (y, x) in
|
||||
APPROVED_APPLICATIONS.items()]:
|
||||
if guid == ta_guid:
|
||||
found_guid = key
|
||||
break
|
||||
|
||||
if found_guid:
|
||||
# Remember if the addon supports Firefox.
|
||||
is_firefox = APPLICATIONS[ta_guid] == "firefox"
|
||||
|
||||
# Grab the minimum and maximum version numbers.
|
||||
if (err.overrides and
|
||||
"targetapp_minVersion" in err.overrides and
|
||||
ta_guid in err.overrides["targetapp_minVersion"]):
|
||||
min_version = \
|
||||
err.overrides["targetapp_minVersion"][ta_guid]
|
||||
else:
|
||||
min_version = install.get_object(target_app, ta_min_ver)
|
||||
|
||||
if (err.overrides and
|
||||
"targetapp_maxVersion" in err.overrides and
|
||||
ta_guid in err.overrides["targetapp_maxVersion"]):
|
||||
max_version = \
|
||||
err.overrides["targetapp_maxVersion"][ta_guid]
|
||||
else:
|
||||
max_version = install.get_object(target_app, ta_max_ver)
|
||||
|
||||
# Get the approved app versions for this application.
|
||||
app_versions = APPROVED_APPLICATIONS[found_guid]["versions"]
|
||||
|
||||
# Ensure that the version numbers are in the app's
|
||||
# list of acceptable version numbers.
|
||||
|
||||
app_name = APPLICATIONS[ta_guid] if \
|
||||
ta_guid in APPLICATIONS else \
|
||||
ta_guid
|
||||
|
||||
try:
|
||||
if min_version is not None:
|
||||
min_ver_pos = app_versions.index(min_version)
|
||||
except ValueError:
|
||||
err.error(("testcases_targetapplication",
|
||||
"test_targetedapplications",
|
||||
"invalid_min_version"),
|
||||
"Invalid minimum version number",
|
||||
["The minimum version that was specified is not "
|
||||
"an acceptable version number for the Mozilla "
|
||||
"product that it corresponds with.",
|
||||
'Version "%s" isn\'t compatible with "%s".' %
|
||||
(min_version, app_name),
|
||||
APP_VERSIONS_URL],
|
||||
"install.rdf")
|
||||
continue
|
||||
|
||||
try:
|
||||
if max_version is not None:
|
||||
max_ver_pos = app_versions.index(max_version)
|
||||
except ValueError:
|
||||
err.error(("testcases_targetapplication",
|
||||
"test_targetedapplications",
|
||||
"invalid_max_version"),
|
||||
"Invalid maximum version number",
|
||||
["The maximum version that was specified is not "
|
||||
"an acceptable version number for the Mozilla "
|
||||
"product that it corresponds with.",
|
||||
'Version "%s" isn\'t compatible with "%s".' %
|
||||
(max_version, app_name),
|
||||
APP_VERSIONS_URL],
|
||||
"install.rdf")
|
||||
continue
|
||||
|
||||
# Now we need to check to see if the version numbers
|
||||
# are in the right order.
|
||||
if min_version is not None and \
|
||||
max_version is not None and \
|
||||
min_ver_pos > max_ver_pos:
|
||||
err.error(("testcases_targetapplication",
|
||||
"test_targetedapplications",
|
||||
"invalid_version_order"),
|
||||
"Invalid min/max versions",
|
||||
["The version numbers provided for the "
|
||||
"application in question are not in the "
|
||||
"correct order. The maximum version must be "
|
||||
"greater than the minimum version.",
|
||||
'"%s" is not less than "%s".' % (min_version,
|
||||
max_version)],
|
||||
"install.rdf")
|
||||
continue
|
||||
|
||||
if min_version is None:
|
||||
err.warning(("testcases_targetapplication",
|
||||
"test_targetedapplications",
|
||||
"missing_minversion"),
|
||||
"Missing minVersion property",
|
||||
"A targetApplication element is missing its "
|
||||
"minVersion property. This may cause it to be "
|
||||
"ignored as invalid.",
|
||||
filename="install.rdf")
|
||||
continue
|
||||
elif max_version is None:
|
||||
err.warning(("testcases_targetapplication",
|
||||
"test_targetedapplications",
|
||||
"missing_maxversion"),
|
||||
"Missing maxVersion property",
|
||||
"A targetApplication element is missing its "
|
||||
"maxVersion property. This may cause it to be "
|
||||
"ignored as invalid.",
|
||||
filename="install.rdf")
|
||||
continue
|
||||
|
||||
all_supported_versions[guid] = \
|
||||
app_versions[min_ver_pos:max_ver_pos + 1]
|
||||
|
||||
# Test whether it's a FF4 addon
|
||||
|
||||
# NOTE: This should probably also be extrapolated for
|
||||
# Thunderbird and the like when they get up to speed. The tests
|
||||
# will likely be the same down the line, so we can keep the
|
||||
# "ff4" resource as a legacy thing and worry about it later.
|
||||
if is_firefox:
|
||||
ff4_pos = app_versions.index(FF4_MIN)
|
||||
if max_ver_pos >= ff4_pos:
|
||||
err.save_resource("ff4", True)
|
||||
|
||||
no_duplicate_targets = set(used_targets)
|
||||
|
||||
if len(used_targets) != len(no_duplicate_targets):
|
||||
err.warning(("testcases_targetapplication",
|
||||
"test_targetedapplication",
|
||||
"duplicate_targetapps"),
|
||||
"Found duplicate <em:targetApplication> elements.",
|
||||
"Multiple targetApplication elements were found in the "
|
||||
"install.manifest file that refer to the same application "
|
||||
"GUID. There should not be duplicate target applications "
|
||||
"entries.",
|
||||
"install.rdf")
|
||||
|
||||
# This finds the UUID of the supported applications and puts it in
|
||||
# a fun and easy-to-use format for use in other tests.
|
||||
supports = []
|
||||
for target in no_duplicate_targets:
|
||||
key = str(target)
|
||||
if key in APPLICATIONS:
|
||||
supports.append(APPLICATIONS[key])
|
||||
err.save_resource("supports", supports)
|
||||
|
||||
if not err.supported_versions: # if not set for compatibility
|
||||
err.supported_versions = all_supported_versions
|
||||
|
||||
if not supports and err.get_resource("listed"):
|
||||
err.error(
|
||||
err_id=("testcases_targetapplication",
|
||||
"test_targetedapplication",
|
||||
"no_mozilla_support"),
|
||||
error="No Mozilla products listed as target applications",
|
||||
description=["None of the target applications listed in "
|
||||
"install.rdf are supported Mozilla products. At "
|
||||
"least one official Mozilla product must be "
|
||||
"supported for inclusion on addons.mozilla.org.",
|
||||
"See "
|
||||
"https://addons.mozilla.org/firefox/pages/appversions/"
|
||||
" for more information on supported target "
|
||||
"applications on AMO."],
|
||||
filename="install.rdf")
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
from validator import decorator
|
||||
from validator.constants import PACKAGE_THEME
|
||||
|
||||
|
||||
@decorator.register_test(tier=2, expected_type=PACKAGE_THEME)
|
||||
def test_theme_manifest(err, xpi_package=None):
|
||||
"""Tests the chrome.manifest files in the package for
|
||||
compliance with the standard theme triples."""
|
||||
|
||||
# Don't even both with the test(s) if there's no chrome.manifest.
|
||||
chrome = err.get_resource("chrome.manifest")
|
||||
if not chrome:
|
||||
return
|
||||
|
||||
for triple in chrome.triples:
|
||||
subject = triple["subject"]
|
||||
# Test to make sure that the triple's subject is valid
|
||||
if subject not in ("skin",
|
||||
"style"):
|
||||
err.warning(("testcases_themes",
|
||||
"test_theme_manifest",
|
||||
"invalid_chrome_manifest_subject"),
|
||||
"Invalid chrome.manifest subject.",
|
||||
["chrome.manifest files for themes are only allowed to "
|
||||
"have 'skin' and 'style' items. Other types of items "
|
||||
"are disallowed for security reasons.",
|
||||
"Invalid subject: %s" % subject],
|
||||
filename=triple["filename"],
|
||||
line=triple["line"],
|
||||
context=triple["context"])
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
def is_ctrl_char(x, y=None):
|
||||
"Returns whether X is an ASCII control character"
|
||||
if y is None:
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
from validator.constants import *
|
||||
|
||||
|
||||
def detect_type(err, install_rdf=None, xpi_package=None):
|
||||
"""Determines the type of add-on being validated based on
|
||||
install.rdf, file extension, and other properties."""
|
||||
|
||||
# The types in the install.rdf don't pair up 1:1 with the type
|
||||
# system that we're using for expectations and the like. This is
|
||||
# to help translate between the two.
|
||||
translated_types = {"2": PACKAGE_EXTENSION,
|
||||
"4": PACKAGE_THEME,
|
||||
"8": PACKAGE_LANGPACK,
|
||||
"32": PACKAGE_MULTI,
|
||||
"64": PACKAGE_DICTIONARY}
|
||||
|
||||
# If we're missing our install.rdf file, we can try to make some
|
||||
# assumptions.
|
||||
if install_rdf is None:
|
||||
types = {"xpi": PACKAGE_DICTIONARY}
|
||||
|
||||
err.notice(("typedetection",
|
||||
"detect_type",
|
||||
"missing_install_rdf"),
|
||||
"install.rdf was not found.",
|
||||
"The type should be determined by install.rdf if present. "
|
||||
"If it isn't, we still need to know the type.")
|
||||
|
||||
# If we know what the file type might be, return it.
|
||||
if xpi_package.extension in types:
|
||||
return types[xpi_package.extension]
|
||||
# Otherwise, we're out of luck :(
|
||||
else:
|
||||
return None
|
||||
|
||||
# Attempt to locate the <em:type> node in the RDF doc.
|
||||
type_uri = install_rdf.uri("type")
|
||||
type_ = install_rdf.get_object(None, type_uri)
|
||||
|
||||
if type_ is not None:
|
||||
if type_ in translated_types:
|
||||
err.save_resource("is_multipackage", type_ == "32", pushable=True)
|
||||
# Make sure we translate back to the normalized version
|
||||
return translated_types[type_]
|
||||
else:
|
||||
err.error(("typedetection",
|
||||
"detect_type",
|
||||
"invalid_em_type"),
|
||||
"Invalid <em:type> value.",
|
||||
"The only valid values for <em:type> are 2, 4, 8, and "
|
||||
"32. Any other values are either invalid or deprecated.",
|
||||
"install.rdf")
|
||||
return
|
||||
|
||||
# Dictionaries are weird too, they might not have the obligatory
|
||||
# em:type. We can assume that if they have a /dictionaries/ folder,
|
||||
# they are a dictionary because even if they aren't, dictionaries
|
||||
# have an extraordinarily strict set of rules and file filters that
|
||||
# must be passed. It's so crazy secure that it's cool if we use it
|
||||
# as kind of a fallback.
|
||||
|
||||
if any(file_ for file_ in xpi_package if
|
||||
file_.startswith("dictionaries/")):
|
||||
return PACKAGE_DICTIONARY
|
||||
|
||||
if type_ is None:
|
||||
err.notice(
|
||||
err_id=("typedetection",
|
||||
"detect_type",
|
||||
"no_em:type"),
|
||||
notice="No <em:type> element found in install.rdf",
|
||||
description="It isn't always required, but it is the most reliable "
|
||||
"method for determining add-on type.",
|
||||
filename="install.rdf")
|
||||
|
||||
# There's no type element, so the spec says that it's either a
|
||||
# theme or an extension. At this point, we know that it isn't
|
||||
# a dictionary, language pack, or multiple extension pack.
|
||||
extensions = {"jar": "4",
|
||||
"xpi": "2"}
|
||||
|
||||
# If the package's extension is listed in the [tiny] extension
|
||||
# dictionary, then just return that. We'll validate against that
|
||||
# add-on type's layout later. Better to false positive than to false
|
||||
# negative.
|
||||
if xpi_package.extension in extensions:
|
||||
# Make sure it gets translated back to the normalized version
|
||||
install_rdf_type = extensions[xpi_package.extension]
|
||||
return translated_types[install_rdf_type]
|
||||
|
|
@ -1,96 +1,12 @@
|
|||
import json
|
||||
import os
|
||||
import types
|
||||
|
||||
from . import constants
|
||||
from .constants import PACKAGE_ANY, PACKAGE_WEBAPP
|
||||
from errorbundler import ErrorBundle
|
||||
import loader
|
||||
import submain
|
||||
import webapp
|
||||
|
||||
|
||||
def validate(path, format="json",
|
||||
approved_applications=os.path.join(os.path.dirname(__file__),
|
||||
"app_versions.json"),
|
||||
determined=True,
|
||||
spidermonkey=False,
|
||||
listed=True,
|
||||
expectation=PACKAGE_ANY,
|
||||
for_appversions=None,
|
||||
overrides=None,
|
||||
timeout=None,
|
||||
market_urls=None,
|
||||
compat_test=False):
|
||||
"""
|
||||
Perform validation in one easy step!
|
||||
|
||||
`path`:
|
||||
*Required*
|
||||
A file system path to the package to be validated.
|
||||
`format`:
|
||||
The format to return the results in. Defaults to "json". Currently, any
|
||||
other format will simply return the error bundle.
|
||||
`approved_applications`:
|
||||
Path to the list of approved application versions
|
||||
`determined`:
|
||||
If set to `False`, validation will halt at the end of the first tier
|
||||
that raises errors.
|
||||
`spidermonkey`:
|
||||
Path to the local spidermonkey installation. Defaults to `False`, which
|
||||
uses the validator's built-in detection of Spidermonkey. Specifying
|
||||
`None` will disable JavaScript tests. Any other value is treated as the
|
||||
path.
|
||||
`listed`:
|
||||
Whether the app is headed for the app marketplace or AMO. Defaults to
|
||||
`True`.
|
||||
`expectation`:
|
||||
The type of package that should be expected. Must be a symbolic constant
|
||||
from validator.constants (i.e.: validator.constants.PACKAGE_*). Defaults
|
||||
to PACKAGE_ANY.
|
||||
`for_appversions`:
|
||||
A dict of app GUIDs referencing lists of versions. Determines which
|
||||
version-dependant tests should be run.
|
||||
`timeout`:
|
||||
Number of seconds before aborting addon validation.
|
||||
`market_urls`:
|
||||
A list of URLs used for validating the `installs_allowed_from` property
|
||||
of webapp manifests.
|
||||
`compat_tests`:
|
||||
A flag to signal the validator to skip tests which should not be run
|
||||
during compatibility bumps. Defaults to `False`.
|
||||
"""
|
||||
|
||||
bundle = ErrorBundle(listed=listed, determined=determined,
|
||||
overrides=overrides, spidermonkey=spidermonkey,
|
||||
for_appversions=for_appversions)
|
||||
bundle.save_resource("is_compat_test", compat_test)
|
||||
|
||||
if isinstance(approved_applications, types.StringTypes):
|
||||
# Load up the target applications if the approved applications is a
|
||||
# path (string).
|
||||
with open(approved_applications) as approved_apps:
|
||||
apps = json.load(approved_apps)
|
||||
elif isinstance(approved_applications, dict):
|
||||
# If the lists of approved applications are already in a dict, just use
|
||||
# that instead of trying to pull from a file.
|
||||
apps = approved_applications
|
||||
else:
|
||||
raise ValueError("Unknown format for `approved_applications`.")
|
||||
|
||||
constants.APPROVED_APPLICATIONS.clear()
|
||||
constants.APPROVED_APPLICATIONS.update(apps)
|
||||
|
||||
# Set the marketplace URLs if they're provided.
|
||||
set_market_urls(market_urls)
|
||||
|
||||
submain.prepare_package(bundle, path, expectation,
|
||||
for_appversions=for_appversions,
|
||||
timeout=timeout)
|
||||
|
||||
return format_result(bundle, format)
|
||||
|
||||
|
||||
def validate_app(data, listed=True, market_urls=None):
|
||||
"""
|
||||
A handy function for validating apps.
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# Derived from nsVersionComparator.cpp in mozilla-central
|
||||
|
||||
from itertools import izip_longest
|
||||
import re
|
||||
|
||||
__all__ = 'Version', 'VersionPart'
|
||||
|
||||
def strcmp(a, b):
|
||||
# Null string comes after any non-null string
|
||||
if not a:
|
||||
return 1 if b else 0
|
||||
elif not b:
|
||||
return -1
|
||||
|
||||
return cmp(a, b)
|
||||
|
||||
class VersionPart(object):
|
||||
numA = 0
|
||||
strB = ''
|
||||
numC = 0
|
||||
extraD = ''
|
||||
|
||||
def __init__(self, part):
|
||||
self._part = part
|
||||
|
||||
if part == '*':
|
||||
self.numA = float('inf')
|
||||
else:
|
||||
self.numA, self.strB = self._splitnum(part)
|
||||
|
||||
if self.strB:
|
||||
if self.strB[0] == '+':
|
||||
self.numA += 1
|
||||
self.strB = "pre"
|
||||
else:
|
||||
match = re.match(r'([^\d+-]*)(.*)', self.strB)
|
||||
|
||||
self.strB = match.group(1)
|
||||
self.numC, self.extraD = self._splitnum(match.group(2))
|
||||
|
||||
def __repr__(self):
|
||||
return 'VersionPart(%s)' % repr(self._part)
|
||||
|
||||
def __str__(self):
|
||||
return self._part
|
||||
|
||||
def __cmp__(self, other):
|
||||
if other is None:
|
||||
return 1
|
||||
|
||||
return (cmp(self.numA, other.numA) or
|
||||
strcmp(self.strB, other.strB) or
|
||||
cmp(self.numC, other.numC) or
|
||||
strcmp(self.extraD, other.extraD))
|
||||
|
||||
def _splitnum(self, string):
|
||||
match = re.match(r'((?:[+-]?\d+)?)(.*)', string)
|
||||
return (int(match.group(1) or 0),
|
||||
match.group(2))
|
||||
|
||||
class Version(object):
|
||||
def __init__(self, version):
|
||||
self._version = version
|
||||
self.parts = map(VersionPart, version.split('.'))
|
||||
|
||||
def __repr__(self):
|
||||
return 'Version(%s)' % repr(self._version)
|
||||
|
||||
def __str__(self):
|
||||
return self._version
|
||||
|
||||
def __cmp__(self, other):
|
||||
return reduce(lambda acc, (a, b): acc or cmp(a, b),
|
||||
izip_longest(self.parts, other.parts),
|
||||
0)
|
||||
|
||||
@property
|
||||
def is_release(self):
|
||||
return bool(re.match(r'^[\d.]+$', self._version))
|
||||
|
|
@ -4,7 +4,7 @@ import types
|
|||
import urlparse
|
||||
|
||||
from unicodehelper import decode
|
||||
from validator.specs.webapps import WebappSpec
|
||||
from .specs.webapps import WebappSpec
|
||||
|
||||
|
||||
def detect_webapp(err, package):
|
||||
|
@ -40,7 +40,7 @@ def detect_webapp_raw(err, webapp):
|
|||
Parse and validate a webapp based on the dict version of the manifest.
|
||||
"""
|
||||
|
||||
from validator.specs.webapps import WebappSpec
|
||||
from .specs.webapps import WebappSpec
|
||||
ws = WebappSpec(webapp, err)
|
||||
ws.validate()
|
||||
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
from ..helper import RegexTestCase
|
||||
from ..js_helper import TestCase as JSTestCase
|
||||
|
||||
|
||||
class CompatTestCase(JSTestCase, RegexTestCase):
|
||||
"""
|
||||
A TestCase object which aids in running tests regarding compatibility
|
||||
tests. It is expected that you will supply a static member `VERSION`
|
||||
describing the version(s) of Gecko that these tests apply to.
|
||||
"""
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(CompatTestCase, self).setUp(*args, **kwargs)
|
||||
|
||||
def run_script_for_compat(self, script, expose_pollution=False):
|
||||
"""
|
||||
Test a script with and without version restrictions to determine
|
||||
whether it properly raises JavaScript compatibility messages.
|
||||
"""
|
||||
self._run_member_for_compat(lambda: self.run_script(script,
|
||||
expose_pollution))
|
||||
|
||||
def run_xpcom_for_compat(self, interface, methods=None):
|
||||
"""
|
||||
Yields after each method has been run as a script. Used to test that
|
||||
XPCOM members are properly flagged for compatibility tests.
|
||||
|
||||
- `interface` should be the name of the XPCOM interface.
|
||||
- `methods` should be the member to test. It may be a simple reference
|
||||
to a member (i.e.: `foo`) or an action upon a member (i.e.:
|
||||
`foo('bar')` or `foo["bar"] = "zap"`).
|
||||
"""
|
||||
|
||||
script = """
|
||||
var x = Components.classes[""].createInstance(Components.interfaces.%s);
|
||||
x.%%s;
|
||||
""" % interface
|
||||
|
||||
if not methods:
|
||||
return
|
||||
|
||||
for method in methods:
|
||||
self.run_script_for_compat(script % method)
|
||||
yield
|
||||
|
||||
def run_regex_for_compat(self, input):
|
||||
"""
|
||||
Test an input with and without version restrictions to determine
|
||||
whether it properly raises regex compatibility messages.
|
||||
"""
|
||||
self._run_member_for_compat(lambda: self.run_regex(input))
|
||||
|
||||
def _run_member_for_compat(self, method):
|
||||
# Run the method without version restrictions.
|
||||
method()
|
||||
|
||||
# Store away that error bundle to prepare for the next error bundle.
|
||||
standard_err = self.err
|
||||
self.err = None
|
||||
|
||||
# Run the method again with version restrictions.
|
||||
self.setup_err(for_appversions=self.VERSION)
|
||||
method()
|
||||
|
||||
self.compat_err = self.err
|
||||
self.err = standard_err
|
||||
|
||||
def assert_compat_silent(self):
|
||||
"""Assert that no compatibility messages have been raised."""
|
||||
assert not any(self.compat_err.compat_summary.values()), \
|
||||
"Got %s" % self.compat_err.compat_summary
|
||||
|
||||
def assert_compat_error(self, type_="warning"):
|
||||
"""
|
||||
Assert that a compat error was raised as a message of type `type_`.
|
||||
"""
|
||||
self._assert_compat_type("error", type_)
|
||||
|
||||
def assert_compat_warning(self, type_="notice"):
|
||||
"""
|
||||
Assert that a compat warning was raised as a message of type `type_`.
|
||||
"""
|
||||
self._assert_compat_type("warning", type_)
|
||||
|
||||
def _assert_compat_type(self, compat_type, type_):
|
||||
print self.compat_err.print_summary()
|
||||
message_collection = {"error": self.compat_err.errors,
|
||||
"warning": self.compat_err.warnings,
|
||||
"notice": self.compat_err.notices}[type_]
|
||||
assert message_collection, \
|
||||
"No %ss were raised in the compatibility test." % type_
|
||||
assert any(m["compatibility_type"] == compat_type for
|
||||
m in message_collection), \
|
||||
("No %ss that raise a compatibility %s were found." %
|
||||
(type_, compat_type))
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX10_DEFINITION
|
||||
|
||||
|
||||
class TestFX10Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 10 are properly executed."""
|
||||
|
||||
VERSION = FX10_DEFINITION
|
||||
|
||||
def test_isSameNode(self):
|
||||
"""Test that `isSameNode` is flagged in Gecko 10."""
|
||||
self.run_script_for_compat('alert(x.isSameNode(foo));')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_replaceWholeText(self):
|
||||
"""Test that `repalceWholeText` is flagged in Gecko 10."""
|
||||
self.run_script_for_compat('alert(x.replaceWholeText());')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_isElementContentWhitespace(self):
|
||||
"""Test that `isElementContentWhitespace` is flagged in Gecko 10."""
|
||||
self.run_script_for_compat('alert(x.isElementContentWhitespace);')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_xml_docuemnt_properties(self):
|
||||
"""
|
||||
Test that the `xmlEncoding`, `xmlVersion`, and `xmlStandalone` objects
|
||||
are dead for the document object in Gecko 10.
|
||||
"""
|
||||
patterns = ["document.xmlEncoding", "document.xmlVersion",
|
||||
"document.xmlStandalone", "content.document.xmlEncoding"]
|
||||
for pattern in patterns:
|
||||
self.run_script_for_compat("alert(%s);" % pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_xml_properties(self):
|
||||
"""
|
||||
Test that the `xmlEncoding`, `xmlVersion`, and `xmlStandalone` objects
|
||||
are dead in Gecko 10.
|
||||
"""
|
||||
patterns = ["foo.xmlEncoding", "foo.xmlVersion", "foo.xmlStandalone"]
|
||||
for pattern in patterns:
|
||||
self.run_script_for_compat("alert(%s);" % pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_interfaces(self):
|
||||
interfaces = ["nsIDOMNSHTMLFrameElement", "nsIDOMNSHTMLElement"]
|
||||
for interface in interfaces:
|
||||
self.run_script_for_compat("""
|
||||
var x = Components.classes[""].createInstance(
|
||||
Components.interfaces.%s);
|
||||
""" % interface)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_nsIBrowserHistory(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIBrowserHistory", ["lastPageVisited"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX11_DEFINITION
|
||||
|
||||
|
||||
class TestFX11Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 11 are properly executed."""
|
||||
|
||||
VERSION = FX11_DEFINITION
|
||||
|
||||
def test_requestAnimationFrame(self):
|
||||
"""Test that requestAnimationFrame requires at least one parameter."""
|
||||
|
||||
self.run_script_for_compat('requestAnimationFrame(foo);')
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
||||
self.run_script_for_compat('requestAnimationFrame();')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_patterns(self):
|
||||
patterns = ["nsICharsetResolver", '"omni.jar"']
|
||||
for pattern in patterns:
|
||||
self.run_regex_for_compat(pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX12_DEFINITION
|
||||
|
||||
|
||||
class TestFX12Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 12 are properly executed."""
|
||||
|
||||
VERSION = FX12_DEFINITION
|
||||
|
||||
def test_chromemargin(self):
|
||||
self.run_regex_for_compat('<foo chromemargin="1">')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_documentCharsetInfo(self):
|
||||
self.run_regex_for_compat('window.documentCharsetInfo;')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_interfaces(self):
|
||||
interfaces = ["nsIJetpack", "nsIJetpackService",
|
||||
"nsIProxyObjectManager"]
|
||||
for interface in interfaces:
|
||||
self.run_regex_for_compat("Components.interfaces.%s" % interface)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX13_DEFINITION
|
||||
|
||||
|
||||
class TestFX13Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 13 are properly executed."""
|
||||
|
||||
VERSION = FX13_DEFINITION
|
||||
|
||||
def test_startendMarker(self):
|
||||
"""Test that _startMarker and _endMarker are flagged in Gecko 13."""
|
||||
|
||||
patterns = ["bar()._startMarker",
|
||||
"bar()._startMarker = 1",
|
||||
"bar()._endMarker",
|
||||
"bar()._endMarker = 1"]
|
||||
for pattern in patterns:
|
||||
self.run_script_for_compat(pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_excludeItemsIfParentHasAnnotation(self):
|
||||
"""
|
||||
Test that `excludeItemsIfParentHasAnnotation` is flagged in Gecko 13.
|
||||
"""
|
||||
self.run_regex_for_compat("excludeItemsIfParentHasAnnotation")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_globalStorage_flagged(self):
|
||||
"""Test that `globalStorage` is flagged in Gecko 13."""
|
||||
self.run_regex_for_compat('globalStorage["foo"]')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsILivemarkService(self):
|
||||
self.run_regex_for_compat("nsILivemarkService")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsIPrefBranch2(self):
|
||||
self.run_regex_for_compat("nsIPrefBranch2")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_nsIScriptableUnescapeHTML(self):
|
||||
self.run_regex_for_compat("nsIScriptableUnescapeHTML")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
def test_nsIAccessNode(self):
|
||||
self.run_regex_for_compat("nsIAccessNode")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX14_DEFINITION
|
||||
|
||||
|
||||
class TestFX14Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 14 are properly executed."""
|
||||
|
||||
VERSION = FX14_DEFINITION
|
||||
|
||||
def test_nsIPlacesImportExportService(self):
|
||||
"""
|
||||
nsIPlacesImportExportService.importHTMLFromFile and .importHTMLFromURI
|
||||
have both been removed from Gecko 14.
|
||||
"""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIPlacesImportExportService",
|
||||
["importHTMLFromFile", "importHTMLFromURI"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsIDOMHTMLDocument(self):
|
||||
"""
|
||||
Test that `queryCommandText` and `execCommandShowHelp` have been
|
||||
flagged in Gecko 14.
|
||||
"""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIDOMHTMLDocument",
|
||||
["queryCommandText()", "execCommandShowHelp()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsINavBookmarksService(self):
|
||||
"""Test that GUIDs are removed from `nsINavBookarmsService`."""
|
||||
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsINavBookmarksService", ["getItemGUID", "setItemGUID",
|
||||
"getItemIdForGUID"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsIHistoryQueryOptions(self):
|
||||
"""Test that `redirectsMode` is flagged in Gecko 14."""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsINavHistoryQueryOptions", ["redirectsMode"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_onFaviconDataAvailable(self):
|
||||
"""Test that `onFaviconDataAvailable` is flagged in Gecko 14."""
|
||||
self.run_script_for_compat("alert(x.onFaviconDataAvailable());")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_onFaviconDataAvailable_assignment(self):
|
||||
"""
|
||||
Test that assignments to `onFaviconDataAvailable` is flagged in Gecko
|
||||
14.
|
||||
"""
|
||||
self.run_script_for_compat("{onFaviconDataAvailable: foo()}")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_Blob(self):
|
||||
"""Test that the (Moz)BlobBuilder objects are deprecated."""
|
||||
self.run_regex_for_compat("MozBlobBuilder")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
self.run_regex_for_compat("BlobBuilder")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_nsILocalFile(self):
|
||||
"""
|
||||
Test that `nsILocalFile` suggests that `nsIFile` should be used
|
||||
instead.
|
||||
"""
|
||||
self.run_regex_for_compat("nsILocalFile")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX5_DEFINITION
|
||||
|
||||
|
||||
class TestFX5Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Firefox 5 are properly executed."""
|
||||
|
||||
VERSION = FX5_DEFINITION
|
||||
|
||||
def test_navigator_language(self):
|
||||
"""
|
||||
Test that `navigator.language is flagged as potentially incompatible
|
||||
with Gecko 5.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat('alert(navigator.language);')
|
||||
self.assert_silent()
|
||||
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX6_DEFINITION
|
||||
from validator.errorbundler import ErrorBundle
|
||||
from validator.testcases.markup.markuptester import MarkupParser
|
||||
|
||||
|
||||
class TestGecko6Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Gecko 6 are properly executed."""
|
||||
|
||||
VERSION = FX6_DEFINITION
|
||||
|
||||
def test_window_top(self):
|
||||
"""
|
||||
Test that `window.top` (a reserved global variable as of Gecko 6) is
|
||||
flagged as incompatible.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat('window.top = "foo";');
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
# Also test the same script for implied global overwrites.
|
||||
self.run_script_for_compat('top = "foo";');
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
def test_custom_addon_types(self):
|
||||
"""
|
||||
Test that registering custom add-on types is flagged as being
|
||||
incompatible with Gecko 6.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat('AddonManagerPrivate.registerProvider();')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_menu_item_compat(self):
|
||||
"""
|
||||
Test that compatibility warnings are raised for the stuff from bug
|
||||
660349.
|
||||
"""
|
||||
|
||||
def _run_test(data, name="foo.xul", should_fail=False):
|
||||
def test(versions):
|
||||
err = ErrorBundle()
|
||||
err.supported_versions = versions
|
||||
parser = MarkupParser(err)
|
||||
parser.process(name,
|
||||
data,
|
||||
name.split(".")[-1])
|
||||
print err.print_summary(verbose=True)
|
||||
assert not err.failed()
|
||||
return err
|
||||
|
||||
err = test(FX6_DEFINITION)
|
||||
if should_fail:
|
||||
assert err.notices
|
||||
assert err.compat_summary["warnings"]
|
||||
else:
|
||||
assert not err.notices
|
||||
|
||||
assert not test({}).notices
|
||||
|
||||
# Test that the testcase doesn't apply to non-XUL files.
|
||||
err = _run_test("""
|
||||
<foo>
|
||||
<bar insertbefore="webConsole" />
|
||||
</foo>
|
||||
""", name="foo.xml")
|
||||
|
||||
# Test that a legitimate testcase will fail.
|
||||
err = _run_test("""
|
||||
<foo>
|
||||
<bar insertbefore="what,webConsole,evar" />
|
||||
</foo>
|
||||
""", should_fail=True)
|
||||
|
||||
# Test that the testcase only applies to the proper attribute values.
|
||||
err = _run_test("""
|
||||
<foo>
|
||||
<bar insertbefore="something else" />
|
||||
</foo>
|
||||
""")
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX7_DEFINITION
|
||||
|
||||
|
||||
class TestGecko7Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Gecko 7 are properly executed."""
|
||||
|
||||
VERSION = FX7_DEFINITION
|
||||
|
||||
def test_nsIDOMDocumentStyle(self):
|
||||
self.run_regex_for_compat("nsIDOMDocumentStyle")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsINavHistoryObserver(self):
|
||||
self.run_regex_for_compat("nsINavHistoryObserver")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_nsIMarkupDocumentViewer(self):
|
||||
self.run_regex_for_compat("nsIMarkupDocumentViewer_MOZILLA_2_0_BRANCH")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_nsIDOMFile(self):
|
||||
"""
|
||||
Test that nsIDOMFile's getAsBinary() and getAsDataURL() are flagged.
|
||||
"""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIDOMFile", ["getAsBinary()", "getAsDataURL()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_nsIJSON(self):
|
||||
"""Test that nsIJSON's encode() and decode() methods are flagged."""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIJSON", ["encode()", "decode()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX8_DEFINITION
|
||||
|
||||
|
||||
class TestGecko8Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Gecko 8 are properly executed."""
|
||||
|
||||
VERSION = FX8_DEFINITION
|
||||
|
||||
def test_nsISelection2(self):
|
||||
self.run_regex_for_compat("nsISelection2")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
def test_nsIDOMWindowInternal(self):
|
||||
self.run_regex_for_compat("nsIDOMWindowInternal")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_ISO8601DateUtils(self):
|
||||
self.run_regex_for_compat("ISO8601DateUtils")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="warning")
|
||||
|
||||
def test_getSelection(self):
|
||||
self.run_script_for_compat("document.getSelection();")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import FX9_DEFINITION
|
||||
|
||||
|
||||
class TestGecko9Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Gecko 9 are properly executed."""
|
||||
|
||||
VERSION = FX9_DEFINITION
|
||||
|
||||
def test_taintEnabled(self):
|
||||
self.run_script_for_compat("alert(navigator.taintEnabled);")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_documentURIObject(self):
|
||||
self.run_script_for_compat("alert(document.documentURIObject);")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
def test_nodePrincipal(self):
|
||||
self.run_script_for_compat("alert(document.nodePrincipal);")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
def test_baseURIObject(self):
|
||||
self.run_script_for_compat("alert(document.baseURIObject);")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning()
|
||||
|
||||
def test_nsIGlobalHistory3(self):
|
||||
self.run_regex_for_compat("nsIGlobalHistory3")
|
||||
self.assert_silent()
|
||||
self.assert_compat_warning(type_="warning")
|
||||
|
||||
def test_nsIURLParser_parsePath(self):
|
||||
"""nsIURLParser.parsePath takes 8 args instead of 10 now."""
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var URLi = Components.classes[
|
||||
"@mozilla.org/network/url-parser;1?auth=maybe"].
|
||||
createInstance(Components.interfaces.nsIURLParser);
|
||||
var filepathPos = {}, filepathLen = {}, paramPos = {}, paramLen = {},
|
||||
queryPos = {}, queryLen = {}, refPos = {}, refLen = {};
|
||||
URLi.parsePath(urlObj.path, -1, filepathPos, filepathLen, paramPos,
|
||||
paramLen, queryPos, queryLen, refPos, refLen);
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var URLi = Components.classes[
|
||||
"@mozilla.org/network/url-parser;1?auth=maybe"].
|
||||
createInstance(Components.interfaces.nsIURLParser);
|
||||
var filepathPos = {}, filepathLen = {}, queryPos = {}, queryLen = {},
|
||||
refPos = {}, refLen = {};
|
||||
URLi.parsePath(urlObj.path, -1, filepathPos, filepathLen, queryPos,
|
||||
queryLen, refPos, refLen);
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
||||
def test_nsIURL(self):
|
||||
"""nsIURL.param was removed in Gecko 9."""
|
||||
for method in self.run_xpcom_for_compat("nsIURL", ["param"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_nsIBrowserHistory_removePages(self):
|
||||
"""
|
||||
nsIBrowserHistory.removePages() takes 2 arguments instead of 3 in Gecko
|
||||
9.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var history = Components.classes[""].getService(
|
||||
Components.interfaces.nsIBrowserHistory);
|
||||
history.removePages(foo, bar, false);
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var history = Components.classes[""].getService(
|
||||
Components.interfaces.nsIBrowserHistory);
|
||||
history.removePages(foo, bar);
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
||||
def test_nsIBrowserHistory_register(self):
|
||||
"""
|
||||
nsIBrowserHistory.registerOpenPage() and
|
||||
nsIBrowserHistory.unregisterOpenPage() no longer exist in Gecko 9.
|
||||
"""
|
||||
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIBrowserHistory", ["registerOpenPage()",
|
||||
"unregisterOpenPage()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_nsIEditorSpellCheck_saveDefaultDictionary(self):
|
||||
"""
|
||||
In Gecko 9, nsIEditorSpellCheck no longer has the function
|
||||
`saveDefaultDictionary()`.
|
||||
"""
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIEditorSpellCheck", ["saveDefaultDictionary"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
def test_nsIEditorSpellCheck_UpdateCurrentDictionary(self):
|
||||
"""
|
||||
In Gecko 9, nsIEditorSpellCheck.UpdateCurrentDictionary no longer
|
||||
accepts parameters.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var editor = Components.classes[""].getService(
|
||||
Components.interfaces.nsIEditorSpellCheck);
|
||||
editor.UpdateCurrentDictionary(foo, bar);
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="error")
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var editor = Components.classes[""].getService(
|
||||
Components.interfaces.nsIEditorSpellCheck);
|
||||
editor.UpdateCurrentDictionary();
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
||||
def test_geo_prefs(self):
|
||||
"""
|
||||
`geo.wifi.uri` and `geo.wifi.protocol` are no longer set in Gecko 9.
|
||||
Referring to them produces a compatibility error.
|
||||
"""
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var app = Components.classes["@mozilla.org/fuel/application;1"]
|
||||
.getService(Components.interfaces.fuelIApplication);
|
||||
var _uri = app.prefs.get('geo.wifi.uri');
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var app = Components.classes["@mozilla.org/fuel/application;1"]
|
||||
.getService(Components.interfaces.fuelIApplication);
|
||||
var _protocol = app.prefs.get('geo.wifi.protocol');
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error()
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB10_DEFINITION
|
||||
|
||||
|
||||
class TestTB10Compat(CompatTestCase):
|
||||
"""
|
||||
Test that compatibility tests for Thunderbird 10 are properly executed.
|
||||
"""
|
||||
|
||||
VERSION = TB10_DEFINITION
|
||||
|
||||
def test_functions(self):
|
||||
patterns = ["MsgDeleteMessageFromMessageWindow", "goToggleSplitter",
|
||||
"AddMessageComposeOfflineObserver",
|
||||
"RemoveMessageComposeOfflineObserver",
|
||||
"gDownloadManagerStrings.get"]
|
||||
for pattern in patterns:
|
||||
self.run_script_for_compat("%s();" % pattern)
|
||||
self.assert_silent()
|
||||
assert self.compat_err.warnings or self.compat_err.notices
|
||||
assert self.compat_err.compat_summary["errors"]
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB11_DEFINITION
|
||||
|
||||
|
||||
class TestTB11Compat(CompatTestCase):
|
||||
"""
|
||||
Test that compatibility tests for Thunderbird 11 are properly executed.
|
||||
"""
|
||||
|
||||
VERSION = TB11_DEFINITION
|
||||
|
||||
def test_interfaces(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgQuote", ["quoteMessage()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMailtoUrl", ["GetMessageContents()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB12_DEFINITION
|
||||
|
||||
|
||||
class TestTB12Compat(CompatTestCase):
|
||||
"""
|
||||
Test that compatibility tests for Thunderbird 12 are properly executed.
|
||||
"""
|
||||
|
||||
VERSION = TB12_DEFINITION
|
||||
|
||||
def test_interfaces(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgDBService", ["openMailDBFromFile()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgDatabase", ["Open()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB13_DEFINITION
|
||||
|
||||
|
||||
class TestTB13Compat(CompatTestCase):
|
||||
"""
|
||||
Test that compatibility tests for Thunderbird 13 are properly executed.
|
||||
"""
|
||||
|
||||
VERSION = TB13_DEFINITION
|
||||
|
||||
def test_interfaces(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgLocalMailFolder", ["addMessage()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="warning")
|
||||
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgNewsFolder", ["getGroupUsernameWithUI()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="warning")
|
||||
|
||||
def test_functions(self):
|
||||
patterns = ["serverPageInit", "loginPageInit", "serverPageValidate",
|
||||
"serverPageUnload", "loginPageValidate", "setupBccTextbox",
|
||||
"setupCcTextbox"]
|
||||
for pattern in patterns:
|
||||
self.run_regex_for_compat("var x = %s();" % pattern)
|
||||
self.assert_silent()
|
||||
assert self.compat_err.notices
|
||||
assert self.compat_err.compat_summary["errors"]
|
||||
|
||||
# Extra tests for similar functions.
|
||||
for pattern in ["serverPage", "clientPageInit"]:
|
||||
self.run_regex_for_compat("var x = %s();" % pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB14_DEFINITION
|
||||
|
||||
|
||||
class TestTB14Compat(CompatTestCase):
|
||||
"""
|
||||
Test that compatibility tests for Thunderbird 14 are properly executed.
|
||||
"""
|
||||
|
||||
VERSION = TB14_DEFINITION
|
||||
|
||||
def test_interfaces(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgPluggableStore", ["copyMessages()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_functions(self):
|
||||
patterns = ["mailnews.display.html_sanitizer.allowed_tags", "gPrefs()",
|
||||
"gHeaderParser()", "(msgComposeService()", "nsPrefBranch()",
|
||||
"(cvHeaderParser",]
|
||||
for pattern in patterns:
|
||||
self.run_regex_for_compat("var x = %s();" % pattern)
|
||||
self.assert_silent()
|
||||
assert self.compat_err.notices
|
||||
assert self.compat_err.compat_summary["errors"]
|
||||
|
||||
# Extra tests for similar functions.
|
||||
for pattern in ["cvsPrefs()", "mailnews()",
|
||||
"CollapseSectionHeaderators()",
|
||||
"msgMailSession", "msgPrefs",]:
|
||||
self.run_regex_for_compat("var x = %s();" % pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_silent()
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB6_DEFINITION
|
||||
|
||||
|
||||
class TestTB6Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Thunderbird 6 are properly executed."""
|
||||
|
||||
VERSION = TB6_DEFINITION
|
||||
|
||||
def test_nsIImapMailFolderSink(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIImapMailFolderSink", ["setUrlState()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_nsIImapProtocol(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIImapProtocol", ["NotifyHdrsToDownload()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB7_DEFINITION
|
||||
|
||||
|
||||
class TestTB7Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Thunderbird 7 are properly executed."""
|
||||
|
||||
VERSION = TB7_DEFINITION
|
||||
|
||||
def test_nsIMsgThread(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgThread", ["GetChildAt()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_mail_attachment(self):
|
||||
"""Test that the old mail attachment global functions are flagged."""
|
||||
functions = ["createNewAttachmentInfo",
|
||||
"saveAttachment",
|
||||
"attachmentIsEmpty",
|
||||
"openAttachment",
|
||||
"detachAttachment",
|
||||
"cloneAttachment"]
|
||||
for function in functions:
|
||||
self.run_script_for_compat("%s()" % function)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
def test_dictUtils_removal(self):
|
||||
"""Test that dictUtils.js imports are flagged."""
|
||||
self.run_script_for_compat(
|
||||
'Components.utils.import("resource:///modules/dictUtils.js");')
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="warning")
|
||||
|
||||
def test_deRDF_addressbook(self):
|
||||
"""Test that addressbook RDF sources are flagged."""
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var x = 'datasources="rdf:addressdirectory" ref="moz-abdirectory://"';
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
||||
self.run_script_for_compat("""
|
||||
var x = 'GetResource(SomeText).QueryInterface(6inTestxnsIAbDirectory);';
|
||||
""")
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB8_DEFINITION
|
||||
|
||||
|
||||
class TestTB8Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Thunderbird 8 are properly executed."""
|
||||
|
||||
VERSION = TB8_DEFINITION
|
||||
|
||||
def test_nsIMsgSearchScopeTerm(self):
|
||||
for method in self.run_xpcom_for_compat(
|
||||
"nsIMsgSearchScopeTerm", ["mailFile()", "inputStream()"]):
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
from helper import CompatTestCase
|
||||
from validator.compat import TB9_DEFINITION
|
||||
|
||||
|
||||
class TestTB9Compat(CompatTestCase):
|
||||
"""Test that compatibility tests for Thunderbird 9 are properly executed."""
|
||||
|
||||
VERSION = TB9_DEFINITION
|
||||
|
||||
def test_functions(self):
|
||||
patterns = ["gComposeBundle", "FocusOnFirstAttachment",
|
||||
"WhichPaneHasFocus"]
|
||||
for pattern in patterns:
|
||||
self.run_script_for_compat("%s();" % pattern)
|
||||
self.assert_silent()
|
||||
self.assert_compat_error(type_="notice")
|
||||
|
|
@ -1,15 +1,11 @@
|
|||
import sys
|
||||
|
||||
from validator.submain import populate_chrome_manifest
|
||||
from validator.rdf import RDFParser
|
||||
from validator.xpi import XPIManager
|
||||
from validator.errorbundler import ErrorBundle
|
||||
from validator.outputhandlers.shellcolors import OutputHandler
|
||||
import validator.testcases.regex as regex
|
||||
from appvalidator.xpi import XPIManager
|
||||
from appvalidator.errorbundler import ErrorBundle
|
||||
from appvalidator.outputhandlers.shellcolors import OutputHandler
|
||||
|
||||
|
||||
def _do_test(path, test, failure=True,
|
||||
require_install=False, set_type=0,
|
||||
def _do_test(path, test, failure=True, set_type=0,
|
||||
listed=False, xpi_mode="r"):
|
||||
|
||||
package_data = open(path, "rb")
|
||||
|
@ -21,13 +17,6 @@ def _do_test(path, test, failure=True,
|
|||
# Populate in the dependencies.
|
||||
if set_type:
|
||||
err.set_type(set_type) # Conduit test requires type
|
||||
if require_install:
|
||||
err.save_resource("has_install_rdf", True)
|
||||
rdf_data = package.read("install.rdf")
|
||||
install_rdf = RDFParser(err, rdf_data)
|
||||
err.save_resource("install_rdf", install_rdf)
|
||||
|
||||
populate_chrome_manifest(err, package)
|
||||
|
||||
test(err, package)
|
||||
|
||||
|
@ -44,7 +33,6 @@ def _do_test(path, test, failure=True,
|
|||
class TestCase(object):
|
||||
def setUp(self):
|
||||
self.err = None
|
||||
self.is_jetpack = False
|
||||
self.is_bootstrapped = False
|
||||
self.detected_type = None
|
||||
self.listed = True
|
||||
|
@ -70,8 +58,6 @@ class TestCase(object):
|
|||
listed=self.listed)
|
||||
self.err.handler = OutputHandler(sys.stdout, True)
|
||||
|
||||
if self.is_jetpack:
|
||||
self.err.metadata["is_jetpack"] = True
|
||||
if self.is_bootstrapped:
|
||||
self.err.save_resource("em:bootstrap", True)
|
||||
if self.detected_type is not None:
|
||||
|
@ -125,8 +111,6 @@ class TestCase(object):
|
|||
assert not self.err.errors, 'Got these: %s' % self.err.errors
|
||||
assert not self.err.warnings, 'Got these: %s' % self.err.warnings
|
||||
assert not self.err.notices, 'Got these: %s' % self.err.notices
|
||||
assert not any(self.err.compat_summary.values()), \
|
||||
"Found compatibility messages."
|
||||
|
||||
def assert_got_errid(self, errid):
|
||||
"""
|
||||
|
@ -138,26 +122,6 @@ class TestCase(object):
|
|||
"%s was expected, but it was not found." % repr(errid)
|
||||
|
||||
|
||||
class RegexTestCase(TestCase):
|
||||
"""
|
||||
A helper class to provide functions useful for performing tests against
|
||||
regex test scenarios.
|
||||
"""
|
||||
|
||||
def run_regex(self, input):
|
||||
"""Run the standard regex tests for non-JavaScript input."""
|
||||
if self.err is None:
|
||||
self.setup_err()
|
||||
input = '<input onclick="%s" />' % input
|
||||
regex.run_regex_tests(input, self.err, "foo.txt", is_js=False)
|
||||
|
||||
def run_js_regex(self, input):
|
||||
"""Run the standard regex tests for JavaScript input."""
|
||||
if self.err is None:
|
||||
self.setup_err()
|
||||
regex.run_regex_tests(input, self.err, "foo.txt", is_js=True)
|
||||
|
||||
|
||||
class MockZipFile:
|
||||
|
||||
def namelist(self):
|
||||
|
|
|
@ -4,11 +4,11 @@ from nose.tools import eq_
|
|||
|
||||
import helper
|
||||
from helper import MockXPI
|
||||
from validator.errorbundler import ErrorBundle
|
||||
from validator.outputhandlers.shellcolors import OutputHandler
|
||||
import validator.testcases.content
|
||||
import validator.testcases.scripting
|
||||
validator.testcases.scripting.traverser.DEBUG = True
|
||||
from appvalidator.errorbundler import ErrorBundle
|
||||
from appvalidator.outputhandlers.shellcolors import OutputHandler
|
||||
import appvalidator.testcases.content
|
||||
import appvalidator.testcases.scripting
|
||||
appvalidator.testcases.scripting.traverser.DEBUG = True
|
||||
|
||||
|
||||
def _do_test(path):
|
||||
|
@ -23,8 +23,6 @@ def _do_test_raw(script, path="foo.js", bootstrap=False, ignore_pollution=True,
|
|||
"Performs a test on a JS file"
|
||||
|
||||
err = ErrorBundle(instant=True)
|
||||
if jetpack:
|
||||
err.metadata["is_jetpack"] = True
|
||||
|
||||
err.handler = OutputHandler(sys.stdout, True)
|
||||
err.supported_versions = {}
|
||||
|
@ -33,7 +31,7 @@ def _do_test_raw(script, path="foo.js", bootstrap=False, ignore_pollution=True,
|
|||
if detected_type:
|
||||
err.detected_type = detected_type
|
||||
|
||||
validator.testcases.content._process_file(
|
||||
appvalidator.testcases.content._process_file(
|
||||
err, MockXPI(), path, script, path.lower(), not ignore_pollution)
|
||||
if err.final_context is not None:
|
||||
print err.final_context.output()
|
||||
|
@ -42,7 +40,7 @@ def _do_test_raw(script, path="foo.js", bootstrap=False, ignore_pollution=True,
|
|||
|
||||
|
||||
def _do_real_test_raw(script, path="foo.js", versions=None, detected_type=None,
|
||||
metadata=None, resources=None, jetpack=False):
|
||||
metadata=None, resources=None):
|
||||
"""Perform a JS test using a non-mock bundler."""
|
||||
|
||||
err = ErrorBundle(for_appversions=versions or {})
|
||||
|
@ -52,11 +50,9 @@ def _do_real_test_raw(script, path="foo.js", versions=None, detected_type=None,
|
|||
err.metadata = metadata
|
||||
if resources is not None:
|
||||
err.resources = resources
|
||||
if jetpack:
|
||||
err.metadata["is_jetpack"] = True
|
||||
|
||||
validator.testcases.content._process_file(err, MockXPI(), path, script,
|
||||
path.lower())
|
||||
appvalidator.testcases.content._process_file(err, MockXPI(), path, script,
|
||||
path.lower())
|
||||
return err
|
||||
|
||||
|
||||
|
@ -103,10 +99,10 @@ class TestCase(helper.TestCase):
|
|||
if self.err.supported_versions is None:
|
||||
self.err.supported_versions = {}
|
||||
|
||||
validator.testcases.content._process_file(self.err, MockXPI(),
|
||||
self.file_path, script,
|
||||
self.file_path.lower(),
|
||||
expose_pollution)
|
||||
appvalidator.testcases.content._process_file(self.err, MockXPI(),
|
||||
self.file_path, script,
|
||||
self.file_path.lower(),
|
||||
expose_pollution)
|
||||
if self.err.final_context is not None:
|
||||
print self.err.final_context.output()
|
||||
self.final_context = self.err.final_context
|
||||
|
|
|
@ -1,216 +0,0 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 expandtab */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
|
||||
results: Cr, manager: Cm } = Components;
|
||||
const ioService = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
const resourceHandler = ioService.getProtocolHandler('resource')
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
const XMLHttpRequest = CC('@mozilla.org/xmlextras/xmlhttprequest;1',
|
||||
'nsIXMLHttpRequest');
|
||||
const prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService).
|
||||
QueryInterface(Ci.nsIPrefBranch2);
|
||||
const mozIJSSubScriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
|
||||
'install', 'uninstall', 'upgrade', 'downgrade' ];
|
||||
|
||||
let loader = null;
|
||||
let loaderUri = null;
|
||||
|
||||
const URI = __SCRIPT_URI_SPEC__.replace(/bootstrap\.js$/, "");
|
||||
|
||||
// Initializes default preferences
|
||||
function setDefaultPrefs() {
|
||||
let branch = prefs.getDefaultBranch("");
|
||||
let prefLoaderScope = {
|
||||
pref: function(key, val) {
|
||||
switch (typeof val) {
|
||||
case "boolean":
|
||||
branch.setBoolPref(key, val);
|
||||
break;
|
||||
case "number":
|
||||
if (val % 1 == 0) // number must be a integer, otherwise ignore it
|
||||
branch.setIntPref(key, val);
|
||||
break;
|
||||
case "string":
|
||||
branch.setCharPref(key, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let uri = ioService.newURI(
|
||||
"defaults/preferences/prefs.js",
|
||||
null,
|
||||
ioService.newURI(URI, null, null));
|
||||
|
||||
// if there is a prefs.js file, then import the default prefs
|
||||
try {
|
||||
// setup default prefs
|
||||
mozIJSSubScriptLoader.loadSubScript(uri.spec, prefLoaderScope);
|
||||
}
|
||||
// errors here should not kill addon
|
||||
catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the topic that fit best as application startup event, in according with
|
||||
// the current application (e.g. Firefox, Fennec, Thunderbird...)
|
||||
function getAppStartupTopic() {
|
||||
// The following mapping of application names to GUIDs was taken
|
||||
// from `xul-app` module. They should keep in sync, so if you change one,
|
||||
// change the other too!
|
||||
let ids = {
|
||||
Firefox: '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}',
|
||||
Mozilla: '{86c18b42-e466-45a9-ae7a-9b95ba6f5640}',
|
||||
Sunbird: '{718e30fb-e89b-41dd-9da7-e25a45638b28}',
|
||||
SeaMonkey: '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}',
|
||||
Fennec: '{aa3c5121-dab2-40e2-81ca-7ea25febc110}',
|
||||
Thunderbird: '{3550f703-e582-4d05-9a08-453d09bdfdc6}'
|
||||
};
|
||||
|
||||
let id = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULAppInfo).ID;
|
||||
|
||||
switch (id) {
|
||||
case ids.Firefox:
|
||||
case ids.SeaMonkey:
|
||||
return 'sessionstore-windows-restored';
|
||||
case ids.Thunderbird:
|
||||
return 'mail-startup-done';
|
||||
// Temporary, until Fennec Birch will support sessionstore event
|
||||
case ids.Fennec:
|
||||
default:
|
||||
return 'final-ui-startup';
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function that synchronously reads local resource from the given
|
||||
// `uri` and returns content string.
|
||||
function readURI(uri) {
|
||||
let ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
let channel = ioservice.newChannel(uri, "UTF-8", null);
|
||||
let stream = channel.open();
|
||||
|
||||
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
|
||||
cstream.init(stream, "UTF-8", 0, 0);
|
||||
|
||||
let str = {};
|
||||
let data = "";
|
||||
let read = 0;
|
||||
do {
|
||||
read = cstream.readString(0xffffffff, str);
|
||||
data += str.value;
|
||||
} while (read != 0);
|
||||
|
||||
cstream.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Function takes `topic` to be observer via `nsIObserverService` and returns
|
||||
// promise that will be delivered once notification is published.
|
||||
function on(topic) {
|
||||
return function promise(deliver) {
|
||||
const observerService = Cc['@mozilla.org/observer-service;1'].
|
||||
getService(Ci.nsIObserverService);
|
||||
|
||||
observerService.addObserver({
|
||||
observe: function observer(subject, topic, data) {
|
||||
observerService.removeObserver(this, topic);
|
||||
deliver(subject, topic, data);
|
||||
}
|
||||
}, topic, false);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't do anything on install & uninstall yet, but in a future
|
||||
// we should allow add-ons to cleanup after uninstall.
|
||||
function install(data, reason) {}
|
||||
function uninstall(data, reason) {}
|
||||
|
||||
function startup(data, reason) {
|
||||
// TODO: When bug 564675 is implemented this will no longer be needed
|
||||
// Always set the default prefs, because they disappear on restart
|
||||
setDefaultPrefs();
|
||||
|
||||
// TODO: Maybe we should perform read harness-options.json asynchronously,
|
||||
// since we can't do anything until 'sessionstore-windows-restored' anyway.
|
||||
let options = JSON.parse(readURI(URI + './harness-options.json'));
|
||||
options.loadReason = REASON[reason];
|
||||
|
||||
// URI for the root of the XPI file.
|
||||
// 'jar:' URI if the addon is packed, 'file:' URI otherwise.
|
||||
// (Used by l10n module in order to fetch `locale` folder)
|
||||
options.rootURI = data.resourceURI.spec;
|
||||
|
||||
// Register a new resource "domain" for this addon which is mapping to
|
||||
// XPI's `resources` folder.
|
||||
// Generate the domain name by using jetpack ID, which is the extension ID
|
||||
// by stripping common characters that doesn't work as a domain name:
|
||||
let uuidRe =
|
||||
/^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
|
||||
let domain = options.jetpackID.toLowerCase()
|
||||
.replace(/@/g, "-at-")
|
||||
.replace(/\./g, "-dot-")
|
||||
.replace(uuidRe, "$1");
|
||||
|
||||
let resourcesUri = ioService.newURI(URI + '/resources/', null, null);
|
||||
resourceHandler.setSubstitution(domain, resourcesUri);
|
||||
options.uriPrefix = "resource://" + domain + "/";
|
||||
|
||||
// Import loader module using `Cu.imports` and bootstrap module loader.
|
||||
loaderUri = options.uriPrefix + options.loader;
|
||||
loader = Cu.import(loaderUri).Loader.new(options);
|
||||
|
||||
// Creating a promise, that will be delivered once application is ready.
|
||||
// If application is at startup then promise is delivered on
|
||||
// the application startup topic, otherwise it's delivered immediately.
|
||||
let promise = reason === APP_STARTUP ? on(getAppStartupTopic()) :
|
||||
function promise(deliver) deliver()
|
||||
|
||||
// Once application is ready we spawn a new process with main module of
|
||||
// on add-on.
|
||||
promise(function() {
|
||||
try {
|
||||
loader.spawn(options.main, options.mainPath);
|
||||
} catch (error) {
|
||||
// If at this stage we have an error only thing we can do is report about
|
||||
// it via error console. Keep in mind that error won't automatically show
|
||||
// up there when called via observerService.
|
||||
Cu.reportError(error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function shutdown(data, reason) {
|
||||
// If loader is already present unload it, since add-on is disabled.
|
||||
if (loader) {
|
||||
reason = REASON[reason];
|
||||
let system = loader.require('api-utils/system');
|
||||
loader.unload(reason);
|
||||
|
||||
// Bug 724433: We need to unload JSM otherwise it will stay alive
|
||||
// and keep a reference to this compartment.
|
||||
Cu.unload(loaderUri);
|
||||
loader = null;
|
||||
|
||||
// If add-on is lunched via `cfx run` we need to use `system.exit` to let
|
||||
// cfx know we're done (`cfx test` will take care of exit so we don't do
|
||||
// anything here).
|
||||
if (system.env.CFX_COMMAND === 'run' && reason === 'shutdown')
|
||||
system.exit(0);
|
||||
}
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
locale basta resource
|
||||
subject predicate object
|
||||
#subject predicate whatever
|
||||
sub
|
||||
locale predicate resource
|
Двоичные данные
tests/resources/conduit/basta_bar.xpi
Двоичные данные
tests/resources/conduit/basta_bar.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/conduit/conduit_chrome.xpi
Двоичные данные
tests/resources/conduit/conduit_chrome.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/conduit/conduit_params.xpi
Двоичные данные
tests/resources/conduit/conduit_params.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/conduit/conduit_structure.xpi
Двоичные данные
tests/resources/conduit/conduit_structure.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/conduit/conduit_updateurl.xpi
Двоичные данные
tests/resources/conduit/conduit_updateurl.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/conduit/pass.xpi
Двоичные данные
tests/resources/conduit/pass.xpi
Двоичный файл не отображается.
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE overlay SYSTEM "chrome://helloworld/locale/overlay.dtd">
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<!--<em:contributor>contributor_1_value</em:contributor>-->
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
<em:homepageURL>url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<!--<em:type>2</em:type> -->
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>homepage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<em:internalName>name_value</em:internalName>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<em:internalName>name_value</em:internalName>
|
||||
<em:internalName>sadfa</em:internalName>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:id>second id</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,38 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:file>true</em:file>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<em:strictCompatibility>true</em:strictCompatibility>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
|
||||
<em:updateURL>true</em:updateURL>
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
|
||||
<em:type>2</em:type>
|
||||
<em:unpack>true</em:unpack>
|
||||
|
||||
|
||||
<em:id>bastatestapp1@basta.mozilla.com</em:id>
|
||||
<em:version>1.2.3.4</em:version>
|
||||
<em:name>name_value</em:name>
|
||||
<em:description>description_value</em:description>
|
||||
<em:creator>creator_value</em:creator>
|
||||
<em:homepageURL>hompage_url_value</em:homepageURL>
|
||||
|
||||
<em:contributor>contributor_1_value</em:contributor>
|
||||
<em:contributor>contributor_2_value</em:contributor>
|
||||
<em:contributor>contributor_3_value</em:contributor>
|
||||
<em:contributor>contributor_4_value</em:contributor>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.7a5pre</em:minVersion>
|
||||
<em:maxVersion>0.3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
|
||||
</RDF>
|
Двоичные данные
tests/resources/jetpack/jetpack-1.8-outdated.xpi
Двоичные данные
tests/resources/jetpack/jetpack-1.8-outdated.xpi
Двоичный файл не отображается.
Двоичные данные
tests/resources/jetpack/jetpack-1.8-pretending-1.8.1.xpi
Двоичные данные
tests/resources/jetpack/jetpack-1.8-pretending-1.8.1.xpi
Двоичный файл не отображается.
|
@ -1,21 +0,0 @@
|
|||
<!ENTITY
|
||||
foo
|
||||
"bar">
|
||||
<!ENTITY
|
||||
abc.def
|
||||
"xyz">
|
||||
<!ENTITY
|
||||
fast
|
||||
'ball'>
|
||||
<!ENTITY
|
||||
two
|
||||
'per'
|
||||
><!ENTITY line 'woot'>
|
||||
<!ENTITY
|
||||
|
||||
this.is.a
|
||||
'test'>
|
||||
<!ENTITY overwrite
|
||||
'foo'>
|
||||
<!ENTITY overwrite 'bar'
|
||||
>
|
|
@ -1,10 +0,0 @@
|
|||
<!ENTITY foo "bar">
|
||||
<!--Malformed line should not overwrite -->
|
||||
<!ENTITY< foo "oops">
|
||||
<!WHATEVER abc.def "xyz">
|
||||
<!ENTITY abc.def "xyz">
|
||||
<!ENTITY fast 'ball'>
|
||||
<!ENTITY two 'per'><!ENTITY line 'woot'>
|
||||
<!ENTITY this.is.a 'test'>
|
||||
<!ENTITY overwrite 'foo'>
|
||||
<!ENTITY overwrite 'bar'>
|
|
@ -1,9 +0,0 @@
|
|||
<!ENTITY foo "bar">
|
||||
<!ENTITY abc.def "xyz">
|
||||
<!ENTITY fast 'ball'>
|
||||
<!ENTITY two 'per'><!ENTITY line 'woot'>
|
||||
<!ENTITY this.is.a 'test'>
|
||||
<!ENTITY overwrite 'foo'>
|
||||
<!ENTITY overwrite 'bar'>
|
||||
<!-- Non-SGML declarations should not register. -->
|
||||
<xml></xml>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче