Exemplo n.º 1
0
 def test_NoUsefulLines(self):
     # Parse manifest without useful data.  Lines that don't match what
     # we're looking for are ignored.
     manifest = XpiManifest("""
         There are no usable
         locale lines
         in this file.
         """.lstrip())
     self.assertEqual(len(manifest._locales), 0)
     chrome_path, locale = manifest.getChromePathAndLocale('lines')
     self.failIf(chrome_path is not None, "Empty manifest matched a path.")
     chrome_path, locale = manifest.getChromePathAndLocale('')
     self.failIf(chrome_path is not None, "Matched empty path.")
Exemplo n.º 2
0
 def test_NoUsefulLines(self):
     # Parse manifest without useful data.  Lines that don't match what
     # we're looking for are ignored.
     manifest = XpiManifest(
         """
         There are no usable
         locale lines
         in this file.
         """.lstrip()
     )
     self.assertEqual(len(manifest._locales), 0)
     chrome_path, locale = manifest.getChromePathAndLocale("lines")
     self.failIf(chrome_path is not None, "Empty manifest matched a path.")
     chrome_path, locale = manifest.getChromePathAndLocale("")
     self.failIf(chrome_path is not None, "Matched empty path.")
Exemplo n.º 3
0
 def test_TrivialParse(self):
     # Parse and use minimal manifest.
     manifest = XpiManifest("locale chromepath en-US directory/")
     self.assertEqual(len(manifest._locales), 1)
     chrome_path, locale = manifest.getChromePathAndLocale("directory/file.dtd")
     self.failIf(chrome_path is None, "Failed to match simple path")
     self.assertEqual(chrome_path, "chromepath/file.dtd", "Bad chrome path")
Exemplo n.º 4
0
 def test_NonMatch(self):
     # Failure to match path.
     manifest = XpiManifest("locale chromepath en-US directory/")
     chrome_path, locale = manifest.getChromePathAndLocale(
         'nonexistent/file')
     self.failIf(chrome_path is not None, "Unexpected path match.")
     self.failIf(locale is not None, "Got locale without a match.")
Exemplo n.º 5
0
 def test_TrivialParse(self):
     # Parse and use minimal manifest.
     manifest = XpiManifest("locale chromepath en-US directory/")
     self.assertEqual(len(manifest._locales), 1)
     chrome_path, locale = manifest.getChromePathAndLocale(
         'directory/file.dtd')
     self.failIf(chrome_path is None, "Failed to match simple path")
     self.assertEqual(chrome_path, "chromepath/file.dtd", "Bad chrome path")
Exemplo n.º 6
0
 def test_MultipleLocales(self):
     # Different locales.
     dirs = {"foo": "en-US", "bar": "es", "ixx": "zh_CN", "zup": "zh_TW", "gna": "pt", "gnu": "pt_BR"}
     manifest_text = "\n".join(["locale %s %s %sdir/\n" % (dir, locale, dir) for dir, locale in dirs.iteritems()])
     manifest = XpiManifest(manifest_text)
     self._checkSortOrder(manifest)
     for dir, dirlocale in dirs.iteritems():
         path = "%sdir/file.html" % dir
         chrome_path, locale = manifest.getChromePathAndLocale(path)
         self.assertEqual(chrome_path, "%s/file.html" % dir, "Bad chrome path in multi-line parse.")
         self.assertEqual(locale, dirlocale, "Locales got mixed up.")
Exemplo n.º 7
0
 def test_MultipleLines(self):
     # Parse manifest file with multiple entries.
     manifest = XpiManifest(
         """
         locale foo en-US foodir/
         locale bar en-US bardir/
         locale ixx en-US ixxdir/
         locale gna en-US gnadir/
         """.lstrip()
     )
     self.assertEqual(len(manifest._locales), 4)
     self._checkSortOrder(manifest)
     for dir in ["gna", "bar", "ixx", "foo"]:
         path = "%sdir/file.html" % dir
         chrome_path, locale = manifest.getChromePathAndLocale(path)
         self.assertEqual(chrome_path, "%s/file.html" % dir, "Bad chrome path in multi-line parse.")
         self.assertEqual(locale, "en-US", "Bad locale in multi-line parse.")
Exemplo n.º 8
0
 def test_MultipleLines(self):
     # Parse manifest file with multiple entries.
     manifest = XpiManifest("""
         locale foo en-US foodir/
         locale bar en-US bardir/
         locale ixx en-US ixxdir/
         locale gna en-US gnadir/
         """.lstrip())
     self.assertEqual(len(manifest._locales), 4)
     self._checkSortOrder(manifest)
     for dir in ['gna', 'bar', 'ixx', 'foo']:
         path = "%sdir/file.html" % dir
         chrome_path, locale = manifest.getChromePathAndLocale(path)
         self.assertEqual(chrome_path, "%s/file.html" % dir,
                          "Bad chrome path in multi-line parse.")
         self.assertEqual(locale, 'en-US',
                          "Bad locale in multi-line parse.")
Exemplo n.º 9
0
    def test_IgnoredLines(self):
        # Ignored lines: anything that doesn't start with "locale" or doesn't
        # have the right number of arguments.  The one correct line is picked
        # out though.
        manifest = XpiManifest("""
            nonlocale obsolete fr foodir/
            anotherline

            #locale obsolete fr foodir/
            locale okay fr foodir/
            locale overlong fr foordir/ etc. etc. etc.
            locale incomplete fr
            """.lstrip())
        self.assertEqual(len(manifest._locales), 1)
        chrome_path, locale = manifest.getChromePathAndLocale('foodir/x')
        self.failIf(chrome_path is None, "Garbage lines messed up match.")
        self.assertEqual(chrome_path, "okay/x", "Matched wrong line.")
        self.assertEqual(locale, "fr", "Inexplicably mismatched locale.")
Exemplo n.º 10
0
    def test_IgnoredLines(self):
        # Ignored lines: anything that doesn't start with "locale" or doesn't
        # have the right number of arguments.  The one correct line is picked
        # out though.
        manifest = XpiManifest(
            """
            nonlocale obsolete fr foodir/
            anotherline

            #locale obsolete fr foodir/
            locale okay fr foodir/
            locale overlong fr foordir/ etc. etc. etc.
            locale incomplete fr
            """.lstrip()
        )
        self.assertEqual(len(manifest._locales), 1)
        chrome_path, locale = manifest.getChromePathAndLocale("foodir/x")
        self.failIf(chrome_path is None, "Garbage lines messed up match.")
        self.assertEqual(chrome_path, "okay/x", "Matched wrong line.")
        self.assertEqual(locale, "fr", "Inexplicably mismatched locale.")
Exemplo n.º 11
0
 def test_MultipleLocales(self):
     # Different locales.
     dirs = {
         'foo': 'en-US',
         'bar': 'es',
         'ixx': 'zh_CN',
         'zup': 'zh_TW',
         'gna': 'pt',
         'gnu': 'pt_BR'
     }
     manifest_text = '\n'.join([
         "locale %s %s %sdir/\n" % (dir, locale, dir)
         for dir, locale in dirs.iteritems()
     ])
     manifest = XpiManifest(manifest_text)
     self._checkSortOrder(manifest)
     for dir, dirlocale in dirs.iteritems():
         path = "%sdir/file.html" % dir
         chrome_path, locale = manifest.getChromePathAndLocale(path)
         self.assertEqual(chrome_path, "%s/file.html" % dir,
                          "Bad chrome path in multi-line parse.")
         self.assertEqual(locale, dirlocale, "Locales got mixed up.")
Exemplo n.º 12
0
 def test_NonMatch(self):
     # Failure to match path.
     manifest = XpiManifest("locale chromepath en-US directory/")
     chrome_path, locale = manifest.getChromePathAndLocale("nonexistent/file")
     self.failIf(chrome_path is not None, "Unexpected path match.")
     self.failIf(locale is not None, "Got locale without a match.")
Exemplo n.º 13
0
class MozillaZipTraversal:
    """Traversal of an XPI file, or a jar file inside an XPI file.

    To traverse and process an XPI file, derive a class from this one
    and replace any hooks that you may need.

    If an XPI manifest is provided, traversal will be restricted to
    directories it lists as containing localizable resources.
    """

    def __init__(self, filename, archive, xpi_path=None, manifest=None):
        """Open zip (or XPI, or jar) file and scan its contents.

        :param filename: Name of this zip (XPI/jar) archive.
        :param archive: File-like object containing this zip archive.
        :param xpi_path: Full path of this file inside the XPI archive.
            Leave out for the XPI archive itself.
        :param manifest: `XpiManifest` representing the XPI archive's
            manifest file, if any.
        """
        self.filename = filename
        self.header = None
        self.last_translator = None
        self.manifest = manifest
        try:
            self.archive = ZipFile(archive, 'r')
        except BadZipfile as exception:
            raise TranslationFormatInvalidInputError(
                filename=filename, message=str(exception))

        if xpi_path is None:
            # This is the main XPI file.
            xpi_path = ''
            contained_files = set(self.archive.namelist())
            if manifest is None:
                # Look for a manifest.
                for filename in ['chrome.manifest', 'en-US.manifest']:
                    if filename in contained_files:
                        manifest_content = self.archive.read(filename)
                        self.manifest = XpiManifest(manifest_content)
                        break
            if 'install.rdf' in contained_files:
                rdf_content = self.archive.read('install.rdf')
                self.header = XpiHeader(rdf_content)

        # Strip trailing newline to avoid doubling it.
        xpi_path = xpi_path.rstrip('/')

        self._begin()

        # Process zipped files.  Sort by path to keep ordering deterministic.
        # Ordering matters in sequence numbering (which in turn shows up in
        # the UI), but also for consistency in duplicates resolution and for
        # automated testing.
        for entry in sorted(self.archive.namelist()):
            self._processEntry(entry, xpi_path)

        self._finish()

    def _processEntry(self, entry, xpi_path):
        """Read one zip archive entry, figure out what to do with it."""
        rootname, suffix = splitext(entry)
        if basename(rootname) == '':
            # If filename starts with a dot, that's not really a suffix.
            suffix = ''

        if suffix == '.jar':
            jarpath = make_jarpath(xpi_path, entry)
            if not self.manifest or self.manifest.containsLocales(jarpath):
                # If this is a jar file that may contain localizable
                # resources, don't process it in the normal way; recurse
                # by creating another parser instance.
                content = self.archive.read(entry)
                nested_instance = self.__class__(
                    filename=entry, archive=StringIO(content),
                    xpi_path=jarpath, manifest=self.manifest)

                self._processNestedJar(nested_instance)
                return

        # Construct XPI path; identical to "entry" if previous xpi_path
        # was empty.  XPI paths use slashes as separators, regardless of
        # what the native filesystem uses.
        xpi_path = '/'.join([xpi_path, entry]).lstrip('/')

        if self.manifest is None:
            # No manifest, so we don't have chrome paths.  Process
            # everything just to be sure.
            chrome_path = None
            locale_code = None
        else:
            chrome_path, locale_code = self.manifest.getChromePathAndLocale(
                xpi_path)
            if chrome_path is None:
                # Not in a directory containing localizable resources.
                return

        self._processTranslatableFile(
            entry, locale_code, xpi_path, chrome_path, suffix)

    def _begin(self):
        """Overridable hook: optional pre-traversal actions."""

    def _processTranslatableFile(self, entry, locale_code, xpi_path,
                                 chrome_path, filename_suffix):
        """Overridable hook: process a file entry.

        Called only for files that may be localizable.  If there is a
        manifest, that means the file must be in a location (or subtree)
        named by a "locale" entry.

        :param entry: Full name of file inside this zip archive,
            including path relative to the archive's root.
        :param locale_code: Code for locale this file belongs to, e.g.
            "en-US".
        :param xpi_path: Full path of this file inside the XPI archive,
            e.g. "jar:locale/en-US.jar!/data/messages.dtd".
        :param chrome_path: File's chrome path.  This is a kind of
            "normalized" path used in XPI to describe a virtual
            directory hierarchy.  The zip archive's actual layout (which
            the XPI paths describe) may be different.
        :param filename_suffix: File name suffix or "extension" of the
            translatable file.  This may be e.g. ".dtd" or ".xhtml," or
            the empty string if the filename does not contain a dot.
        """
        raise NotImplementedError(
            "XPI traversal class provides no _processTranslatableFile().")

    def _processNestedJar(self, zip_instance):
        """Overridable hook: handle a nested jar file.

        :param zip_instance: An instance of the same class as self, which
            has just parsed the nested jar file.
        """
        raise NotImplementedError(
            "XPI traversal class provides no _processNestedJar().")

    def _finish(self):
        """Overridable hook: post-traversal actions."""
        raise NotImplementedError(
            "XPI traversal class provides no _finish().")
Exemplo n.º 14
0
class MozillaZipTraversal:
    """Traversal of an XPI file, or a jar file inside an XPI file.

    To traverse and process an XPI file, derive a class from this one
    and replace any hooks that you may need.

    If an XPI manifest is provided, traversal will be restricted to
    directories it lists as containing localizable resources.
    """

    def __init__(self, filename, archive, xpi_path=None, manifest=None):
        """Open zip (or XPI, or jar) file and scan its contents.

        :param filename: Name of this zip (XPI/jar) archive.
        :param archive: File-like object containing this zip archive.
        :param xpi_path: Full path of this file inside the XPI archive.
            Leave out for the XPI archive itself.
        :param manifest: `XpiManifest` representing the XPI archive's
            manifest file, if any.
        """
        self.filename = filename
        self.header = None
        self.last_translator = None
        self.manifest = manifest
        try:
            self.archive = ZipFile(archive, "r")
        except BadZipfile as exception:
            raise TranslationFormatInvalidInputError(filename=filename, message=str(exception))

        if xpi_path is None:
            # This is the main XPI file.
            xpi_path = ""
            contained_files = set(self.archive.namelist())
            if manifest is None:
                # Look for a manifest.
                for filename in ["chrome.manifest", "en-US.manifest"]:
                    if filename in contained_files:
                        manifest_content = self.archive.read(filename)
                        self.manifest = XpiManifest(manifest_content)
                        break
            if "install.rdf" in contained_files:
                rdf_content = self.archive.read("install.rdf")
                self.header = XpiHeader(rdf_content)

        # Strip trailing newline to avoid doubling it.
        xpi_path = xpi_path.rstrip("/")

        self._begin()

        # Process zipped files.  Sort by path to keep ordering deterministic.
        # Ordering matters in sequence numbering (which in turn shows up in
        # the UI), but also for consistency in duplicates resolution and for
        # automated testing.
        for entry in sorted(self.archive.namelist()):
            self._processEntry(entry, xpi_path)

        self._finish()

    def _processEntry(self, entry, xpi_path):
        """Read one zip archive entry, figure out what to do with it."""
        rootname, suffix = splitext(entry)
        if basename(rootname) == "":
            # If filename starts with a dot, that's not really a suffix.
            suffix = ""

        if suffix == ".jar":
            jarpath = make_jarpath(xpi_path, entry)
            if not self.manifest or self.manifest.containsLocales(jarpath):
                # If this is a jar file that may contain localizable
                # resources, don't process it in the normal way; recurse
                # by creating another parser instance.
                content = self.archive.read(entry)
                nested_instance = self.__class__(
                    filename=entry, archive=StringIO(content), xpi_path=jarpath, manifest=self.manifest
                )

                self._processNestedJar(nested_instance)
                return

        # Construct XPI path; identical to "entry" if previous xpi_path
        # was empty.  XPI paths use slashes as separators, regardless of
        # what the native filesystem uses.
        xpi_path = "/".join([xpi_path, entry]).lstrip("/")

        if self.manifest is None:
            # No manifest, so we don't have chrome paths.  Process
            # everything just to be sure.
            chrome_path = None
            locale_code = None
        else:
            chrome_path, locale_code = self.manifest.getChromePathAndLocale(xpi_path)
            if chrome_path is None:
                # Not in a directory containing localizable resources.
                return

        self._processTranslatableFile(entry, locale_code, xpi_path, chrome_path, suffix)

    def _begin(self):
        """Overridable hook: optional pre-traversal actions."""

    def _processTranslatableFile(self, entry, locale_code, xpi_path, chrome_path, filename_suffix):
        """Overridable hook: process a file entry.

        Called only for files that may be localizable.  If there is a
        manifest, that means the file must be in a location (or subtree)
        named by a "locale" entry.

        :param entry: Full name of file inside this zip archive,
            including path relative to the archive's root.
        :param locale_code: Code for locale this file belongs to, e.g.
            "en-US".
        :param xpi_path: Full path of this file inside the XPI archive,
            e.g. "jar:locale/en-US.jar!/data/messages.dtd".
        :param chrome_path: File's chrome path.  This is a kind of
            "normalized" path used in XPI to describe a virtual
            directory hierarchy.  The zip archive's actual layout (which
            the XPI paths describe) may be different.
        :param filename_suffix: File name suffix or "extension" of the
            translatable file.  This may be e.g. ".dtd" or ".xhtml," or
            the empty string if the filename does not contain a dot.
        """
        raise NotImplementedError("XPI traversal class provides no _processTranslatableFile().")

    def _processNestedJar(self, zip_instance):
        """Overridable hook: handle a nested jar file.

        :param zip_instance: An instance of the same class as self, which
            has just parsed the nested jar file.
        """
        raise NotImplementedError("XPI traversal class provides no _processNestedJar().")

    def _finish(self):
        """Overridable hook: post-traversal actions."""
        raise NotImplementedError("XPI traversal class provides no _finish().")