addon-sdk/source/app-extension/bootstrap.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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 // @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
     7 'use strict';
     9 // IMPORTANT: Avoid adding any initialization tasks here, if you need to do
    10 // something before add-on is loaded consider addon/runner module instead!
    12 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
    13         results: Cr, manager: Cm } = Components;
    14 const ioService = Cc['@mozilla.org/network/io-service;1'].
    15                   getService(Ci.nsIIOService);
    16 const resourceHandler = ioService.getProtocolHandler('resource').
    17                         QueryInterface(Ci.nsIResProtocolHandler);
    18 const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
    19 const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
    20                      getService(Ci.mozIJSSubScriptLoader);
    21 const prefService = Cc['@mozilla.org/preferences-service;1'].
    22                     getService(Ci.nsIPrefService).
    23                     QueryInterface(Ci.nsIPrefBranch);
    24 const appInfo = Cc["@mozilla.org/xre/app-info;1"].
    25                 getService(Ci.nsIXULAppInfo);
    26 const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
    27            getService(Ci.nsIVersionComparator);
    30 const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
    31                  'install', 'uninstall', 'upgrade', 'downgrade' ];
    33 const bind = Function.call.bind(Function.bind);
    35 let loader = null;
    36 let unload = null;
    37 let cuddlefishSandbox = null;
    38 let nukeTimer = null;
    40 let resourceDomains = [];
    41 function setResourceSubstitution(domain, uri) {
    42   resourceDomains.push(domain);
    43   resourceHandler.setSubstitution(domain, uri);
    44 }
    46 // Utility function that synchronously reads local resource from the given
    47 // `uri` and returns content string.
    48 function readURI(uri) {
    49   let ioservice = Cc['@mozilla.org/network/io-service;1'].
    50     getService(Ci.nsIIOService);
    51   let channel = ioservice.newChannel(uri, 'UTF-8', null);
    52   let stream = channel.open();
    54   let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
    55     createInstance(Ci.nsIConverterInputStream);
    56   cstream.init(stream, 'UTF-8', 0, 0);
    58   let str = {};
    59   let data = '';
    60   let read = 0;
    61   do {
    62     read = cstream.readString(0xffffffff, str);
    63     data += str.value;
    64   } while (read != 0);
    66   cstream.close();
    68   return data;
    69 }
    71 // We don't do anything on install & uninstall yet, but in a future
    72 // we should allow add-ons to cleanup after uninstall.
    73 function install(data, reason) {}
    74 function uninstall(data, reason) {}
    76 function startup(data, reasonCode) {
    77   try {
    78     let reason = REASON[reasonCode];
    79     // URI for the root of the XPI file.
    80     // 'jar:' URI if the addon is packed, 'file:' URI otherwise.
    81     // (Used by l10n module in order to fetch `locale` folder)
    82     let rootURI = data.resourceURI.spec;
    84     // TODO: Maybe we should perform read harness-options.json asynchronously,
    85     // since we can't do anything until 'sessionstore-windows-restored' anyway.
    86     let options = JSON.parse(readURI(rootURI + './harness-options.json'));
    88     let id = options.jetpackID;
    89     let name = options.name;
    91     // Clean the metadata
    92     options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
    94     // freeze the permissionss
    95     Object.freeze(options.metadata[name]['permissions']);
    96     // freeze the metadata
    97     Object.freeze(options.metadata[name]);
    99     // Register a new resource 'domain' for this addon which is mapping to
   100     // XPI's `resources` folder.
   101     // Generate the domain name by using jetpack ID, which is the extension ID
   102     // by stripping common characters that doesn't work as a domain name:
   103     let uuidRe =
   104       /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
   106     let domain = id.
   107       toLowerCase().
   108       replace(/@/g, '-at-').
   109       replace(/\./g, '-dot-').
   110       replace(uuidRe, '$1');
   112     let prefixURI = 'resource://' + domain + '/';
   113     let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
   114     setResourceSubstitution(domain, resourcesURI);
   116     // Create path to URLs mapping supported by loader.
   117     let paths = {
   118       // Relative modules resolve to add-on package lib
   119       './': prefixURI + name + '/lib/',
   120       './tests/': prefixURI + name + '/tests/',
   121       '': 'resource://gre/modules/commonjs/'
   122     };
   124     // Maps addon lib and tests ressource folders for each package
   125     paths = Object.keys(options.metadata).reduce(function(result, name) {
   126       result[name + '/'] = prefixURI + name + '/lib/'
   127       result[name + '/tests/'] = prefixURI + name + '/tests/'
   128       return result;
   129     }, paths);
   131     // We need to map tests folder when we run sdk tests whose package name
   132     // is stripped
   133     if (name == 'addon-sdk')
   134       paths['tests/'] = prefixURI + name + '/tests/';
   136     let useBundledSDK = options['force-use-bundled-sdk'];
   137     if (!useBundledSDK) {
   138       try {
   139         useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
   140       }
   141       catch (e) {
   142         // Pref doesn't exist, allow using Firefox shipped SDK
   143       }
   144     }
   146     // Starting with Firefox 21.0a1, we start using modules shipped into firefox
   147     // Still allow using modules from the xpi if the manifest tell us to do so.
   148     // And only try to look for sdk modules in xpi if the xpi actually ship them
   149     if (options['is-sdk-bundled'] &&
   150         (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
   151       // Maps sdk module folders to their resource folder
   152       paths[''] = prefixURI + 'addon-sdk/lib/';
   153       // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
   154       // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
   155       // until we no longer support SDK modules in XPI:
   156       paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
   157     }
   159     // Retrieve list of module folder overloads based on preferences in order to
   160     // eventually used a local modules instead of files shipped into Firefox.
   161     let branch = prefService.getBranch('extensions.modules.' + id + '.path');
   162     paths = branch.getChildList('', {}).reduce(function (result, name) {
   163       // Allows overloading of any sub folder by replacing . by / in pref name
   164       let path = name.substr(1).split('.').join('/');
   165       // Only accept overloading folder by ensuring always ending with `/`
   166       if (path) path += '/';
   167       let fileURI = branch.getCharPref(name);
   169       // On mobile, file URI has to end with a `/` otherwise, setSubstitution
   170       // takes the parent folder instead.
   171       if (fileURI[fileURI.length-1] !== '/')
   172         fileURI += '/';
   174       // Maps the given file:// URI to a resource:// in order to avoid various
   175       // failure that happens with file:// URI and be close to production env
   176       let resourcesURI = ioService.newURI(fileURI, null, null);
   177       let resName = 'extensions.modules.' + domain + '.commonjs.path' + name;
   178       setResourceSubstitution(resName, resourcesURI);
   180       result[path] = 'resource://' + resName + '/';
   181       return result;
   182     }, paths);
   184     // Make version 2 of the manifest
   185     let manifest = options.manifest;
   187     // Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
   188     let cuddlefishPath = 'loader/cuddlefish.js';
   189     let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath;
   190     if (paths['sdk/']) { // sdk folder has been overloaded
   191                          // (from pref, or cuddlefish is still in the xpi)
   192       cuddlefishURI = paths['sdk/'] + cuddlefishPath;
   193     }
   194     else if (paths['']) { // root modules folder has been overloaded
   195       cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath;
   196     }
   198     cuddlefishSandbox = loadSandbox(cuddlefishURI);
   199     let cuddlefish = cuddlefishSandbox.exports;
   201     // Normalize `options.mainPath` so that it looks like one that will come
   202     // in a new version of linker.
   203     let main = options.mainPath;
   205     unload = cuddlefish.unload;
   206     loader = cuddlefish.Loader({
   207       paths: paths,
   208       // modules manifest.
   209       manifest: manifest,
   211       // Add-on ID used by different APIs as a unique identifier.
   212       id: id,
   213       // Add-on name.
   214       name: name,
   215       // Add-on version.
   216       version: options.metadata[name].version,
   217       // Add-on package descriptor.
   218       metadata: options.metadata[name],
   219       // Add-on load reason.
   220       loadReason: reason,
   222       prefixURI: prefixURI,
   223       // Add-on URI.
   224       rootURI: rootURI,
   225       // options used by system module.
   226       // File to write 'OK' or 'FAIL' (exit code emulation).
   227       resultFile: options.resultFile,
   228       // Arguments passed as --static-args
   229       staticArgs: options.staticArgs,
   230       // Add-on preferences branch name
   231       preferencesBranch: options.preferencesBranch,
   233       // Arguments related to test runner.
   234       modules: {
   235         '@test/options': {
   236           allTestModules: options.allTestModules,
   237           iterations: options.iterations,
   238           filter: options.filter,
   239           profileMemory: options.profileMemory,
   240           stopOnError: options.stopOnError,
   241           verbose: options.verbose,
   242           parseable: options.parseable,
   243           checkMemory: options.check_memory,
   244         }
   245       }
   246     });
   248     let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
   249     let require = cuddlefish.Require(loader, module);
   251     require('sdk/addon/runner').startup(reason, {
   252       loader: loader,
   253       main: main,
   254       prefsURI: rootURI + 'defaults/preferences/prefs.js'
   255     });
   256   } catch (error) {
   257     dump('Bootstrap error: ' +
   258          (error.message ? error.message : String(error)) + '\n' +
   259          (error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
   260     throw error;
   261   }
   262 };
   264 function loadSandbox(uri) {
   265   let proto = {
   266     sandboxPrototype: {
   267       loadSandbox: loadSandbox,
   268       ChromeWorker: ChromeWorker
   269     }
   270   };
   271   let sandbox = Cu.Sandbox(systemPrincipal, proto);
   272   // Create a fake commonjs environnement just to enable loading loader.js
   273   // correctly
   274   sandbox.exports = {};
   275   sandbox.module = { uri: uri, exports: sandbox.exports };
   276   sandbox.require = function (id) {
   277     if (id !== "chrome")
   278       throw new Error("Bootstrap sandbox `require` method isn't implemented.");
   280     return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
   281       CC: bind(CC, Components), components: Components,
   282       ChromeWorker: ChromeWorker });
   283   };
   284   scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
   285   return sandbox;
   286 }
   288 function unloadSandbox(sandbox) {
   289   if ("nukeSandbox" in Cu)
   290     Cu.nukeSandbox(sandbox);
   291 }
   293 function setTimeout(callback, delay) {
   294   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   295   timer.initWithCallback({ notify: callback }, delay,
   296                          Ci.nsITimer.TYPE_ONE_SHOT);
   297   return timer;
   298 }
   300 function shutdown(data, reasonCode) {
   301   let reason = REASON[reasonCode];
   302   if (loader) {
   303     unload(loader, reason);
   304     unload = null;
   306     // Don't waste time cleaning up if the application is shutting down
   307     if (reason != "shutdown") {
   308       // Avoid leaking all modules when something goes wrong with one particular
   309       // module. Do not clean it up immediatly in order to allow executing some
   310       // actions on addon disabling.
   311       // We need to keep a reference to the timer, otherwise it is collected
   312       // and won't ever fire.
   313       nukeTimer = setTimeout(nukeModules, 1000);
   315       // Bug 944951 - bootstrap.js must remove the added resource: URIs on unload
   316       resourceDomains.forEach(domain => {
   317         resourceHandler.setSubstitution(domain, null);
   318       })
   319     }
   320   }
   321 };
   323 function nukeModules() {
   324   nukeTimer = null;
   325   // module objects store `exports` which comes from sandboxes
   326   // We should avoid keeping link to these object to avoid leaking sandboxes
   327   for (let key in loader.modules) {
   328     delete loader.modules[key];
   329   }
   330   // Direct links to sandboxes should be removed too
   331   for (let key in loader.sandboxes) {
   332     let sandbox = loader.sandboxes[key];
   333     delete loader.sandboxes[key];
   334     // Bug 775067: From FF17 we can kill all CCW from a given sandbox
   335     unloadSandbox(sandbox);
   336   }
   337   loader = null;
   339   // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
   340   // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
   341   // the addon is unload.
   343   unloadSandbox(cuddlefishSandbox.loaderSandbox);
   344   unloadSandbox(cuddlefishSandbox.xulappSandbox);
   346   // Bug 764840: We need to unload cuddlefish otherwise it will stay alive
   347   // and keep a reference to this compartment.
   348   unloadSandbox(cuddlefishSandbox);
   349   cuddlefishSandbox = null;
   350 }

mercurial