| OLD | NEW | 
|---|
| 1 # coding: utf-8 | 1 # coding: utf-8 | 
| 2 | 2 | 
| 3 # This file is part of the Adblock Plus build tools, | 3 # This file is part of the Adblock Plus build tools, | 
| 4 # Copyright (C) 2006-2013 Eyeo GmbH | 4 # Copyright (C) 2006-2013 Eyeo GmbH | 
| 5 # | 5 # | 
| 6 # Adblock Plus is free software: you can redistribute it and/or modify | 6 # Adblock Plus is free software: you can redistribute it and/or modify | 
| 7 # it under the terms of the GNU General Public License version 3 as | 7 # it under the terms of the GNU General Public License version 3 as | 
| 8 # published by the Free Software Foundation. | 8 # published by the Free Software Foundation. | 
| 9 # | 9 # | 
| 10 # Adblock Plus is distributed in the hope that it will be useful, | 10 # Adblock Plus is distributed in the hope that it will be useful, | 
| 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 13 # GNU General Public License for more details. | 13 # GNU General Public License for more details. | 
| 14 # | 14 # | 
| 15 # You should have received a copy of the GNU General Public License | 15 # You should have received a copy of the GNU General Public License | 
| 16 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 16 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 17 | 17 | 
| 18 import sys, os, re, json, struct | 18 import sys, os, re, json, struct | 
| 19 from StringIO import StringIO | 19 from StringIO import StringIO | 
| 20 | 20 | 
|  | 21 import PIL.Image | 
|  | 22 import PIL.ImageMath | 
|  | 23 | 
| 21 import packager | 24 import packager | 
| 22 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild
     Version, getTemplate, Files | 25 from packager import readMetadata, getMetadataPath, getDefaultFileName, getBuild
     Version, getTemplate, Files | 
| 23 | 26 | 
| 24 defaultLocale = 'en_US' | 27 defaultLocale = 'en_US' | 
| 25 | 28 | 
| 26 def getIgnoredFiles(params): | 29 def getIgnoredFiles(params): | 
| 27   result = set(('store.description',)) | 30   result = set(('store.description',)) | 
| 28 | 31 | 
| 29   # Hack: ignore all lib subdirectories | 32   # Hack: ignore all lib subdirectories | 
| 30   libDir = os.path.join(params['baseDir'], 'lib') | 33   libDir = os.path.join(params['baseDir'], 'lib') | 
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 272       'pt': 'pt_PT', | 275       'pt': 'pt_PT', | 
| 273     } | 276     } | 
| 274     for chromeLocale, operaLocale in operaMapping.iteritems(): | 277     for chromeLocale, operaLocale in operaMapping.iteritems(): | 
| 275       chromeFile = '_locales/%s/messages.json' % chromeLocale | 278       chromeFile = '_locales/%s/messages.json' % chromeLocale | 
| 276       operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No
     ne else None | 279       operaFile = '_locales/%s/messages.json' % operaLocale if operaLocale != No
     ne else None | 
| 277       if chromeFile in files: | 280       if chromeFile in files: | 
| 278         if operaFile != None: | 281         if operaFile != None: | 
| 279           files[operaFile] = files[chromeFile] | 282           files[operaFile] = files[chromeFile] | 
| 280         del files[chromeFile] | 283         del files[chromeFile] | 
| 281 | 284 | 
| 282     # Hack: Replace "Chrome" by "Opera" in the locales | 285   if params['type'] in ('opera', 'safari'): | 
|  | 286     # Hack: Replace "Chrome" by "Opera" or "Safari" in the locales | 
| 283     for path, data in files.iteritems(): | 287     for path, data in files.iteritems(): | 
| 284       if path.startswith("_locales/") and path.endswith("/messages.json"): | 288       if path.startswith("_locales/") and path.endswith("/messages.json"): | 
| 285         files[path] = re.sub(r"\bChrome\b", "Opera", data) | 289         files[path] = re.sub(r"\bChrome\b", params['type'].capitalize(), data) | 
| 286 | 290 | 
| 287 def signBinary(zipdata, keyFile): | 291 def signBinary(zipdata, keyFile): | 
| 288   import M2Crypto | 292   import M2Crypto | 
| 289   if not os.path.exists(keyFile): | 293   if not os.path.exists(keyFile): | 
| 290     M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile,
      cipher=None) | 294     M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile,
      cipher=None) | 
| 291   key = M2Crypto.EVP.load_key(keyFile) | 295   key = M2Crypto.EVP.load_key(keyFile) | 
| 292   key.sign_init() | 296   key.sign_init() | 
| 293   key.sign_update(zipdata) | 297   key.sign_update(zipdata) | 
| 294   return key.final() | 298   return key.final() | 
| 295 | 299 | 
| 296 def getPublicKey(keyFile): | 300 def getPublicKey(keyFile): | 
| 297   import M2Crypto | 301   import M2Crypto | 
| 298   return M2Crypto.EVP.load_key(keyFile).as_der() | 302   return M2Crypto.EVP.load_key(keyFile).as_der() | 
| 299 | 303 | 
| 300 def writePackage(outputFile, pubkey, signature, zipdata): | 304 def writePackage(outputFile, pubkey, signature, zipdata): | 
| 301   if isinstance(outputFile, basestring): | 305   if isinstance(outputFile, basestring): | 
| 302     file = open(outputFile, 'wb') | 306     file = open(outputFile, 'wb') | 
| 303   else: | 307   else: | 
| 304     file = outputFile | 308     file = outputFile | 
| 305   if pubkey != None and signature != None: | 309   if pubkey != None and signature != None: | 
| 306     file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature))) | 310     file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature))) | 
| 307     file.write(pubkey) | 311     file.write(pubkey) | 
| 308     file.write(signature) | 312     file.write(signature) | 
| 309   file.write(zipdata) | 313   file.write(zipdata) | 
| 310 | 314 | 
|  | 315 class ImageConverter(object): | 
|  | 316   def convert(self, params, files): | 
|  | 317     for filename, chain in params['metadata'].items('convert_img'): | 
|  | 318       steps = re.split(r'\s*->\s*', chain) | 
|  | 319       image = PIL.Image.open(steps.pop(0)) | 
|  | 320 | 
|  | 321       for step in steps: | 
|  | 322         filter, args = re.match(r'([^(]+)(?:\((.*)\))?', step).groups() | 
|  | 323         args = tuple(re.split(r'\s*,\s*', args)) if args else () | 
|  | 324         image = getattr(self, 'filter_' + filter)(image, *args) | 
|  | 325 | 
|  | 326       f = StringIO() | 
|  | 327       f.name = filename | 
|  | 328       image.save(f) | 
|  | 329       files[filename] = f.getvalue() | 
|  | 330 | 
|  | 331   def filter_blend(self, image, *args): | 
|  | 332     if len(args) == 2:    # args = (filename, opacity) | 
|  | 333       overlay = PIL.Image.open(args[0]) | 
|  | 334 | 
|  | 335       if image.mode != overlay.mode or image.mode == 'P': | 
|  | 336         overlay = overlay.convert('RGBA') | 
|  | 337         image   =   image.convert('RGBA') | 
|  | 338     elif len(args) == 4:  # args = (red, green, blue, opacity) | 
|  | 339       overlay = PIL.Image.new('RGB', image.size, tuple(map(int, args[:3]))) | 
|  | 340 | 
|  | 341       if image.mode == 'P': | 
|  | 342         image = image.convert('RGBA') | 
|  | 343 | 
|  | 344       if image.mode in ('RGBA', 'LA'): | 
|  | 345         overlay = PIL.Image.merge('RGBA', overlay.split() + image.split()[-1:]) | 
|  | 346 | 
|  | 347       if image.mode != overlay.mode: | 
|  | 348         image = image.convert(overlay.mode) | 
|  | 349     else: | 
|  | 350       raise TypeError | 
|  | 351 | 
|  | 352     return PIL.Image.blend(image, overlay, float(args[-1])) | 
|  | 353 | 
|  | 354   def filter_grayscale(self, image): | 
|  | 355     if image.mode == 'P': | 
|  | 356       image = image.convert('RGBA') | 
|  | 357 | 
|  | 358     bands = list(image.split()) | 
|  | 359     alpha = bands.pop(-1) if image.mode in ('RGBA', 'LA') else None | 
|  | 360 | 
|  | 361     if len(bands) == 1: | 
|  | 362       return image | 
|  | 363 | 
|  | 364     new_image = PIL.ImageMath.eval( | 
|  | 365       "convert((%s) / %d, 'L')" % ( | 
|  | 366         ' + '.join('image%d' % i for i in xrange(len(bands))), | 
|  | 367         len(bands) | 
|  | 368       ), | 
|  | 369       **dict(('image%d' % i, band) for i, band in enumerate(bands)) | 
|  | 370     ) | 
|  | 371 | 
|  | 372     if alpha: | 
|  | 373       new_image = PIL.Image.merge('LA', [new_image, alpha]) | 
|  | 374 | 
|  | 375     return new_image | 
|  | 376 | 
|  | 377   def filter_colorToAlpha(self, image, *color): | 
|  | 378     bands = [band.convert('F') for band in image.convert('RGBA').split()] | 
|  | 379     color = map(float, color) | 
|  | 380 | 
|  | 381     # Find the maximum difference rate between source and color. I had to use tw
     o | 
|  | 382     # difference functions because ImageMath.eval only evaluates the expression | 
|  | 383     # once. | 
|  | 384     alpha = PIL.ImageMath.eval('''\ | 
|  | 385       float( | 
|  | 386         max( | 
|  | 387           max( | 
|  | 388             max( | 
|  | 389               difference1(red_band, cred_band), | 
|  | 390               difference1(green_band, cgreen_band) | 
|  | 391             ), | 
|  | 392             difference1(blue_band, cblue_band) | 
|  | 393           ), | 
|  | 394           max( | 
|  | 395             max( | 
|  | 396               difference2(red_band, cred_band), | 
|  | 397               difference2(green_band, cgreen_band) | 
|  | 398             ), | 
|  | 399             difference2(blue_band, cblue_band) | 
|  | 400           ) | 
|  | 401         ) | 
|  | 402       )''', | 
|  | 403       difference1=lambda source, col: (source - col) / (255.0 - col), | 
|  | 404       difference2=lambda source, col: (col - source) / col, | 
|  | 405 | 
|  | 406       red_band  = bands[0], | 
|  | 407       green_band= bands[1], | 
|  | 408       blue_band = bands[2], | 
|  | 409 | 
|  | 410       cred_band  = color[0], | 
|  | 411       cgreen_band= color[1], | 
|  | 412       cblue_band = color[2], | 
|  | 413     ) | 
|  | 414 | 
|  | 415     # Calculate the new image colors after the removal of the selected color | 
|  | 416     new_bands = [ | 
|  | 417       PIL.ImageMath.eval( | 
|  | 418         "convert((image - color) / alpha + color, 'L')", | 
|  | 419         image=band, | 
|  | 420         color=col, | 
|  | 421         alpha=alpha | 
|  | 422       ) | 
|  | 423       for band, col in zip(bands, color) | 
|  | 424     ] | 
|  | 425 | 
|  | 426     # Add the new alpha band | 
|  | 427     new_bands.append(PIL.ImageMath.eval( | 
|  | 428       "convert(alpha_band * alpha, 'L')", | 
|  | 429       alpha = alpha, | 
|  | 430       alpha_band = bands[3] | 
|  | 431     )) | 
|  | 432 | 
|  | 433     return PIL.Image.merge('RGBA', new_bands) | 
|  | 434 | 
| 311 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil
     d=False, keyFile=None, experimentalAPI=False, devenv=False): | 435 def createBuild(baseDir, type='chrome', outFile=None, buildNum=None, releaseBuil
     d=False, keyFile=None, experimentalAPI=False, devenv=False): | 
| 312   metadata = readMetadata(baseDir, type) | 436   metadata = readMetadata(baseDir, type) | 
| 313   version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) | 437   version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) | 
| 314 | 438 | 
| 315   if outFile == None: | 439   if outFile == None: | 
| 316     outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el
     se 'zip') | 440     outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile el
     se 'zip') | 
| 317 | 441 | 
| 318   params = { | 442   params = { | 
| 319     'type': type, | 443     'type': type, | 
| 320     'baseDir': baseDir, | 444     'baseDir': baseDir, | 
| 321     'releaseBuild': releaseBuild, | 445     'releaseBuild': releaseBuild, | 
| 322     'version': version, | 446     'version': version, | 
| 323     'experimentalAPI': experimentalAPI, | 447     'experimentalAPI': experimentalAPI, | 
| 324     'devenv': devenv, | 448     'devenv': devenv, | 
| 325     'metadata': metadata, | 449     'metadata': metadata, | 
| 326   } | 450   } | 
| 327 | 451 | 
| 328   files = Files(getPackageFiles(params), getIgnoredFiles(params), | 452   files = Files(getPackageFiles(params), getIgnoredFiles(params), | 
| 329                 process=lambda path, data: processFile(path, data, params)) | 453                 process=lambda path, data: processFile(path, data, params)) | 
| 330   files['manifest.json'] = createManifest(params) | 454   files['manifest.json'] = createManifest(params) | 
| 331   if metadata.has_section('mapping'): | 455   if metadata.has_section('mapping'): | 
| 332     files.readMappedFiles(metadata.items('mapping')) | 456     files.readMappedFiles(metadata.items('mapping')) | 
| 333   files.read(baseDir) | 457   files.read(baseDir) | 
| 334 | 458 | 
| 335   if metadata.has_section('convert_js'): | 459   if metadata.has_section('convert_js'): | 
| 336     convertJS(params, files) | 460     convertJS(params, files) | 
| 337 | 461 | 
|  | 462   if metadata.has_section('convert_img'): | 
|  | 463     ImageConverter().convert(params, files) | 
|  | 464 | 
| 338   if metadata.has_section('import_locales'): | 465   if metadata.has_section('import_locales'): | 
| 339     importGeckoLocales(params, files) | 466     importGeckoLocales(params, files) | 
| 340 | 467 | 
| 341   if devenv: | 468   if devenv: | 
| 342     files['devenvPoller__.js'] = createPoller(params) | 469     files['devenvPoller__.js'] = createPoller(params) | 
| 343 | 470 | 
| 344   if (metadata.has_option('general', 'backgroundScripts') and | 471   if (metadata.has_option('general', 'backgroundScripts') and | 
| 345       'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip
     ts')) and | 472       'lib/info.js' in re.split(r'\s+', metadata.get('general', 'backgroundScrip
     ts')) and | 
| 346       'lib/info.js' not in files): | 473       'lib/info.js' not in files): | 
| 347     files['lib/info.js'] = createInfoModule(params) | 474     files['lib/info.js'] = createInfoModule(params) | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 379   def shutdown_server(server): | 506   def shutdown_server(server): | 
| 380     time.sleep(10) | 507     time.sleep(10) | 
| 381     server.shutdown() | 508     server.shutdown() | 
| 382   thread.start_new_thread(shutdown_server, (server,)) | 509   thread.start_new_thread(shutdown_server, (server,)) | 
| 383   server.serve_forever() | 510   server.serve_forever() | 
| 384 | 511 | 
| 385   if connections[0] == 0: | 512   if connections[0] == 0: | 
| 386     print 'Warning: No incoming connections, extension probably not active in th
     e browser yet' | 513     print 'Warning: No incoming connections, extension probably not active in th
     e browser yet' | 
| 387   else: | 514   else: | 
| 388     print 'Handled %i connection(s)' % connections[0] | 515     print 'Handled %i connection(s)' % connections[0] | 
| OLD | NEW | 
|---|