b2g/components/DirectoryProvider.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     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 file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 const Cc = Components.classes;
     6 const Ci = Components.interfaces;
     7 const Cu = Components.utils;
     8 const Cr = Components.results;
    10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    11 Cu.import("resource://gre/modules/Services.jsm");
    13 const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
    14 const UPDATE_ARCHIVE_DIR = "UpdArchD"
    15 const LOCAL_DIR = "/data/local";
    16 const UPDATES_DIR = "updates/0";
    17 const FOTA_DIR = "updates/fota";
    19 XPCOMUtils.defineLazyServiceGetter(Services, "env",
    20                                    "@mozilla.org/process/environment;1",
    21                                    "nsIEnvironment");
    23 XPCOMUtils.defineLazyServiceGetter(Services, "um",
    24                                    "@mozilla.org/updates/update-manager;1",
    25                                    "nsIUpdateManager");
    27 XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
    28                                    "@mozilla.org/telephony/volume-service;1",
    29                                    "nsIVolumeService");
    31 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
    32                                    "@mozilla.org/childprocessmessagemanager;1",
    33                                    "nsISyncMessageSender");
    35 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
    36     return Services.env.get("EXTERNAL_STORAGE");
    37 });
    39 // This exists to mark the affected code for bug 828858.
    40 const gUseSDCard = true;
    42 const VERBOSE = 1;
    43 let log =
    44   VERBOSE ?
    45   function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
    46   function log_noop(msg) { };
    48 function DirectoryProvider() {
    49 }
    51 DirectoryProvider.prototype = {
    52   classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
    54   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
    55   _xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
    57   _profD: null,
    59   getFile: function dp_getFile(prop, persistent) {
    60 #ifdef MOZ_WIDGET_GONK
    61     let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
    62                       "permissionDBPDir", "UpdRootD"];
    63     if (localProps.indexOf(prop) != -1) {
    64       let file = Cc["@mozilla.org/file/local;1"]
    65                    .createInstance(Ci.nsILocalFile)
    66       file.initWithPath(LOCAL_DIR);
    67       persistent.value = true;
    68       return file;
    69     }
    70     if (prop == "ProfD") {
    71       let dir = Cc["@mozilla.org/file/local;1"]
    72                   .createInstance(Ci.nsILocalFile);
    73       dir.initWithPath(LOCAL_DIR+"/tests/profile");
    74       if (dir.exists()) {
    75         persistent.value = true;
    76         return dir;
    77       }
    78     }
    79     if (prop == "coreAppsDir") {
    80       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
    81       file.initWithPath("/system/b2g");
    82       persistent.value = true;
    83       return file;
    84     }
    85     if (prop == UPDATE_ARCHIVE_DIR) {
    86       // getUpdateDir will set persistent to false since it may toggle between
    87       // /data/local/ and /mnt/sdcard based on free space and/or availability
    88       // of the sdcard.
    89       // before download, check if free space is 2.1 times of update.mar
    90       return this.getUpdateDir(persistent, UPDATES_DIR, 2.1);
    91     }
    92     if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
    93       // getUpdateDir will set persistent to false since it may toggle between
    94       // /data/local/ and /mnt/sdcard based on free space and/or availability
    95       // of the sdcard.
    96       // before apply, check if free space is 1.1 times of update.mar
    97       return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
    98     }
    99 #else
   100     // In desktop builds, coreAppsDir is the same as the profile directory.
   101     // We just need to get the path from the parent, and it is then used to
   102     // build jar:remoteopenfile:// uris.
   103     if (prop == "coreAppsDir") {
   104       let appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
   105       appsDir.append("webapps");
   106       persistent.value = true;
   107       return appsDir;
   108     } else if (prop == "ProfD") {
   109       let inParent = Cc["@mozilla.org/xre/app-info;1"]
   110                        .getService(Ci.nsIXULRuntime)
   111                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
   112       if (inParent) {
   113         // Just bail out to use the default from toolkit.
   114         return null;
   115       }
   116       if (!this._profD) {
   117         this._profD = cpmm.sendSyncMessage("getProfD", {})[0];
   118       }
   119       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
   120       file.initWithPath(this._profD);
   121       persistent.value = true;
   122       return file;
   123     }
   124 #endif
   125     return null;
   126   },
   128   // The VolumeService only exists on the device, and not on desktop
   129   volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
   130     if (!volumePath) {
   131       return false;
   132     }
   133     if (!Services.volumeService) {
   134       return false;
   135     }
   136     let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
   137     if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
   138       return false;
   139     }
   140     let stat = volume.getStats();
   141     if (!stat) {
   142       return false;
   143     }
   144     return requiredSpace <= stat.freeBytes;
   145   },
   147   findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
   148     if (!Services.volumeService) {
   149       return this.createUpdatesDir(LOCAL_DIR, subdir);
   150     }
   152     let activeUpdate = Services.um.activeUpdate;
   153     if (gUseSDCard) {
   154       if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
   155         let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
   156         if (extUpdateDir !== null) {
   157           return extUpdateDir;
   158         }
   159         log("Warning: " + gExtStorage + " has enough free space for update " +
   160             activeUpdate.name + ", but is not writable");
   161       }
   162     }
   164     if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
   165       let localUpdateDir = this.createUpdatesDir(LOCAL_DIR, subdir);
   166       if (localUpdateDir !== null) {
   167         return localUpdateDir;
   168       }
   169       log("Warning: " + LOCAL_DIR + " has enough free space for update " +
   170           activeUpdate.name + ", but is not writable");
   171     }
   173     return null;
   174   },
   176   getUpdateDir: function dp_getUpdateDir(persistent, subdir, multiple) {
   177     let defaultUpdateDir = this.getDefaultUpdateDir();
   178     persistent.value = false;
   180     let activeUpdate = Services.um.activeUpdate;
   181     if (!activeUpdate) {
   182       log("Warning: No active update found, using default update dir: " +
   183           defaultUpdateDir);
   184       return defaultUpdateDir;
   185     }
   187     let selectedPatch = activeUpdate.selectedPatch;
   188     if (!selectedPatch) {
   189       log("Warning: No selected patch, using default update dir: " +
   190           defaultUpdateDir);
   191       return defaultUpdateDir;
   192     }
   194     let requiredSpace = selectedPatch.size * multiple;
   195     let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, subdir);
   196     if (updateDir) {
   197       return updateDir;
   198     }
   200     // If we've gotten this far, there isn't enough free space to download the patch
   201     // on either external storage or /data/local. All we can do is report the
   202     // error and let upstream code handle it more gracefully.
   203     log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
   204         " update " + activeUpdate.name);
   205     activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
   206     return null;
   207   },
   209   createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
   210       let dir = Cc["@mozilla.org/file/local;1"]
   211                    .createInstance(Ci.nsILocalFile);
   212       dir.initWithPath(root);
   213       if (!dir.isWritable()) {
   214         log("Error: " + dir.path + " isn't writable");
   215         return null;
   216       }
   217       dir.appendRelativePath(subdir);
   218       if (dir.exists()) {
   219         if (dir.isDirectory() && dir.isWritable()) {
   220           return dir;
   221         }
   222         // subdir is either a file or isn't writable. In either case we
   223         // can't use it.
   224         log("Error: " + dir.path + " is a file or isn't writable");
   225         return null;
   226       }
   227       // subdir doesn't exist, and the parent is writable, so try to
   228       // create it. This can fail if a file named updates exists.
   229       try {
   230         dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
   231       } catch (e) {
   232         // The create failed for some reason. We can't use it.
   233         log("Error: " + dir.path + " unable to create directory");
   234         return null;
   235       }
   236       return dir;
   237   },
   239   getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
   240     let path = gExtStorage;
   241     if (!path) {
   242       path = LOCAL_DIR;
   243     }
   245     if (Services.volumeService) {
   246       let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
   247       if (!extVolume) {
   248         path = LOCAL_DIR;
   249       }
   250     }
   252     let dir = Cc["@mozilla.org/file/local;1"]
   253                  .createInstance(Ci.nsILocalFile)
   254     dir.initWithPath(path);
   256     if (!dir.exists() && path != LOCAL_DIR) {
   257       // Fallback to LOCAL_DIR if we didn't fallback earlier
   258       dir.initWithPath(LOCAL_DIR);
   260       if (!dir.exists()) {
   261         throw Cr.NS_ERROR_FILE_NOT_FOUND;
   262       }
   263     }
   265     dir.appendRelativePath("updates");
   266     return dir;
   267   }
   268 };
   270 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);

mercurial