forked from mirrors/gecko-dev
		
	 0a08b11eb3
			
		
	
	
		0a08b11eb3
		
	
	
	
	
		
			
			MozReview-Commit-ID: E83Hs7QDlLJ --HG-- extra : rebase_source : e2decd1deef359d695a496d0337b7b31c8fec5f5
		
			
				
	
	
		
			123 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| "use strict";
 | |
| 
 | |
| this.EXPORTED_SYMBOLS = ["AttributionCode"];
 | |
| 
 | |
| const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 | |
| 
 | |
| Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, 'AppConstants',
 | |
|   'resource://gre/modules/AppConstants.jsm');
 | |
| XPCOMUtils.defineLazyModuleGetter(this, 'OS',
 | |
|   'resource://gre/modules/osfile.jsm');
 | |
| XPCOMUtils.defineLazyModuleGetter(this, 'Services',
 | |
|   'resource://gre/modules/Services.jsm');
 | |
| XPCOMUtils.defineLazyModuleGetter(this, 'Task',
 | |
|   'resource://gre/modules/Task.jsm');
 | |
| 
 | |
| const ATTR_CODE_MAX_LENGTH = 200;
 | |
| const ATTR_CODE_KEYS_REGEX = /^source|medium|campaign|content$/;
 | |
| const ATTR_CODE_VALUE_REGEX = /[a-zA-Z0-9_%\\-\\.\\(\\)]*/;
 | |
| const ATTR_CODE_FIELD_SEPARATOR = "%26"; // URL-encoded &
 | |
| const ATTR_CODE_KEY_VALUE_SEPARATOR = "%3D"; // URL-encoded =
 | |
| 
 | |
| let gCachedAttrData = null;
 | |
| 
 | |
| /**
 | |
|  * Returns an nsIFile for the file containing the attribution data.
 | |
|  */
 | |
| function getAttributionFile() {
 | |
|   let file = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
 | |
|   // appinfo does not exist in xpcshell, so we need defaults.
 | |
|   file.append(Services.appinfo.vendor || "mozilla");
 | |
|   file.append(AppConstants.MOZ_APP_NAME);
 | |
|   file.append("postSigningData");
 | |
|   return file;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns an object containing a key-value pair for each piece of attribution
 | |
|  * data included in the passed-in attribution code string.
 | |
|  * If the string isn't a valid attribution code, returns an empty object.
 | |
|  */
 | |
| function parseAttributionCode(code) {
 | |
|   if (code.length > ATTR_CODE_MAX_LENGTH) {
 | |
|     return {};
 | |
|   }
 | |
| 
 | |
|   let isValid = true;
 | |
|   let parsed = {};
 | |
|   for (let param of code.split(ATTR_CODE_FIELD_SEPARATOR)) {
 | |
|     let [key, value] = param.split(ATTR_CODE_KEY_VALUE_SEPARATOR, 2);
 | |
|     if (key && ATTR_CODE_KEYS_REGEX.test(key)) {
 | |
|       if (value && ATTR_CODE_VALUE_REGEX.test(value)) {
 | |
|         parsed[key] = value;
 | |
|       }
 | |
|     } else {
 | |
|       isValid = false;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   return isValid ? parsed : {};
 | |
| }
 | |
| 
 | |
| var AttributionCode = {
 | |
|   /**
 | |
|    * Reads the attribution code, either from disk or a cached version.
 | |
|    * Returns a promise that fulfills with an object containing the parsed
 | |
|    * attribution data if the code could be read and is valid,
 | |
|    * or an empty object otherwise.
 | |
|    */
 | |
|   getAttrDataAsync() {
 | |
|     return Task.spawn(function*() {
 | |
|       if (gCachedAttrData != null) {
 | |
|         return gCachedAttrData;
 | |
|       }
 | |
| 
 | |
|       let code = "";
 | |
|       try {
 | |
|         let bytes = yield OS.File.read(getAttributionFile().path);
 | |
|         let decoder = new TextDecoder();
 | |
|         code = decoder.decode(bytes);
 | |
|       } catch (ex) {
 | |
|         // The attribution file may already have been deleted,
 | |
|         // or it may have never been installed at all;
 | |
|         // failure to open or read it isn't an error.
 | |
|       }
 | |
| 
 | |
|       gCachedAttrData = parseAttributionCode(code);
 | |
|       return gCachedAttrData;
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Deletes the attribution data file.
 | |
|    * Returns a promise that resolves when the file is deleted,
 | |
|    * or if the file couldn't be deleted (the promise is never rejected).
 | |
|    */
 | |
|   deleteFileAsync() {
 | |
|     return Task.spawn(function*() {
 | |
|       try {
 | |
|         yield OS.File.remove(getAttributionFile().path);
 | |
|       } catch (ex) {
 | |
|         // The attribution file may already have been deleted,
 | |
|         // or it may have never been installed at all;
 | |
|         // failure to delete it isn't an error.
 | |
|       }
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Clears the cached attribution code value, if any.
 | |
|    * Does nothing if called from outside of an xpcshell test.
 | |
|    */
 | |
|   _clearCache() {
 | |
|     let env = Cc["@mozilla.org/process/environment;1"]
 | |
|               .getService(Ci.nsIEnvironment);
 | |
|     if (env.exists("XPCSHELL_TEST_PROFILE_DIR")) {
 | |
|       gCachedAttrData = null;
 | |
|     }
 | |
|   },
 | |
| };
 |