addon-sdk/source/python-lib/cuddlefish/xpi.py

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 # This Source Code Form is subject to the terms of the Mozilla Public
     2 # License, v. 2.0. If a copy of the MPL was not distributed with this
     3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     5 import os
     6 import zipfile
     7 import simplejson as json
     8 from cuddlefish.util import filter_filenames, filter_dirnames
    10 class HarnessOptionAlreadyDefinedError(Exception):
    11     """You cannot use --harness-option on keys that already exist in
    12     harness-options.json"""
    14 ZIPSEP = "/" # always use "/" in zipfiles
    16 def make_zipfile_path(localroot, localpath):
    17     return ZIPSEP.join(localpath[len(localroot)+1:].split(os.sep))
    19 def mkzipdir(zf, path):
    20     dirinfo = zipfile.ZipInfo(path)
    21     dirinfo.external_attr = int("040755", 8) << 16L
    22     zf.writestr(dirinfo, "")
    24 def build_xpi(template_root_dir, manifest, xpi_path,
    25               harness_options, limit_to=None, extra_harness_options={},
    26               bundle_sdk=True, pkgdir=""):
    27     IGNORED_FILES = [".hgignore", ".DS_Store", "install.rdf",
    28                      "application.ini", xpi_path]
    30     files_to_copy = {} # maps zipfile path to local-disk abspath
    31     dirs_to_create = set() # zipfile paths, no trailing slash
    33     zf = zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED)
    35     open('.install.rdf', 'w').write(str(manifest))
    36     zf.write('.install.rdf', 'install.rdf')
    37     os.remove('.install.rdf')
    39     # Handle add-on icon
    40     if 'icon' in harness_options:
    41         zf.write(str(harness_options['icon']), 'icon.png')
    42         del harness_options['icon']
    44     if 'icon64' in harness_options:
    45         zf.write(str(harness_options['icon64']), 'icon64.png')
    46         del harness_options['icon64']
    48     # chrome.manifest
    49     if os.path.isfile(os.path.join(pkgdir, 'chrome.manifest')):
    50       files_to_copy['chrome.manifest'] = os.path.join(pkgdir, 'chrome.manifest')
    52     # chrome folder (would contain content, skin, and locale folders typically)
    53     folder = 'chrome'
    54     if os.path.exists(os.path.join(pkgdir, folder)):
    55       dirs_to_create.add('chrome')
    56       # cp -r folder
    57       abs_dirname = os.path.join(pkgdir, folder)
    58       for dirpath, dirnames, filenames in os.walk(abs_dirname):
    59           goodfiles = list(filter_filenames(filenames, IGNORED_FILES))
    60           dirnames[:] = filter_dirnames(dirnames)
    61           for dirname in dirnames:
    62             arcpath = make_zipfile_path(template_root_dir,
    63                                         os.path.join(dirpath, dirname))
    64             dirs_to_create.add(arcpath)
    65           for filename in goodfiles:
    66               abspath = os.path.join(dirpath, filename)
    67               arcpath = ZIPSEP.join(
    68                   [folder,
    69                    make_zipfile_path(abs_dirname, os.path.join(dirpath, filename)),
    70                    ])
    71               files_to_copy[str(arcpath)] = str(abspath)
    73     # Handle simple-prefs
    74     if 'preferences' in harness_options:
    75         from options_xul import parse_options, validate_prefs
    77         validate_prefs(harness_options["preferences"])
    79         opts_xul = parse_options(harness_options["preferences"],
    80                                  harness_options["jetpackID"],
    81                                  harness_options["preferencesBranch"])
    82         open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
    83         zf.write('.options.xul', 'options.xul')
    84         os.remove('.options.xul')
    86         from options_defaults import parse_options_defaults
    87         prefs_js = parse_options_defaults(harness_options["preferences"],
    88                                           harness_options["preferencesBranch"])
    89         open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
    91     else:
    92         open('.prefs.js', 'wb').write("")
    94     zf.write('.prefs.js', 'defaults/preferences/prefs.js')
    95     os.remove('.prefs.js')
    98     for dirpath, dirnames, filenames in os.walk(template_root_dir):
    99         filenames = list(filter_filenames(filenames, IGNORED_FILES))
   100         dirnames[:] = filter_dirnames(dirnames)
   101         for dirname in dirnames:
   102             arcpath = make_zipfile_path(template_root_dir,
   103                                         os.path.join(dirpath, dirname))
   104             dirs_to_create.add(arcpath)
   105         for filename in filenames:
   106             abspath = os.path.join(dirpath, filename)
   107             arcpath = make_zipfile_path(template_root_dir, abspath)
   108             files_to_copy[arcpath] = abspath
   110     # `packages` attribute contains a dictionnary of dictionnary
   111     # of all packages sections directories
   112     for packageName in harness_options['packages']:
   113       base_arcpath = ZIPSEP.join(['resources', packageName])
   114       # Eventually strip sdk files. We need to do that in addition to the
   115       # whilelist as the whitelist is only used for `cfx xpi`:
   116       if not bundle_sdk and packageName == 'addon-sdk':
   117           continue
   118       # Always write the top directory, even if it contains no files, since
   119       # the harness will try to access it.
   120       dirs_to_create.add(base_arcpath)
   121       for sectionName in harness_options['packages'][packageName]:
   122         abs_dirname = harness_options['packages'][packageName][sectionName]
   123         base_arcpath = ZIPSEP.join(['resources', packageName, sectionName])
   124         # Always write the top directory, even if it contains no files, since
   125         # the harness will try to access it.
   126         dirs_to_create.add(base_arcpath)
   127         # cp -r stuff from abs_dirname/ into ZIP/resources/RESOURCEBASE/
   128         for dirpath, dirnames, filenames in os.walk(abs_dirname):
   129             goodfiles = list(filter_filenames(filenames, IGNORED_FILES))
   130             dirnames[:] = filter_dirnames(dirnames)
   131             for filename in goodfiles:
   132                 abspath = os.path.join(dirpath, filename)
   133                 if limit_to is not None and abspath not in limit_to:
   134                     continue  # strip unused files
   135                 arcpath = ZIPSEP.join(
   136                     ['resources',
   137                      packageName,
   138                      sectionName,
   139                      make_zipfile_path(abs_dirname,
   140                                        os.path.join(dirpath, filename)),
   141                      ])
   142                 files_to_copy[str(arcpath)] = str(abspath)
   143     del harness_options['packages']
   145     locales_json_data = {"locales": []}
   146     mkzipdir(zf, "locale/")
   147     for language in sorted(harness_options['locale']):
   148         locales_json_data["locales"].append(language)
   149         locale = harness_options['locale'][language]
   150         # Be carefull about strings, we need to always ensure working with UTF-8
   151         jsonStr = json.dumps(locale, indent=1, sort_keys=True, ensure_ascii=False)
   152         info = zipfile.ZipInfo('locale/' + language + '.json')
   153         info.external_attr = 0644 << 16L
   154         zf.writestr(info, jsonStr.encode( "utf-8" ))
   155     del harness_options['locale']
   157     jsonStr = json.dumps(locales_json_data, ensure_ascii=True) +"\n"
   158     info = zipfile.ZipInfo('locales.json')
   159     info.external_attr = 0644 << 16L
   160     zf.writestr(info, jsonStr.encode("utf-8"))
   162     # now figure out which directories we need: all retained files parents
   163     for arcpath in files_to_copy:
   164         bits = arcpath.split("/")
   165         for i in range(1,len(bits)):
   166             parentpath = ZIPSEP.join(bits[0:i])
   167             dirs_to_create.add(parentpath)
   169     # Create zipfile in alphabetical order, with each directory before its
   170     # files
   171     for name in sorted(dirs_to_create.union(set(files_to_copy))):
   172         if name in dirs_to_create:
   173             mkzipdir(zf, name+"/")
   174         if name in files_to_copy:
   175             zf.write(files_to_copy[name], name)
   177     # Add extra harness options
   178     harness_options = harness_options.copy()
   179     for key,value in extra_harness_options.items():
   180         if key in harness_options:
   181             msg = "Can't use --harness-option for existing key '%s'" % key
   182             raise HarnessOptionAlreadyDefinedError(msg)
   183         harness_options[key] = value
   185     # Write harness-options.json
   186     open('.options.json', 'w').write(json.dumps(harness_options, indent=1,
   187                                                 sort_keys=True))
   188     zf.write('.options.json', 'harness-options.json')
   189     os.remove('.options.json')
   191     zf.close()

mercurial