├── license.ja.txt ├── locale ├── ja │ ├── messages.properties │ └── messages.dtd └── en-US │ ├── messages.properties │ └── messages.dtd ├── .gitmodules ├── chrome.manifest ├── make.sh ├── make.bat ├── README.md ├── modules ├── locale │ ├── messages.properties.ja │ └── messages.properties.en-US ├── config.js ├── install.js ├── lib │ ├── here.js │ ├── locale.js │ ├── easyTemplate.js │ ├── WindowManager.js │ ├── prefs.js │ ├── KeyboardShortcut.js │ ├── http.js │ ├── config.js │ └── ToolbarItem.js └── main.js ├── Makefile ├── license.txt ├── install.rdf ├── content └── config.xul ├── bootstrap.js └── components └── loader.js /license.ja.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piroor/restartless/HEAD/license.ja.txt -------------------------------------------------------------------------------- /locale/ja/messages.properties: -------------------------------------------------------------------------------- 1 | config.labelFromProperties=Propertiesファイルで定義されたラベル 2 | -------------------------------------------------------------------------------- /locale/en-US/messages.properties: -------------------------------------------------------------------------------- 1 | config.labelFromProperties=Label defined in a properties file 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "makexpi"] 2 | path = makexpi 3 | url = https://github.com/piroor/makexpi.git 4 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | content restartless content/ 2 | locale restartless en-US locale/en-US/ 3 | locale restartless ja locale/ja/ 4 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | appname=restartless 4 | 5 | cp makexpi/makexpi.sh ./ 6 | ./makexpi.sh -n $appname -o 7 | rm ./makexpi.sh 8 | 9 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | setlocal 2 | set appname=restartless 3 | 4 | copy makexpi\makexpi.sh .\ 5 | bash makexpi.sh -n %appname% -o 6 | del makexpi.sh 7 | endlocal 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # restartless 2 | Restartless Addon, A template for restartless addons, for Firefox older than its version 57. 3 | 4 | This project is obsolete and not maintained anymore. 5 | -------------------------------------------------------------------------------- /modules/locale/messages.properties.ja: -------------------------------------------------------------------------------- 1 | message=ローカライズ済みのメッセージです。 2 | 3 | config.title=設定ダイアログ 4 | config.general=全般 5 | config.testBoolean=真偽値の設定 6 | config.appearance=外観 7 | config.testInteger=整数値の設定 8 | 9 | -------------------------------------------------------------------------------- /locale/ja/messages.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /modules/config.js: -------------------------------------------------------------------------------- 1 | var config = require('lib/config'); 2 | config.setDefault('extensions.restartless@piro.sakura.ne.jp.testBoolean', true); 3 | config.setDefault('extensions.restartless@piro.sakura.ne.jp.testInteger', 10); 4 | -------------------------------------------------------------------------------- /modules/locale/messages.properties.en-US: -------------------------------------------------------------------------------- 1 | message=This is a localized message. 2 | 3 | config.title=Configuration Dialog 4 | config.general=General 5 | config.testBoolean=This is a boolean setting. 6 | config.appearance=Appearance 7 | config.testInteger=This is an integer. 8 | 9 | -------------------------------------------------------------------------------- /locale/en-US/messages.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME = restartless 2 | 3 | .PHONY: all xpi signed clean 4 | 5 | all: xpi 6 | 7 | xpi: makexpi/makexpi.sh 8 | makexpi/makexpi.sh -n $(PACKAGE_NAME) -o 9 | 10 | makexpi/makexpi.sh: 11 | git submodule update --init 12 | 13 | signed: xpi 14 | makexpi/sign_xpi.sh -k $(JWT_KEY) -s $(JWT_SECRET) -p ./$(PACKAGE_NAME)_noupdate.xpi 15 | 16 | clean: 17 | rm $(PACKAGE_NAME).xpi $(PACKAGE_NAME)_noupdate.xpi sha1hash.txt 18 | -------------------------------------------------------------------------------- /modules/install.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Installation module for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 1 5 | * 6 | * @license 7 | * The MIT License, Copyright (c) 2011 YUKI "Piro" Hiroshi. 8 | * https://github.com/piroor/restartless/blob/master/license.txt 9 | * @url http://github.com/piroor/restartless 10 | */ 11 | 12 | dump('install.js loaded\n'); 13 | 14 | function install() 15 | { 16 | dump('install!\n'); 17 | } 18 | 19 | function uninstall() 20 | { 21 | dump('uninstall!\n'); 22 | } 23 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010 YUKI "Piro" Hiroshi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /modules/lib/here.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Here-document module for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 3 5 | * @description Inspired from https://github.com/cho45/node-here.js 6 | * 7 | * @license 8 | * The MIT License, Copyright (c) 2012 YUKI "Piro" Hiroshi. 9 | * https://github.com/piroor/restartless/blob/master/license.txt 10 | * @url http://github.com/piroor/restartless 11 | */ 12 | 13 | var EXPORTED_SYMBOLS = ['here']; 14 | 15 | var cache = {}; 16 | 17 | function here() { 18 | var caller = Components.stack.caller; 19 | var filename = caller.filename.split(' -> ').slice(-1)[0]; 20 | var line = caller.lineNumber-1; 21 | var key = filename + ':' + line; 22 | if (key in cache) return cache[key]; 23 | 24 | var source = read(filename); 25 | var part = source.split(/\r?\n/).slice(line).join('\n'); 26 | part = part.replace(/.*\bhere\([^\/]*\/\*/, ''); 27 | part = part.split('*/')[0]; 28 | cache[key] = part; 29 | return part; 30 | } 31 | 32 | function shutdown() { 33 | cache = undefined; 34 | } 35 | 36 | if (typeof read == 'undefined') { 37 | var Cc = Components.classes; 38 | var Ci = Components.interfaces; 39 | var IOService = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService); 40 | 41 | read = function read(aURI) { 42 | var uri = IOService.newURI(aURI, null, null); 43 | var channel = IOService.newChannelFromURI(uri); 44 | var stream = channel.open(); 45 | 46 | var fileContents = null; 47 | try { 48 | var scriptableStream = Cc['@mozilla.org/scriptableinputstream;1'] 49 | .createInstance(Ci.nsIScriptableInputStream); 50 | scriptableStream.init(stream); 51 | fileContents = scriptableStream.read(scriptableStream.available()); 52 | scriptableStream.close(); 53 | } 54 | finally { 55 | stream.close(); 56 | } 57 | 58 | return fileContents; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /install.rdf: -------------------------------------------------------------------------------- 1 | 2 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 43 | 44 | 45 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /content/config.xul: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 17 | 18 | 20 | 21 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 49 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | -------------------------------------------------------------------------------- /modules/lib/locale.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Locale module for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 7 5 | * 6 | * @license 7 | * The MIT License, Copyright (c) 2010-2013 YUKI "Piro" Hiroshi. 8 | * https://github.com/piroor/restartless/blob/master/license.txt 9 | * @url http://github.com/piroor/restartless 10 | */ 11 | 12 | var EXPORTED_SYMBOLS = ['locale']; 13 | 14 | var DEFAULT_LOCALE = 'en-US'; 15 | 16 | var gCache = {} 17 | var get = function(aPath, aBaseURI) { 18 | if (/^\w+:/.test(aPath)) 19 | aBaseURI = aPath; 20 | 21 | var uri = aPath; 22 | if (!/^chrome:\/\/[^\/]+\/locale\//.test(uri)) { 23 | let locale = DEFAULT_LOCALE; 24 | try { 25 | let prefs = Cc['@mozilla.org/preferences;1'].getService(Ci.nsIPrefBranch); 26 | locale = prefs.getCharPref('general.useragent.locale'); 27 | if (/\w+:/.test(locale)) 28 | locale = prefs.getComplexValue('general.useragent.locale', Ci.nsIPrefLocalizedString).data; 29 | locale = locale || DEFAULT_LOCALE; 30 | } 31 | catch(e) { 32 | dump(e+'\n'); 33 | } 34 | [ 35 | aPath+'.'+locale, 36 | aPath+'.'+(locale.split('-')[0]), 37 | aPath+'.'+DEFAULT_LOCALE, 38 | aPath+'.'+(DEFAULT_LOCALE.split('-')[0]) 39 | ].some(function(aURI) { 40 | let resolved = exists(aURI, aBaseURI); 41 | if (resolved) { 42 | uri = resolved; 43 | return true; 44 | } 45 | return false; 46 | }); 47 | } 48 | 49 | if (!(uri in gCache)) { 50 | gCache[uri] = new StringBundle(uri); 51 | } 52 | return gCache[uri]; 53 | }; 54 | exports.get = get; 55 | 56 | var locale = { 'get' : get }; 57 | 58 | const Service = Cc['@mozilla.org/intl/stringbundle;1'] 59 | .getService(Ci.nsIStringBundleService); 60 | 61 | function StringBundle(aURI) 62 | { 63 | this._bundle = Service.createBundle(aURI); 64 | } 65 | StringBundle.prototype = { 66 | getString : function(aKey) { 67 | try { 68 | return this._bundle.GetStringFromName(aKey); 69 | } 70 | catch(e) { 71 | Cu.reportError(new Error('locale.js: failed to call GetStringFromName() with: ' + aKey + '\n' + e)); 72 | } 73 | return ''; 74 | }, 75 | getFormattedString : function(aKey, aArray) { 76 | try { 77 | return this._bundle.formatStringFromName(aKey, aArray, aArray.length); 78 | } 79 | catch(e) { 80 | Cu.reportError(new Error('locale.js: failed to call formatStringFromName() with: ' + JSON.stringify({ key: aKey, args: aArray }) + '\n' + e)); 81 | Cu.reportError(e); 82 | } 83 | return ''; 84 | }, 85 | get strings() { 86 | return this._bundle.getSimpleEnumeration(); 87 | } 88 | }; 89 | 90 | /** A handler for bootstrap.js */ 91 | function shutdown() 92 | { 93 | gCache = {}; 94 | Service.flushBundles(); 95 | } 96 | -------------------------------------------------------------------------------- /modules/lib/easyTemplate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Easy template module for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 5 5 | * 6 | * @license 7 | * The MIT License, Copyright (c) 2012-2016 YUKI "Piro" Hiroshi. 8 | * https://github.com/piroor/restartless/blob/master/license.txt 9 | * @url http://github.com/piroor/restartless 10 | */ 11 | 12 | if (typeof window == 'undefined' || 13 | (window && typeof window.constructor == 'function')) { 14 | this.EXPORTED_SYMBOLS = ['easyTemplate']; 15 | 16 | /** A handler for bootstrap.js */ 17 | function shutdown() { 18 | easyTemplate = undefined; 19 | } 20 | } 21 | 22 | var easyTemplate = { 23 | apply : function() { 24 | if (arguments.length) 25 | return this._applyToString.apply(this, arguments); 26 | else 27 | return this._applyToDocument(); 28 | }, 29 | 30 | _systemPrincipal : Components.classes['@mozilla.org/systemprincipal;1'] 31 | .createInstance(Components.interfaces.nsIPrincipal), 32 | _documentPrincipal : (function() { 33 | return (typeof window != 'undefined' && window && window.document) ? 34 | window.document.nodePrincipal : null; 35 | })(), 36 | 37 | _applyToString : function(aString, aNamespace) { 38 | var sandbox = new Components.utils.Sandbox( 39 | this._documentPrincipal || this._systemPrincipal, 40 | { sandboxPrototype: aNamespace } 41 | ); 42 | return aString.split('}}') 43 | .map(function(aPart) { 44 | let [string, code] = aPart.split('{{'); 45 | return string + (code && Components.utils.evalInSandbox(code, sandbox) || ''); 46 | }) 47 | .join(''); 48 | }, 49 | 50 | _applyToDocument : function() { 51 | var sandbox = new Components.utils.Sandbox( 52 | this._documentPrincipal, 53 | { sandboxPrototype: window } 54 | ); 55 | 56 | for (let aBundle of document.querySelectorAll('stringbundle')) 57 | { 58 | if (aBundle.id) 59 | sandbox[aBundle.id] = aBundle; 60 | } 61 | 62 | ['title', 'label', 'value'].forEach(function(aAttribute) { 63 | var selector = '*[' + aAttribute + '^="{{"][' + aAttribute + '$="}}"]'; 64 | var anonymousRoot = document.getAnonymousNodes(document.documentElement)[0]; 65 | [...document.querySelectorAll(selector)] 66 | .concat([...anonymousRoot.querySelectorAll(selector)]) 67 | .forEach(function(aNode) { 68 | var definition = aNode.getAttribute(aAttribute); 69 | definition = definition.replace(/^\{\{|\}\}$/g, ''); 70 | var label = Components.utils.evalInSandbox(definition, sandbox); 71 | aNode.setAttribute(aAttribute, label); 72 | }); 73 | }); 74 | 75 | var textNodes = document.evaluate( 76 | 'descendant::text()[starts-with(self::text(), "{{")]', 77 | document, 78 | null, 79 | Components.interfaces.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE, 80 | null 81 | ); 82 | for (var i = textNodes.snapshotLength-1; i > -1; i--) { 83 | let node = textNodes.snapshotItem(i); 84 | let definition = node.nodeValue; 85 | if (/\}\}$/.test(definition)) { // because "ends-with()" is not available yet 86 | definition = definition.replace(/^\{\{|\}\}$/g, ''); 87 | node.nodeValue = Components.utils.evalInSandbox(definition, sandbox); 88 | } 89 | } 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Bootstrap code for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 3 5 | * 6 | * @description 7 | * This provides ability to load a script file placed to "modules/main.js". 8 | * Functions named "shutdown", defined in main.js and any loaded script 9 | * will be called when the addon is disabled or uninstalled (include 10 | * updating). 11 | * 12 | * @license 13 | * The MIT License, Copyright (c) 2010-2012 YUKI "Piro" Hiroshi. 14 | * https://github.com/piroor/restartless/blob/master/license.txt 15 | * @url http://github.com/piroor/restartless 16 | */ 17 | 18 | var _gLoader; 19 | var _gResourceRegistered = false; 20 | 21 | function _load(aScriptName, aId, aRoot, aReason) 22 | { 23 | const IOService = Components.classes['@mozilla.org/network/io-service;1'] 24 | .getService(Components.interfaces.nsIIOService); 25 | 26 | var resource, loader, script; 27 | if (aRoot.isDirectory()) { 28 | resource = IOService.newFileURI(aRoot); 29 | 30 | loader = aRoot.clone(); 31 | loader.append('components'); 32 | loader.append('loader.js'); 33 | loader = IOService.newFileURI(loader).spec; 34 | 35 | script = aRoot.clone(); 36 | script.append('modules'); 37 | script.append(aScriptName+'.js'); 38 | script = IOService.newFileURI(script).spec; 39 | } 40 | else { 41 | let base = 'jar:'+IOService.newFileURI(aRoot).spec+'!/'; 42 | loader = base + 'components/loader.js'; 43 | script = base + 'modules/'+aScriptName+'.js'; 44 | resource = IOService.newURI(base, null, null); 45 | } 46 | 47 | if (!_gLoader) { 48 | _gLoader = {}; 49 | Components.classes['@mozilla.org/moz/jssubscript-loader;1'] 50 | .getService(Components.interfaces.mozIJSSubScriptLoader) 51 | .loadSubScript(loader, _gLoader); 52 | } 53 | 54 | if (!_gLoader.exists('modules/'+aScriptName+'.js', resource.spec)) 55 | return; 56 | 57 | if (!_gResourceRegistered) { 58 | _gLoader.registerResource(aId.split('@')[0]+'-resources', resource); 59 | _gResourceRegistered = true; 60 | } 61 | _gLoader.load(script); 62 | } 63 | 64 | function _reasonToString(aReason) 65 | { 66 | switch (aReason) 67 | { 68 | case APP_STARTUP: return 'APP_STARTUP'; 69 | case APP_SHUTDOWN: return 'APP_SHUTDOWN'; 70 | case ADDON_ENABLE: return 'ADDON_ENABLE'; 71 | case ADDON_DISABLE: return 'ADDON_DISABLE'; 72 | case ADDON_INSTALL: return 'ADDON_INSTALL'; 73 | case ADDON_UNINSTALL: return 'ADDON_UNINSTALL'; 74 | case ADDON_UPGRADE: return 'ADDON_UPGRADE'; 75 | case ADDON_DOWNGRADE: return 'ADDON_DOWNGRADE'; 76 | } 77 | return aReason; 78 | } 79 | 80 | function _free() 81 | { 82 | _gLoader = 83 | _load = 84 | _reasonToString = 85 | _free = 86 | _gResourceRegistered = 87 | install = 88 | uninstall = 89 | startup = 90 | shoutdown = 91 | undefined; 92 | } 93 | 94 | /** 95 | * handlers for bootstrap 96 | */ 97 | 98 | function install(aData, aReason) 99 | { 100 | _load('install', aData.id, aData.installPath, _reasonToString(aReason)); 101 | _gLoader.install(_reasonToString(aReason)); 102 | } 103 | 104 | function startup(aData, aReason) 105 | { 106 | _load('main', aData.id, aData.installPath, _reasonToString(aReason)); 107 | } 108 | 109 | function shutdown(aData, aReason) 110 | { 111 | if (!_gLoader) return; 112 | if (_gResourceRegistered) { 113 | _gLoader.unregisterResource(aData.id.split('@')[0]+'-resources'); 114 | } 115 | _gLoader.shutdown(_reasonToString(aReason)); 116 | _free(); 117 | } 118 | 119 | function uninstall(aData, aReason) 120 | { 121 | if (!_gLoader) { 122 | _load('install', aData.id, aData.installPath, _reasonToString(aReason)); 123 | } 124 | _gLoader.uninstall(_reasonToString(aReason)); 125 | if (_gResourceRegistered) { 126 | _gLoader.unregisterResource(aData.id.split('@')[0]+'-resources'); 127 | } 128 | _free(); 129 | } 130 | -------------------------------------------------------------------------------- /modules/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Main module for restartless addons 3 | * @author YUKI "Piro" Hiroshi 4 | * @version 4 5 | * 6 | * @license 7 | * The MIT License, Copyright (c) 2015 YUKI "Piro" Hiroshi. 8 | * https://github.com/piroor/restartless/blob/master/license.txt 9 | * @url http://github.com/piroor/restartless 10 | */ 11 | 12 | dump('main.js loaded\n'); 13 | 14 | /** 15 | * load() works like Components.utils.import(). EXPORTED_SYMBOLS 16 | * in loaded scripts are exported to the global object of this script. 17 | */ 18 | load('lib/WindowManager'); 19 | load('lib/ToolbarItem'); 20 | load('lib/KeyboardShortcut'); 21 | load('lib/here'); 22 | load('lib/easyTemplate'); 23 | load('lib/prefs'); 24 | // this.import() also available instead of load(), as an alias. 25 | // Note: don't use simply "import()" without the prefix "this.", 26 | // because the keyword "import" will be a reserved word in future. 27 | // https://developer.mozilla.org/en/JavaScript/Strict_mode#Paving_the_way_for_future_ECMAScript_versions 28 | this.import('config'); 29 | 30 | var http = require('lib/http'); 31 | 32 | /** 33 | * Localized messages sample. 34 | */ 35 | var bundle = require('lib/locale') 36 | .get(location.href+'/../locale/messages.properties'); 37 | dump(bundle.getString('message')+'\n'); 38 | 39 | 40 | /** 41 | * Preferences example 42 | */ 43 | var myPrefs = prefs.createStore('extensions.restartless@piro.sakura.ne.jp.'); 44 | // property name, default value, preference key (optional) 45 | myPrefs.define('booleanProp', false, 'testBoolean2'); 46 | myPrefs.define('integerProp', 64, 'testInteger2'); 47 | dump('current boolean value is: '+myPrefs.booleanProp+'\n'); 48 | 49 | 50 | /** 51 | * Sample code for addons around browser windows. 52 | */ 53 | const TYPE_BROWSER = 'navigator:browser'; 54 | 55 | var global = this; 56 | function handleWindow(aWindow) 57 | { 58 | var doc = aWindow.document; 59 | if (doc.documentElement.getAttribute('windowtype') != TYPE_BROWSER) 60 | return; 61 | 62 | /* sample: hello world */ 63 | var range = doc.createRange(); 64 | range.selectNodeContents(doc.documentElement); 65 | range.collapse(false); 66 | 67 | var fragment = range.createContextualFragment(here(/* 68 |