toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,186 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +this.EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"];
    1.11 +
    1.12 +const Cu = Components.utils;
    1.13 +const Ci = Components.interfaces;
    1.14 +
    1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.16 +
    1.17 +XPCOMUtils.defineLazyModuleGetter(this, "Services",
    1.18 +  "resource://gre/modules/Services.jsm");
    1.19 +
    1.20 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
    1.21 +  "resource://gre/modules/FileUtils.jsm");
    1.22 +
    1.23 +const ORIGIN_TOP_RIGHT = 1;
    1.24 +const ORIGIN_BOTTOM_LEFT = 2;
    1.25 +
    1.26 +this.LightweightThemeImageOptimizer = {
    1.27 +  optimize: function LWTIO_optimize(aThemeData, aScreen) {
    1.28 +    let data = Utils.createCopy(aThemeData);
    1.29 +    if (!data.headerURL) {
    1.30 +      return data;
    1.31 +    }
    1.32 +
    1.33 +    data.headerURL = ImageCropper.getCroppedImageURL(
    1.34 +      data.headerURL, aScreen, ORIGIN_TOP_RIGHT);
    1.35 +
    1.36 +    if (data.footerURL) {
    1.37 +      data.footerURL = ImageCropper.getCroppedImageURL(
    1.38 +        data.footerURL, aScreen, ORIGIN_BOTTOM_LEFT);
    1.39 +    }
    1.40 +
    1.41 +    return data;
    1.42 +  },
    1.43 +
    1.44 +  purge: function LWTIO_purge() {
    1.45 +    let dir = FileUtils.getDir("ProfD", ["lwtheme"]);
    1.46 +    dir.followLinks = false;
    1.47 +    try {
    1.48 +      dir.remove(true);
    1.49 +    } catch (e) {}
    1.50 +  }
    1.51 +};
    1.52 +
    1.53 +Object.freeze(LightweightThemeImageOptimizer);
    1.54 +
    1.55 +let ImageCropper = {
    1.56 +  _inProgress: {},
    1.57 +
    1.58 +  getCroppedImageURL:
    1.59 +  function ImageCropper_getCroppedImageURL(aImageURL, aScreen, aOrigin) {
    1.60 +    // We can crop local files, only.
    1.61 +    if (!aImageURL.startsWith("file://")) {
    1.62 +      return aImageURL;
    1.63 +    }
    1.64 +
    1.65 +    // Generate the cropped image's file name using its
    1.66 +    // base name and the current screen size.
    1.67 +    let uri = Services.io.newURI(aImageURL, null, null);
    1.68 +    let file = uri.QueryInterface(Ci.nsIFileURL).file;
    1.69 +
    1.70 +    // Make sure the source file exists.
    1.71 +    if (!file.exists()) {
    1.72 +      return aImageURL;
    1.73 +    }
    1.74 +
    1.75 +    let fileName = file.leafName + "-" + aScreen.width + "x" + aScreen.height;
    1.76 +    let croppedFile = FileUtils.getFile("ProfD", ["lwtheme", fileName]);
    1.77 +
    1.78 +    // If we have a local file that is not in progress, return it.
    1.79 +    if (croppedFile.exists() && !(croppedFile.path in this._inProgress)) {
    1.80 +      let fileURI = Services.io.newFileURI(croppedFile);
    1.81 +
    1.82 +      // Copy the query part to avoid wrong caching.
    1.83 +      fileURI.QueryInterface(Ci.nsIURL).query = uri.query;
    1.84 +      return fileURI.spec;
    1.85 +    }
    1.86 +
    1.87 +    // Crop the given image in the background.
    1.88 +    this._crop(uri, croppedFile, aScreen, aOrigin);
    1.89 +
    1.90 +    // Return the original image while we're waiting for the cropped version
    1.91 +    // to be written to disk.
    1.92 +    return aImageURL;
    1.93 +  },
    1.94 +
    1.95 +  _crop: function ImageCropper_crop(aURI, aTargetFile, aScreen, aOrigin) {
    1.96 +    let inProgress = this._inProgress;
    1.97 +    inProgress[aTargetFile.path] = true;
    1.98 +
    1.99 +    function resetInProgress() {
   1.100 +      delete inProgress[aTargetFile.path];
   1.101 +    }
   1.102 +
   1.103 +    ImageFile.read(aURI, function crop_readImageFile(aInputStream, aContentType) {
   1.104 +      if (aInputStream && aContentType) {
   1.105 +        let image = ImageTools.decode(aInputStream, aContentType);
   1.106 +        if (image && image.width && image.height) {
   1.107 +          let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType);
   1.108 +          if (stream) {
   1.109 +            ImageFile.write(aTargetFile, stream, resetInProgress);
   1.110 +            return;
   1.111 +          }
   1.112 +        }
   1.113 +      }
   1.114 +
   1.115 +      resetInProgress();
   1.116 +    });
   1.117 +  }
   1.118 +};
   1.119 +
   1.120 +let ImageFile = {
   1.121 +  read: function ImageFile_read(aURI, aCallback) {
   1.122 +    this._netUtil.asyncFetch(aURI, function read_asyncFetch(aInputStream, aStatus, aRequest) {
   1.123 +      if (Components.isSuccessCode(aStatus) && aRequest instanceof Ci.nsIChannel) {
   1.124 +        let channel = aRequest.QueryInterface(Ci.nsIChannel);
   1.125 +        aCallback(aInputStream, channel.contentType);
   1.126 +      } else {
   1.127 +        aCallback();
   1.128 +      }
   1.129 +    });
   1.130 +  },
   1.131 +
   1.132 +  write: function ImageFile_write(aFile, aInputStream, aCallback) {
   1.133 +    let fos = FileUtils.openSafeFileOutputStream(aFile);
   1.134 +    this._netUtil.asyncCopy(aInputStream, fos, function write_asyncCopy(aResult) {
   1.135 +      FileUtils.closeSafeFileOutputStream(fos);
   1.136 +
   1.137 +      // Remove the file if writing was not successful.
   1.138 +      if (!Components.isSuccessCode(aResult)) {
   1.139 +        try {
   1.140 +          aFile.remove(false);
   1.141 +        } catch (e) {}
   1.142 +      }
   1.143 +
   1.144 +      aCallback();
   1.145 +    });
   1.146 +  }
   1.147 +};
   1.148 +
   1.149 +XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
   1.150 +  "resource://gre/modules/NetUtil.jsm", "NetUtil");
   1.151 +
   1.152 +let ImageTools = {
   1.153 +  decode: function ImageTools_decode(aInputStream, aContentType) {
   1.154 +    let outParam = {value: null};
   1.155 +
   1.156 +    try {
   1.157 +      this._imgTools.decodeImageData(aInputStream, aContentType, outParam);
   1.158 +    } catch (e) {}
   1.159 +
   1.160 +    return outParam.value;
   1.161 +  },
   1.162 +
   1.163 +  encode: function ImageTools_encode(aImage, aScreen, aOrigin, aContentType) {
   1.164 +    let stream;
   1.165 +    let width = Math.min(aImage.width, aScreen.width);
   1.166 +    let height = Math.min(aImage.height, aScreen.height);
   1.167 +    let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
   1.168 +
   1.169 +    try {
   1.170 +      stream = this._imgTools.encodeCroppedImage(aImage, aContentType, x, 0,
   1.171 +                                                 width, height);
   1.172 +    } catch (e) {}
   1.173 +
   1.174 +    return stream;
   1.175 +  }
   1.176 +};
   1.177 +
   1.178 +XPCOMUtils.defineLazyServiceGetter(ImageTools, "_imgTools",
   1.179 +  "@mozilla.org/image/tools;1", "imgITools");
   1.180 +
   1.181 +let Utils = {
   1.182 +  createCopy: function Utils_createCopy(aData) {
   1.183 +    let copy = {};
   1.184 +    for (let [k, v] in Iterator(aData)) {
   1.185 +      copy[k] = v;
   1.186 +    }
   1.187 +    return copy;
   1.188 +  }
   1.189 +};

mercurial