/**
* @module main
* @overview The entry point into the addon.
* The "main" module has access to all the high-level APIs
* for interacting with the browser.
* This module does not implement any algorithm logic itself,
* but merely extracts parameters from,
* and saves parameters to, the browser environment.
* It communicates with the main addon logic using
* message passing.
*
* @author Manjul Apratim (manjul.apratim@gmail.com)
* @date Sep 07, 2014
*
* @license GNU General Public License v3 or Later
* @copyright Manjul Apratim, 2014, 2015
*/
"use strict";
// ========================================================================
// GLOBAL CONSTANTS
/**
* @namespace
* @summary A global namespace for miscellaneous "environment" variables.
*/
var ENV = {
/**
* @summary The actionable events that could be triggered when
* interacting with the UI.
* @enum {string}
*/
events : {
DONE : "done",
GENERATE_SALT_KEY : "generateSaltKey",
HIDE : "hide",
SET_SALT_KEY : "setSaltKey",
SAVE : "save",
SHOW : "show"
},
/**
* @summary The indentation for "pretty-printing" JSON objects.
*/
indentation : 4,
/**
* @summary A "category" to log with, to identify which component
* the log is coming from.
*/
logCategory : "MAIN: ",
/**
* @summary The mime types recognized by the clipboard.
* @enum {string}
*/
mimeTypes : {
TEXT : "text"
},
/**
* @summary The list of all the user "preferences" stored in the
* browser's preferences system.
*/
preferences : {
defaultIterations : "defaultIterations",
generateSaltKey : "generateSaltKey",
saltKey : "saltKey",
siteAttributesList : "siteAttributesList",
unlockSaltKey : "unlockSaltKey"
}
};
// ========================================================================
// SDK METHODS
var self = require("sdk/self");
var clipboard = require("sdk/clipboard");
var panels = require("sdk/panel");
var tabs = require("sdk/tabs");
var { ToggleButton } = require('sdk/ui/button/toggle');
var simplePrefs = require('sdk/simple-prefs');
var prefs = simplePrefs.prefs;
var prefsService = require('sdk/preferences/service');
// ========================================================================
// TOGGLE BUTTON
/**
* @summary The toggle button, clicking which shows the addon UI
* and emits the "show" event for the addon code to intercept.
* Clicking again will hide the UI and emit the "hide" event
* for the addon code.
*/
var button = ToggleButton({
id : "Gobbledygook",
label : "Generate a password",
icon : {
"16" : "./icon/icon-16.png",
"32" : "./icon/icon-32.png",
"48" : "./icon/icon-48.png",
"64" : "./icon/icon-64.png"
},
onChange : handleChange
});
/**
* @summary Event handler for changing the state of the ToggleButton
* @return {undefined}
*/
function handleChange(state) {
if (state.checked) {
panel.show({
position : button
});
}
}
// ========================================================================
// PANEL
/**
* @summary The addon panel, which hosts the UI of the addon.
* The addon logic is encompassed in the "contentScriptFiles"
* which are known to this panel.
*/
var panel = panels.Panel({
width : 350,
height : 510,
contentURL : self.data.url("gobbledygook.html"),
contentScriptFile : [
self.data.url("keygen.js"),
self.data.url("sjcl/sjcl.js"),
self.data.url("workhorse.js"),
self.data.url("workhorsefunctions.js")
],
contentScriptOptions : {},
onHide : handleHide
});
/**
* @summary Event handler for the "show" event. It obtains the necessary
* parameters from the url and the browser's preference system,
* and emits the "show" event with this payload for the
* addon code to intercept.
* @return {undefined}
*/
panel.on(ENV.events.SHOW, function() {
panel.port.emit(ENV.events.SHOW, {
url : tabs.activeTab.url,
saltKey : prefs[ENV.preferences.saltKey],
defaultIterations : prefs[ENV.preferences.defaultIterations],
encodedAttributesList : prefs[ENV.preferences.siteAttributesList]
});
});
/**
* @summary Event handler for the "hide" event. It hides the addon panel.
* @return {undefined}
*/
function handleHide() {
button.state('window', {checked : false});
}
/**
* @summary Event handler for the "hide" event.
* It emits the "hide" event for the addon code to intercept.
* @return {undefined}
*/
panel.on(ENV.events.HIDE, function() {
panel.port.emit(ENV.events.HIDE);
});
/**
* @summary Event handler for the "done" event received from the
* addon code, in other words, function to "finalize"
* the algorithm.
* It has two primary responsibilities:
* a) Copy the generated proxy password to the system clipboard
* for readily pasting into the target password field,
* b) Save overridden attributes, if any,
* into the browser's preference system.
* @param {object} doneData - The final payload.
* It has the following properties:
* @prop {string} doneData.password - The generated
* proxy password.
* @prop {string} doneData.attributesListString - The encoded
* list of attributes for all domains (modified with data
* from the current domain if asked to save).
* If this is empty, the existing attributes list string
* in the browser's preference system is left untouched.
* @return {undefined}
*/
panel.port.on(ENV.events.DONE, function (doneData) {
console.debug(ENV.logCategory + "Received 'done'..., doneData=" +
JSON.stringify(doneData, null, ENV.indentation));
// Copy the proxy password to the clipboard.
console.info(ENV.logCategory + "Copying to clipboard...");
clipboard.set(doneData.password, ENV.mimeTypes.TEXT);
// Check if attributes need to be saved into the preference system.
if ("" !== doneData.attributesListString) {
console.info(ENV.logCategory + "Saving site attributes...");
prefs[ENV.preferences.siteAttributesList] =
doneData.attributesListString;
panel.port.emit(ENV.events.SAVE, true);
}
});
// ========================================================================
// SYNC PREFERENCES
// Create a syncable attribute for each of the preferences for the
// addon, so that their values are sync'ed across machines.
var prefKeys = prefsService.keys('extensions.' + self.id);
prefKeys.forEach(function(preferenceName) {
prefsService.set('services.sync.prefs.sync.extensions.' +
self.id + preferenceName,
true);
});
/**
* @summary Event listener for the "Generate Key" button
* in the Options UI.
* It calls out to the 'Workhorse' to do the actual generation,
* but IFF the "editSaltKey" radio button is switched "On"
* (to prevent accidental resets of the key).
* @return {undefined}
*/
function onGenerateSaltKey() {
console.info(ENV.logCategory + "Generating salt 'key'...");
if (prefs[ENV.preferences.unlockSaltKey] !== "N") {
console.info(ENV.logCategory +
"'unlockSaltKey' is unlocked. Proceeding...");
panel.port.emit(ENV.events.GENERATE_SALT_KEY);
} else {
console.info(ENV.logCategory +
"'unlockSaltKey' is locked. Doing nothing.");
}
}
simplePrefs.on(ENV.preferences.generateSaltKey, onGenerateSaltKey);
/**
* @summary Event listener for the "setSaltKey" event
* fired from the Workhorse.
* @return {undefined}
*/
panel.port.on(ENV.events.SET_SALT_KEY, function (key) {
// Set the salt key
prefs[ENV.preferences.saltKey] = key;
// Automatically re-lock the salt key to
// prevent accidental regeneration.
prefs[ENV.preferences.unlockSaltKey] = "N";
});