Sat, 03 Jan 2015 20:18:00 +0100
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 }