This commit is contained in:
Matt Basta 2012-08-01 15:43:36 -07:00
Родитель ac8e0163f0
Коммит b38db0cac7
201 изменённых файлов: 159 добавлений и 44998 удалений

3
.gitignore поставляемый
Просмотреть файл

@ -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/firefox.xpi

Двоичный файл не отображается.

Двоичные данные
appvalidator/testcases/langpacks/mozilla.xpi

Двоичный файл не отображается.

Двоичные данные
appvalidator/testcases/langpacks/seamonkey.xpi

Двоичный файл не отображается.

Двоичные данные
appvalidator/testcases/langpacks/sunbird.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

216
tests/resources/bootstrap.js поставляемый
Просмотреть файл

@ -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/conduit_chrome.xpi

Двоичный файл не отображается.

Двоичные данные
tests/resources/conduit/conduit_params.xpi

Двоичный файл не отображается.

Двоичные данные
tests/resources/conduit/conduit_structure.xpi

Двоичный файл не отображается.

Двоичные данные
tests/resources/conduit/conduit_updateurl.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

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -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>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше