diff --git a/.gitignore b/.gitignore
index e44912c7..45a71c17 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,10 @@ BASE.*
LOCAL.*
REMOTE.*
build
+*.DS_Store
+*.py
+*.rb
+*.sh
+node_modules
+*.komodoproject
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..da2c7c75
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+# Contributing
+
+## Overview
+
+* Code contributions should follow the [style guide](https://github.com/scriptish/scriptish/wiki/JavaScript-Style-Guide).
+* Tests may be requested, and eventually will be required.
+* All code must be reviewed by a reviewer.
+** Sometimes code will need additional reviews.
+
+## Contributing Code
+
+* Check that your [issue](https://github.com/scriptish/scriptish/issues) does not
+already exist.
+* If it does not yet exist [please create one](https://github.com/scriptish/scriptish/issues/new).
+* Make your changes, preferably in a branch named `bug_number`.
+* Make sure there is a test in the branch (reverse the order of this for TDD).
+* Mention your `branch_name`, with a link to it, in the issue thread.
+* Ask for a review from one our reviewers.
+
+## Reviewers
+
+* [@erikvold](https://github.com/erikvold)
+* [@supahgreg](https://github.com/supahgreg)
+* [@nmaier](https://github.com/nmaier)
diff --git a/LICENSE.txt b/LICENSE.txt
index 91f4f2f9..66dd06ca 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -9,14 +9,13 @@ Scriptish contains code derived from FireBug ( http://getfirebug.com/ ),
reused under the terms of the BSD license; see LICENSE.bsd.
Scriptish also contains code derived from Mozilla projects, including
-Firefox ( http://mozilla.org/ ). This code is reused under the MPL license;
-see the LICENSE.mpl file. All such code is located in the content/third-party/
-directory, and all such files contain the appropriate licensing disclaimers
-and notifications.
+Firefox ( http://mozilla.org/ ). This code is reused under the MPL license.
-The icon used is taken from the PICOL icon library http://www.picol.org/icon_library.php
-This library is released under the Creative Commons-License BY-SA
-http://creativecommons.org/licenses/by-sa/3.0/
+All code located in the content/third-party/, content/js/third-party/,
+modules/third-party/, and skin/third-party/ directories, contain the appropriate
+licensing disclaimers and notifications.
+
+The icon used was created by Slowpoke for Scriptish.
The uso_medium.png icon contained within Scriptish is the property of
UserScripts.org http://userscripts.org and is being used with their consent
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..bc5ebede
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+
+all:
+ sh build.sh amo
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ca69d4de
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Scriptish
+
+[](https://gitter.im/scriptish/scriptish)
+
+## About
+
+Scriptish is a userscript manager for Firefox, forked from Greasemonkey.
+
+## Browsers Supported
+
+* Firefox 21+
+* Seamonkey 2.8+
+
+
+## Download
+
+* [Addons.Mozilla.Org (AMO)](https://addons.mozilla.org/firefox/addon/scriptish)
+* [Source Code](https://github.com/scriptish/scriptish)
+
+## Help
+
+* [Wiki](https://github.com/scriptish/scriptish/wiki)
+* [Issues](https://github.com/scriptish/scriptish/issues)
+* [Blog](http://scriptish.org/blog)
+* irc.mozilla.org #userscripts (for UserScript development help)
+* irc.mozilla.org #scriptish (for Scriptish development)
+* [StackOverflow](http://stackoverflow.com/questions/tagged/userscripts)
+
+## UserScripts
+
+* [Userscripts](http://userscripts.org)
diff --git a/README.txt b/README.txt
deleted file mode 100644
index e62078f3..00000000
--- a/README.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Scriptish is a userscript manager for Firefox, forked from Greasemonkey.
-
-
-Requirements
-----------
-
-* Using Scriptish: Firefox 4.0b5pre or higher
-* Building an XPI: Unix shell
-* Playing w/ code: Git
-
-
-Links
-----------
-
-* Addons.Mozilla.Org (AMO): https://addons.mozilla.org/en-US/firefox/addon/231203
-* Userscripts: http://userscripts.org
-* Wiki: http://github.com/erikvold/scriptish/wiki
-* Issues: http://github.com/erikvold/scriptish/issues
-* Google Group: http://groups.google.com/group/scriptish
-* IRC: irc://irc.freenode.net/scriptish
-* StatusNet http://identi.ca/scriptish
-* Source Code: http://github.com/erikvold/scriptish
diff --git a/assets/generate.svg b/assets/generate.svg
new file mode 100644
index 00000000..8566d1b9
--- /dev/null
+++ b/assets/generate.svg
@@ -0,0 +1,118 @@
+
+
+
+
diff --git a/assets/scriptish.png b/assets/scriptish.png
new file mode 100755
index 00000000..f7752abc
Binary files /dev/null and b/assets/scriptish.png differ
diff --git a/assets/scriptish16.png b/assets/scriptish16.png
new file mode 100755
index 00000000..cd8b9e30
Binary files /dev/null and b/assets/scriptish16.png differ
diff --git a/blocklist.json b/blocklist.json
index 0f4c7489..7c89b839 100644
--- a/blocklist.json
+++ b/blocklist.json
@@ -1,3 +1,3 @@
{
- "uso": [20145, 35611, 38017, 68219]
-}
\ No newline at end of file
+ "uso": [20145, 26062, 35611, 38017, 59744, 68219, 92054, 114457, 128437, 130770, 167885, 169552]
+}
diff --git a/build.sh b/build.sh
index 8041e53b..8782d42a 100755
--- a/build.sh
+++ b/build.sh
@@ -1,9 +1,11 @@
#!/bin/sh
# Set up variables
-if [ "amo" = "$1" ] || [ "staging" = "$1" ]; then
+if [ "amo" = "$1" ]; then
# For official builds, use the version in install.rdf.
VER=`grep -Go 'em:version\>\(.*\)\<' extension/install.rdf | grep -Go '>\(.*\)<' | sed -e 's/[><]*//g'`
+elif [ "test" = "$1" ]; then
+ VER=`echo test`
else
# For beta builds, generate a version number.
VER=`date +"%Y.%m.%d.beta"`
@@ -11,7 +13,9 @@ fi
XPI="scriptish-$VER.xpi"
# Copy base structure to a temporary build directory and change to it
-echo "Creating working directory ..."
+if [ "test" != "$1" ]; then
+ echo "Creating working directory ..."
+fi
rm -rf build
mkdir build
cp LICENSE.txt build/
@@ -33,14 +37,26 @@ fi
(sed -e 's/
-
-
-
-
+
+
+
diff --git a/extension/content/addonstab.xul b/extension/content/addonstab.xul
index 62d7d897..3fc3f1c6 100644
--- a/extension/content/addonstab.xul
+++ b/extension/content/addonstab.xul
@@ -3,6 +3,9 @@
+
+
+
{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
- 4.0b5pre
- 4.*
+ 30.0
+ 33.*
+
{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}
- 2.1a3
- 2.1b3pre
+ 2.27
+ 2.30.*
diff --git a/extension/locale/de/description.properties b/extension/locale/de/description.properties
new file mode 100644
index 00000000..f3deeaef
--- /dev/null
+++ b/extension/locale/de/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish ist eine unabhängige Weiterentwicklung von Greasemonkey und beinhaltet:
+amo.description.line2=Alles, was Greasemoneky kann (was tatsächlich nützlich ist)
+amo.description.line3=Aktualisierungen: Benutzerskripte können von Scriptish mit @updateURL aktualisiert werden
+amo.description.line4=Viele neue Metadaten @Schlüssel
+amo.description.line5=Viele neue Funktionen der GM_ API
+amo.description.line6=Überlegene Leistung: Schnellerer, saubererer Code, der von allem, was aktuelle Firefox-Versionen zu bieten haben, Gebrauch macht
+amo.description.line7=Überlegene Sicherheit: Scriptish beinhaltet Sicherheitsfunktionen, die Sie nirgendwo anders finden
+amo.description.line8=Tests: Die Entwickler benutzen viele Tests um eine korrekte Arbeitsweise sicherzustellen und Fehler schnell zu entdecken
+amo.developerComments.line1=Scriptish beinhaltet alles, was auch Greasemonkey kann, plus viele neue und verbesserte Funktionen. Erfahren Sie mehr im Scriptish-Wiki
+amo.developerComments.line2=Bitte senden Sie alle Fehler oder Funktionsvorschläge an: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=Wenn sie weitere Fragen haben, dann senden Sie diese bitte auf Englisch an die Mailing-Liste: http://groups.google.com/group/scriptish
+amo.summary=Die beste Benutzerskript-Anwendung im Internet
+extensions.scriptish@erikvold.com.description=Ein Skript-Erweiterungsmanager für Firefox
diff --git a/extension/locale/de/scriptish.properties b/extension/locale/de/scriptish.properties
new file mode 100644
index 00000000..4aab8852
--- /dev/null
+++ b/extension/locale/de/scriptish.properties
@@ -0,0 +1,178 @@
+contributions.description=Der Entwickler dieses Benutzerskripts bittet um einen kleinen Beitrag zur Unterstützung der weiteren Entwicklung
+copyDownloadURL=Adresse zum Herunterladen kopieren
+copyDownloadURL.ak=k
+edit=Bearbeiten
+edit.ak=B
+editor.couldNotLaunch=Konnte den Editor nicht starten.
+editor.pleasePickExecutable=Bitte wählen sie eine ausführbare Anwendung zum Bearbeiten von Benutzerskripten aus.
+editor.prompt=Bitte zuerst den bevorzugten Texteditor wählen
+editor.useScratchpad=Wollen Sie Scratchpad zum bearbeiten benutzen?
+editor.useScratchpad.no=Nein (Ich will meinen eigenen nehmen)
+editor.useScratchpad.yes=Ja
+error.api.badArguments=Ungültige oder nicht ausreichende Argumente wurden zur GM_ Funktion übergeben
+error.api.noResourceWithName=Keine Quelle mit dem Namen
+error.api.noSecondArgValue=Zweites Argument nicht angegeben: Wert
+error.api.prefNotFound=Ein ungültiger oder nicht vorhandener vorgegebener Name wurde zu einer GM_ Funktion übergeben.
+error.api.reqURL=Ungültige Adresse
+error.api.reqURL.scheme=Verbotenes Schema (Protokoll) in der Adreese
+error.api.safeHTMLParser.url=Das Adressenargument der GM_safeHTMLParser konnte nicht analysiert werden
+error.api.unsafeAccess=Scriptish Zugriffsverletzung: unsafeWindow darf nicht aufrufen werden
+error.charset=Ungültige Zeichenkodierung angegeben.
+error.dependency.loading=Fehler beim Laden der Anhängigkeit
+error.dependency.local=SecurityException: Anfragen nach lokalen oder Chrome-Adressen sind verboten
+error.dependency.serverReturned=Fehler! Server gab zurück
+error.hash.algorithm=Ungültiger Hash-Algorithmus angegeben.
+error.icon.dataURL=Ungültige Daten: Adreese für @icon
+error.icon.notImage=Fehler! @icon hat keinen Bild MIME-Typ
+error.icon.URL=Ungültige Adresse für @icon
+error.invalidCert=Ungültiges SSL-Zertifikat.\nWenn gewünscht, erlauben Sie alle Zertifikate, indem Sie die Option in den Scriptish-Einstellungen ändern → Erweitert → Aktualisierungssicherheit
+error.isInvalidValue=ist ein ungültiger Wert
+error.matchPattern.rules=@match Muster hält sich nicht an die Regeln für Muster
+error.matchPattern.rules.file=Dateischema @match Muster hält sich nicht an die Regeln für Muster
+error.notSecure=Unsichere Adresse.\nWenn gewünscht, erlauben Sie alle nicht-HTTPS Aktualisierungen, indem Sie die Option in den Scriptish-Einstellungen ändern → Erweitert → Aktualisierungssicherheit
+error.notSupported.Firefox=wird nicht in dieser Version von Firefox unterstützt
+error.openingFile=Datei konnte nicht geöffnet werden
+error.pattern.parsing=Muster konnte nicht verarbeitet werden
+error.pref.type=Nicht unterstützter Einstellungstyp. Gültige Typen sind: string, bool und 32-bit Zahlen.
+error.remoteVersionOlder=Das Fernbedienungsskript hat keine neuere Versionsnummer als das aktuelle Skript.
+error.resource.dupName=ist ein doppelter Quellenname. Jede @resource muss einen eindeutigen Namen tragen.
+error.resource.syntax=Ungültige Syntax für @resource Deklaration
+error.retrieving=Fehler beim Abrufen
+error.script.installing=Fehler bei der Installation des Benutzerskripts
+error.script.loading=Fehler beim Laden des Benutzerskriptes
+greeting.btn.ak=I
+greeting.msg=Dies ist ein Benutzerskript. »Installieren« klicken um es zu benutzen.
+install=Installieren
+install.domains=Seiten:
+install.excludes=Läuft nicht auf:
+install.includes=Läuft auf:
+install.matches=Passend zu:
+install.requires=Benötigt:
+install.resources=Quellen:
+install.grants=Zuwendungen:
+install.showScriptSource=Quelltext anzeigen
+install.title=Benutzerskript Installation
+install.warning1=Bösartige Benutzerskripte können Ihre Daten ausspähen oder ohne Ihre Genehmigung in ihren Namen handeln. Es handelt sich um echte kleine Programme.
+install.warning2=Sie sollten nur Benutzerskripte aus Quellen installieren, denen Sie auch vertrauen.
+installFromFile=Benutzerskript aus Datei installieren …
+installFromFile.ak=D
+menu.commands=Benutzerskriptbefehle …
+menu.commands.ak=B
+menu.install=Benutzerskript installieren …
+menu.install.ak=I
+menu.manage=Benutzerskripte verwalten …
+menu.manage.ak=v
+menu.report=Report An Issue
+menu.report.ak=R
+menu.new=Neues Benutzerskript …
+menu.new.ak=N
+menu.options.ak=O
+menu.show=Quelltext anzeigen …
+menu.show.ak=Q
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=Dieses Benutzerskript installieren
+menuitem.manage=Benutzerskripte verwalten
+menuitem.new=Neues Benutzerskript
+moving.dependency=Abhängigkeitsdatei verschieben von
+moving.script=Benutzerskript verschieben von
+newscript.author=Autor
+newscript.description=Beschreibung
+newscript.excludes=Ausnahmen (eine pro Zeile)
+newscript.exists=Ein Benutzerskript mit diesem Namen existiert bereits.\nÜberschreiben?
+newscript.id=ID
+newscript.includes=Einschlüsse (einer pro Zeile)
+newscript.missing.id=Bitte eine ID angeben.
+newscript.missing.name=Bitte einen Namen angeben. Der Name wird in der Add-ons-Verwaltung angezeigt.
+newscript.name=Name
+newscript.namespace=Namensraum
+newscript.noID=Bitte gültige ID für Ihr Benutzerskript angeben.
+newscript.noName=Bitte Namen für Ihr Benutzerskript angeben.
+newscript.runat=Ausführen wenn
+newscript.runat.documentstart=Dokument beginnt zu laden
+newscript.runat.documentend=Dokument fertig geladen (keine Quellen)
+newscript.runat.documentidle=Dokument ist zunächst im Leerlauf
+newscript.runat.documentcomplete=Dokument fertig geladen (einschließlich Quellen)
+newscript.runat.windowload=Das Fenster incl. aller Medien ist fertig geladen
+newscript.version=Version
+nothing.timedOut=Nichts (Zeitüberschreitung)
+openFolder=Ordner öffnen
+openFolder.ak=O
+openUserScriptsManager=Benutzerskript-Verwaltung öffnen
+openUserScriptsManager.ak=B
+options=Optionen
+options.addonsManager=Add-ons-Verwaltung
+options.alsoUninstallPrefs=Bei der Deinstallation eines Skriptes auch seine Einstellungen entfernen
+options.cache=Zwischenspeicher
+options.cache.enabled=Benutzerskripte zwischenspeichern
+options.changeEditor=Editor ändern
+options.editor=Editor
+options.editor.notset=Kein Editor ausgewählt
+options.enableCopyDownloadURL=Kopieren der Adresse zu herunterladen aktivieren
+options.enableInstallDetection=Installationserkennung aktivieren
+options.enabledSchemes.about=about Protokoll (intern)
+options.enabledSchemes.advanced=Zusätzliche Protokolle
+options.enabledSchemes.chrome=chrome Protokoll (intern)
+options.enabledSchemes.data=data Protokoll
+options.enabledSchemes.file=file Protokoll (Lokale Dateien)
+options.enabledSchemes.ftp=ftp Protokoll
+options.enabledSchemes.http=http/https Protokoll
+options.enabledSchemes.main=Protokolle
+options.enabledSchemes.securityRemark=Die zusätzlichen Protokolle können vollen Systemzugriff haben.
+options.enabledSchemes.securityRemark2=Daher stellen sie ein beträchtliches Sicherheitsrisiko mit nicht vertrauenswürdigen Benutzerskripten dar!
+options.enabledSchemes.unmht=unmht Protokoll (Mozilla-Archivformat)
+options.excludes.desc=Globale Ausnahmen heben Benutzerskript oder Benutzerregeln auf
+options.excludes.empty=Keine globalen Ausnahmen spezifiziert
+options.excludes.remark=(Sie können eine Ausnahme pro Zeile spezifizieren)
+options.grant.sniffing=@grant schnüffeln aktivieren
+options.logChrome=Nicht kritische Erweiterungsnachrichten in der Fehlerkonsole protokollieren
+options.logging=Protokollieren
+options.logToErrorConsole=GM_log benutzt die Fehlerkonsole
+options.notifications=Benachrichtigungen
+options.notifications.popup.enable=Popup-Benachrichtigungen aktivieren
+options.notifications.sliding.enable=gleitenden Benachrichtigungen aktivieren
+options.notifications.whenDisabled=Wenn es deaktiviert ist, wird jede Meldung in der Fehlerkonsole angezeigt.
+options.other=Weitere
+options.pane.advanced=Erweitert
+options.pane.excludes=Globale Ausnahmen
+options.pane.main=Allgemein
+options.pane.ui=Benutzeroberfläche
+options.requireBuiltInCerts=Sichere Aktualisierungen müssen eingebaute Zertifikate nutzen
+options.requireSecured=Benutzerskripte müssen über eine sichere Verbindung (https) aktualisiert werden
+options.sync.desc=Der Firefox-Sync-Service und seine »Einstellungs«-Option müssen aktiviert sein!
+options.sync.ScriptishPrefs=Scriptish Einstellungen
+options.sync.ScriptishPrefs.common=Einstellungen der gemeinsamen Scriptish-Erweiterungen synchronisieren
+options.sync.ScriptishPrefs.editor=Die Editor-Einstellungen synchronisieren
+options.title=Scriptish-Optionen
+options.toolbarbutton=Werkzeugleistenknopf
+options.toolbarbutton.showScripts=Anzahl aktiver Skripte anzeigen
+options.translation=Übersetzung
+options.translation.useEnglish=Immer Englisch benutzen
+options.update.security=Aktualisierungsicherheit
+options.useDownloadURL=Adresse vom herunterladen für Aktualisierungen benutzen, wenn es keine gesonderte Aktualisierungsadresse gibt
+reinstall=Erneut installieren
+saving=Speichern
+scratchpad.saveAsUserScript=Als Benutzerskript speichern
+scratchpad.saveAsUserScript.ak=B
+scriptOptions.disableScriptIncludes=Die Einschlüsse dieses Benutzerskriptes deaktivieren und nur Benutzereinschlüsse verwenden.
+scriptOptions.excludes=Benutzerausnahmen:
+scriptOptions.includes=Benutzereinschlüsse:
+statusbar.enabled=Aktiviert
+statusbar.enabled.ak=K
+statusbar.installed=erfolgreich installiert
+statusbar.modified=geändert
+statusbar.noScripts.excluded=Seite ist ausgeschlossen!
+statusbar.noScripts.notfound=Keine Benutzerskripte für diese Seite!
+statusbar.noScripts.scheme=Protokoll ist deaktiviert!
+statusbar.updated=erfolgreich aktualisiert
+sync=Synchronisieren
+tooltip.loading=Wird geladen …
+Uninstall=Entfernen
+Uninstall.ak=E
+untitledScript=Unbenanntes Skript
+Update=Aktualisieren
+userscript=Benutzerskript
+userscripts=Benutzerskripte
+userscripts.get=Benutzerskripte finden
+userscripts.noneInstalled=Sie haben keine Benutzerskripte installiert
+warning.returnfrommain=»return« aus dem Hauptbereich wird normalerweise nicht unterstützt. Sie sollten dieses im Benutzerskript beheben.
diff --git a/extension/locale/en-US/description.properties b/extension/locale/en-US/description.properties
new file mode 100644
index 00000000..7cb0fe4f
--- /dev/null
+++ b/extension/locale/en-US/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish is a fork of Greasemonkey, offering:
+amo.description.line2=Everything Greasemonkey does (which is actually useful)
+amo.description.line3=Updating: User scripts can be updated in Scriptish using @updateURL
+amo.description.line4=Many new metadata @keys
+amo.description.line5=Many new additions to the GM_ API
+amo.description.line6=Superior Performance: A faster, cleaner code base which takes advantage of all that recent Firefox versions have to offer
+amo.description.line7=Superior Security: Scriptish provides security features you won't find elsewhere
+amo.description.line8=Testing: The developers make use of a lot of tests to ensure the correct functioning of Scriptish and to find any bugs sooner
+amo.developerComments.line1=Scriptish has everything Greasemonkey has, plus many features which you can read about at: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=Please submit any bugs or feature requests to: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=If you have any other questions, please send them to the mailing list: http://groups.google.com/group/scriptish
+amo.summary=The greatest user script engine on the Internet.
+extensions.scriptish@erikvold.com.description=A Script Extension Manager for Firefox
diff --git a/extension/locale/en-US/scriptish.properties b/extension/locale/en-US/scriptish.properties
index 155550a7..d69a11f3 100644
--- a/extension/locale/en-US/scriptish.properties
+++ b/extension/locale/en-US/scriptish.properties
@@ -1,3 +1,4 @@
+contributions.description=The developer of this user script asks that you help support its continued development by making a small contribution.
copyDownloadURL=Copy Download URL
copyDownloadURL.ak=C
edit=Edit
@@ -5,11 +6,16 @@ edit.ak=E
editor.couldNotLaunch=Could not launch editor.
editor.pleasePickExecutable=Please pick an executable application to use for editing user scripts.
editor.prompt=Please choose your preferred text editor first
-error.api.clipboard.type=is not a type that is supported by GM_setClipboard.
+editor.useScratchpad=Do you want to use Scratchpad as your editor?
+editor.useScratchpad.no=No (I will pick my own)
+editor.useScratchpad.yes=Yes
+error.api.badArguments=Invalid or insufficient arguments were passed to a GM_ function.
error.api.noResourceWithName=No resource with name
error.api.noSecondArgValue=Second argument not specified: Value
-error.api.reqURL.scheme=Disallowed scheme in URL
+error.api.prefNotFound=An invalid or non-existent preference name was passed to a GM_ function.
error.api.reqURL=Invalid URL
+error.api.reqURL.scheme=Disallowed scheme in URL
+error.api.safeHTMLParser.url=The "URL" argument of GM_safeHTMLParser could not be parsed
error.api.unsafeAccess=Scriptish access violation: unsafeWindow cannot call
error.charset=Invalid charset specified.
error.dependency.loading=Error loading dependency
@@ -19,40 +25,46 @@ error.hash.algorithm=Invalid hash algorithm specified.
error.icon.dataURL=Invalid data: URL for @icon
error.icon.notImage=Error! @icon does not have an image MIME type
error.icon.URL=Invalid URL for @icon
+error.invalidCert=Invalid SSL certificate.\nIf desired, allow all certificates by enabling the option in Scriptish Options / Advanced / Update Security.
error.isInvalidValue=is an invalid value
-error.matchPattern.rules.file=File scheme @match pattern does not conform to pattern rules
error.matchPattern.rules=@match pattern does not conform to pattern rules
+error.matchPattern.rules.file=File scheme @match pattern does not conform to pattern rules
+error.notSecure=Insecure URI.\nIf desired, allow non-HTTPS updates by enabling the option in Scriptish Options / Advanced / Update Security.
error.notSupported.Firefox=is not supported by this version of Firefox
-error.observerNotFound=Observer not found
error.openingFile=Could not open file
error.pattern.parsing=Pattern could not be parsed
error.pref.type=Unsupported preference type. Valid types are: string, bool, and 32-bit integers.
+error.remoteVersionOlder=The remote script does not have a newer version number than your current script.
error.resource.dupName=is a duplicate resource name. Each @resource must have a unique name.
error.resource.syntax=Invalid syntax for @resource declaration
error.retrieving=Failed to retrieve
error.script.installing=Error installing user script
error.script.loading=Error loading user script
-greeting.btn=Install
greeting.btn.ak=I
greeting.msg=This is a user script. Click install to start using it.
+install=Install
install.domains=domains:
-install.matches=matches:
-install.includes=runs on:
install.excludes=does not run on:
+install.includes=runs on:
+install.matches=matches:
install.requires=requires:
install.resources=resources:
-install.installButton=Install
+install.grants=grants:
install.showScriptSource=Show Script Source
install.title=User Script Installation
install.warning1=Malicious scripts can violate your privacy and act on your behalf without your knowledge.
install.warning2=You should only install scripts from sources that you trust.
-menu.commands=User Script Commands...
+installFromFile=Install User Script From File...
+installFromFile.ak=U
+menu.commands=User Script Commands
menu.commands.ak=C
-menu.install=Install User Script...
+menu.install=Install User Script
menu.install.ak=I
-menu.manage=Manage User Scripts...
+menu.manage=Manage User Scripts
menu.manage.ak=M
-menu.new=New User Script...
+menu.report=Report An Issue
+menu.report.ak=R
+menu.new=New User Script
menu.new.ak=N
menu.options.ak=O
menu.show=View User Script Source
@@ -62,36 +74,106 @@ menu.title.ak=S
menuitem.install=Install This User Script...
menuitem.manage=Manage User Scripts
menuitem.new=New User Script
+moving.dependency=Moving dependency file from
+moving.script=Moving script file from
+newscript.author=Author
newscript.description=Description
newscript.excludes=Excludes (One per line)
newscript.exists=A script with that name is already installed.\nOK to overwrite?
newscript.id=ID
newscript.includes=Includes (One per line)
+newscript.missing.id=Please provide an ID.
+newscript.missing.name=Please provide a name. The name will be shown within the Add-ons Manager.
newscript.name=Name
newscript.namespace=Namespace
newscript.noID=Please provide a valid ID for your script.
newscript.noName=Please provide a name for your script.
+newscript.runat=Run when
+newscript.runat.documentstart=Document starts loading
+newscript.runat.documentend=Document finishes loading (not resources)
+newscript.runat.documentidle=Document is first idle
+newscript.runat.documentcomplete=Document finishes loading (including resources)
+newscript.runat.windowload=Window Loaded (the page has finished rendering)
+newscript.version=Version
+nothing.timedOut=nothing (timed out)
openFolder=Open Folder
openFolder.ak=O
-scriptOptions.includes=User Includes:
-scriptOptions.excludes=User Excludes:
-scriptOptions.disableScriptIncludes=Disable this script's include patterns and only use the user defined ones.
+openUserScriptsManager=Open User Scripts Manager
+openUserScriptsManager.ak=O
options=Options
-options.excludes=Global Excludes
-options.addonManager=Add-on Manager
+options.addonsManager=Add-ons Manager
options.alsoUninstallPrefs=When uninstalling a script, also remove its preferences
+options.cache=Cache
+options.cache.enabled=Cache user scripts
options.changeEditor=Change Editor
options.editor=Editor
+options.editor.notset=No editor selected
options.enableCopyDownloadURL=Enable copying of download URL
+options.enableInstallDetection=Enable install detection
+options.enabledSchemes.about=about protocol (internal)
+options.enabledSchemes.advanced=Additional protocols
+options.enabledSchemes.chrome=chrome protocol (internal)
+options.enabledSchemes.data=data protocol
+options.enabledSchemes.file=file protocol (Local files)
+options.enabledSchemes.ftp=ftp protocol
+options.enabledSchemes.http=http/https protocol
+options.enabledSchemes.main=Protocols
+options.enabledSchemes.securityRemark=The additional protocols might have full system access.
+options.enabledSchemes.securityRemark2=Hence they pose a sizable security risk with untrusted scripts!
+options.enabledSchemes.unmht=unmht protocol (Mozilla Archive format)
+options.excludes.desc=Global excludes will override any script or user specified rules
+options.excludes.empty=No global excludes specified
+options.excludes.remark=(You may specify one exclude per line)
+options.grant.enabled=Enable @grant
+options.grant.sniffing=Enable @grant sniffing
+options.logChrome=Log non-critical extension messages to the Error Console
+options.logging=Logging
+options.logToErrorConsole=GM_log uses the Error Console
+options.notifications=Notifications
+options.notifications.popup.enable=Enable popup notifications
+options.notifications.sliding.enable=Enable sliding notifications
+options.notifications.whenDisabled=When disabled, any notification will appear in the Error Console.
+options.other=Other
+options.pane.advanced=Advanced
+options.pane.excludes=Global Excludes
+options.pane.main=Main
+options.pane.ui=User Interface
+options.requireBuiltInCerts=Secure update requires using built-in certificates
options.requireSecured=Require user scripts to update using HTTPS
+options.sync.desc=The Firefox Sync service and its "Preferences" option must be enabled!
+options.sync.ScriptishPrefs=Scriptish Preferences
+options.sync.ScriptishPrefs.common=Synchronize common Scriptish extension preferences
+options.sync.ScriptishPrefs.editor=Synchronize the Editor preference
+options.title=Scriptish Options
+options.toolbarbutton=Toolbar button
+options.toolbarbutton.showScripts=Show active script count
+options.translation=Translation
+options.translation.useEnglish=Always use English
+options.update.security=Update Security
options.useDownloadURL=Use download URL for updates if there is no update URL
+reinstall=Reinstall
+saving=Saving
+scratchpad.saveAsUserScript=Save As User Script
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=Disable this script's include patterns and only use the user defined ones.
+scriptOptions.excludes=User Excludes:
+scriptOptions.includes=User Includes:
statusbar.enabled=Enabled
statusbar.enabled.ak=E
statusbar.installed=installed successfully
statusbar.modified=modified
-statusbar.noScripts=No scripts for page!
+statusbar.noScripts.excluded=Page is excluded!
+statusbar.noScripts.notfound=No scripts for page!
+statusbar.noScripts.scheme=Scheme is disabled!
statusbar.updated=updated successfully
+sync=Sync
tooltip.loading=Loading...
Uninstall=Uninstall
+Uninstall.ak=U
+untitledScript=Untitled Script
Update=Update
+userscript=User Script
userscripts=User Scripts
+userscripts.get=Get user scripts
+userscripts.noneInstalled=You don't have any user scripts installed
+warning.returnfrommain="return" from the main scope is not generally supported. You should fix that in your user script.
diff --git a/extension/locale/es-ES/description.properties b/extension/locale/es-ES/description.properties
new file mode 100644
index 00000000..a5dd8a01
--- /dev/null
+++ b/extension/locale/es-ES/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish es un derivado de Greasemonkey, que ofrece:
+amo.description.line2=Todo lo que hace Greasemonkey (que de hecho es útil)
+amo.description.line3=Actualización: Los scripts de usuario pueden ser actualizados en Scriptish usando @updateURL
+amo.description.line4=Muchas nuevas metadata @keys
+amo.description.line5=Muchas nuevas caracterÃsticas añadidas a la GM_ API
+amo.description.line6=Rendimiento Superior: Un código base más rápido y limpio, que aprovecha todo lo que las versiones recientes de Firefox ofrecen
+amo.description.line7=Seguridad Superior: Scriptish proporciona caracterÃsticas de seguridad que no encontrará en otro sitio
+amo.description.line8=Testeo: Los desarrolladores realizan muchos tests para asegurar el correcto funcionamiento de Scriptish y encontrar cualquier error cuanto antes
+amo.developerComments.line1=Scriptish tiene todo lo que tiene Greasemonkey, además de otras muchas caracterÃsticas sobre las que puede leer en: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=Por favor remite cualquier error o solicitud de caracterÃstica a: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=Si tiene alguna otra pregunta, por favor envÃela a la lista de correo: http://groups.google.com/group/scriptish
+amo.summary=El motor de scripts de usuario más avanzado en Internet.
+extensions.scriptish@erikvold.com.description=Un gestor de scripts de usuario para aplicaciones Mozilla
diff --git a/extension/locale/es-ES/scriptish.properties b/extension/locale/es-ES/scriptish.properties
new file mode 100644
index 00000000..b113e4f0
--- /dev/null
+++ b/extension/locale/es-ES/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=El desarrollador de este script de usuario le pide que ayude a mantener su desarrollo continuado haciendo un pequeño donativo.
+copyDownloadURL=Copiar URL de descarga
+copyDownloadURL.ak=C
+edit=Editar
+edit.ak=E
+editor.couldNotLaunch=Error al abrir el editor.
+editor.pleasePickExecutable=Por favor, elija un programa para editar sus scripts de usuario.
+editor.prompt=Por favor, elija antes su editor de texto favorito
+editor.useScratchpad=¿Desea usar Scratchpad como su editor?
+editor.useScratchpad.no=No (yo seleccionaré el mÃo)
+editor.useScratchpad.yes=SÃ
+error.api.badArguments=
+error.api.noResourceWithName=No hay recursos con ese nombre
+error.api.noSecondArgValue=Segundo argumento no especificado: Valor
+error.api.prefNotFound=
+error.api.reqURL=URL no válido
+error.api.reqURL.scheme=Esquema no permitido en URL
+error.api.safeHTMLParser.url=
+error.api.unsafeAccess=Violación de acceso de Scriptish: unsafeWindow no puede invocar
+error.charset=El conjunto de caracteres especificado no es válido.
+error.dependency.loading=Error al cargar dependencia
+error.dependency.local=Excepción de seguridad: prohibidas las peticiones a URLs locales y a chrome
+error.dependency.serverReturned=El servidor ha devuelto ¡Error!
+error.hash.algorithm=Especificado algoritmo de hash no válido.
+error.icon.dataURL=URL de tipo datos para @ICON no válido
+error.icon.notImage=¡Error!: @ICON no tiene un tipo MIME de imagen
+error.icon.URL=URL para @ICON no válido
+error.invalidCert=Certificado SSL no válido.\nSi lo desea, permita todos los certificados activando la opción de Scriptish en Opciones / Avanzado / Seguridad de las actualizaciones.
+error.isInvalidValue=es un valor no permitido
+error.matchPattern.rules=El filtro @match no se ajusta a las reglas para filtros
+error.matchPattern.rules.file=El filtro @match del achivo de patrones no se ajusta a las reglas para filtros
+error.notSecure=URI insegura.\nSi quiere, permita las actualizaciones no-HTTPS activando la opción de Scriptish en Opciones / Avanzado / Seguridad de las actualizaciones.
+error.notSupported.Firefox=no soportado por esta versión de Firefox
+error.openingFile=Error abriendo el archivo
+error.pattern.parsing=Error interpretando el filtro
+error.pref.type=Tipo de preferencia no soportado. Tipos válidos: cadena, booleano y entero de 32 bits.
+error.remoteVersionOlder=El script remoto no tiene un número de versión más nuevo que su actual script.
+error.resource.dupName=es un nombre de recurso duplicado. Cada declaración @Resource debe tener un nombre único.
+error.resource.syntax=Sintaxis no válida para la declaración @Resource
+error.retrieving=Error al descargar
+error.script.installing=Error al instalar script de usuario
+error.script.loading=Error al cargar script de usuario
+greeting.btn.ak=I
+greeting.msg=Este es un script de usuario. Pulse Instalar para empezar a usarlo.
+install=Instalar
+install.domains=dominios:
+install.excludes=no se ejecuta sobre:
+install.includes=se ejecuta sobre:
+install.matches=filtros:
+install.requires=requerimientos:
+install.resources=recursos:
+install.showScriptSource=Mostrar código fuente del script
+install.title=Instalación de script de usuario
+install.warning1=Los scripts maliciosos pueden violar su privacidad y actuar en su nombre sin su conocimiento.
+install.warning2=Sólo deberÃa instalar scripts de fuentes en las que confÃe.
+installFromFile=Instalar script de usuario desde archivo...
+installFromFile.ak=S
+menu.commands=Comandos del script de usuario...
+menu.commands.ak=C
+menu.install=Instalar script de usuario...
+menu.install.ak=I
+menu.manage=Gestionar scripts de usuario...
+menu.manage.ak=G
+menu.new=Nuevo script de usuario...
+menu.new.ak=N
+menu.options.ak=O
+menu.show=Ver código fuente del script
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=Instalar este script de usuario...
+menuitem.manage=Gestionar scripts de usuario
+menuitem.new=Nuevo script de usuario
+moving.dependency=Moviendo el archivo de dependencias desde
+moving.script=Moviendo el archivo de script desde
+newscript.author=Autor
+newscript.description=Descripción
+newscript.excludes=Exclusiones (una por linea)
+newscript.exists=Ya hay instalado un script con ese nombre.\n¿Sobreescribir?
+newscript.id=ID
+newscript.includes=Inclusiones (una por linea)
+newscript.missing.id=Por favor, introduzca un identificador.
+newscript.missing.name=Por favor, introduzca un nombre. El nombre se mostrará en el Administrador de complementos.
+newscript.name=Nombre
+newscript.namespace=Espacio de nombres
+newscript.noID=Por favor, introduzca un identificador válido para su script.
+newscript.noName=Por favor, introduzca un nombre para su script.
+newscript.runat=Ejecutar cuando
+newscript.runat.documentend=el documento termine de cargarse
+newscript.runat.documentidle=el documento esté inactivo por primera vez
+newscript.runat.documentstart=el documento comience a cargarse
+newscript.runat.windowload=la ventana con todo su contenido esté completamente cargada
+newscript.version=Versión
+nothing.timedOut=nada (en espera)
+openFolder=Abrir carpeta
+openFolder.ak=B
+openUserScriptsManager=Abrir Administrador de scripts de usuario
+openUserScriptsManager.ak=A
+options=Opciones
+options.addonsManager=Administrador de complementos
+options.alsoUninstallPrefs=Al desinstalar un script, borrar también sus preferencias
+options.cache=Cache
+options.cache.enabled=Cargar los scripts de usuario en cache
+options.changeEditor=Cambiar editor
+options.editor=Editor
+options.editor.notset=No se ha seleccionado editor
+options.enableCopyDownloadURL=Activar opción Copiar URL de descarga
+options.enabledSchemes.about=Protocolo about (interno)
+options.enabledSchemes.advanced=Protocolos adicionales
+options.enabledSchemes.chrome=Protocolo chrome (interno)
+options.enabledSchemes.data=Protocolo data
+options.enabledSchemes.file=Protocolo file (archivos locales)
+options.enabledSchemes.ftp=Protocolo ftp
+options.enabledSchemes.http=Protocolo http/https
+options.enabledSchemes.main=Protocolos
+options.enabledSchemes.securityRemark=Los protocolos adicionales podrÃan tener acceso completo al sistema.
+options.enabledSchemes.securityRemark2=¡Por lo tanto representan un riesgo de seguridad considerable con scripts no confiables!
+options.enabledSchemes.unmht=Protocolo unmht (Formato de Archivo Mozilla, .maf)
+options.excludes.desc=Prevalecen sobre cualquier regla de script, o definida por el usuario
+options.excludes.empty=No hay definidas exclusiones globales
+options.excludes.remark=(Una exclusión por linea)
+options.logChrome=Registrar en log mensajes de extensión no crÃticos en la Consola de errores
+options.logging=Registro de eventos (logging)
+options.logToErrorConsole=GM_log usa la Consola de errores
+options.notifications=Notificaciones
+options.notifications.popup.enable=Activar notificaciones emergentes
+options.notifications.sliding.enable=Activar notificaciones deslizantes
+options.notifications.whenDisabled=Cuando está deshabilitado, cualquier notificación aparecerá en la Consola de errores.
+options.pane.advanced=Avanzado
+options.pane.excludes=Exclusiones globales
+options.pane.main=Principal
+options.pane.ui=Interfaz de usuario
+options.requireBuiltInCerts=Las actualizaciones seguras requieren que el certificado esté incluÃdo
+options.requireSecured=Requerir que los scripts de usuario se actualicen usando HTTPS
+options.sync.desc=
+options.sync.ScriptishPrefs=
+options.sync.ScriptishPrefs.common=
+options.sync.ScriptishPrefs.editor=
+options.title=Opciones de Scriptish
+options.toolbarbutton=Botón de la Barra de Herramientas
+options.toolbarbutton.showScripts=Muestra la cuenta de scripts activos
+options.translation=Traducción
+options.translation.useEnglish=Usar inglés siempre
+options.update.security=Seguridad de las actualizaciones
+options.useDownloadURL=Utilizar URL de descarga para actualizar si no hay URL de actualización
+reinstall=Reinstalar
+saving=Guardando
+scratchpad.saveAsUserScript=Guardar como script de usuario
+scratchpad.saveAsUserScript.ak=S
+scriptOptions.disableScriptIncludes=Desactivar los filtros de inclusión de este script y utilizar sólo los definidos por el usuario.
+scriptOptions.excludes=Exclusiones definidas por el usuario:
+scriptOptions.includes=Inclusiones definidas por el usuario:
+statusbar.enabled=Activado
+statusbar.enabled.ak=A
+statusbar.installed=instalado con éxito
+statusbar.modified=modificado
+statusbar.noScripts.excluded=¡Esta página está excluÃda!
+statusbar.noScripts.notfound=¡No hay scripts para esta página!
+statusbar.noScripts.scheme=¡Este patrón está deshabilitado!
+statusbar.updated=actualizado con éxito
+sync=
+tooltip.loading=Cargando...
+Uninstall=Desinstalar
+untitledScript=Script sin tÃtulo
+Update=Actualizar
+userscript=Script de usuario
+userscripts=Scripts de usuario
+userscripts.get=Obtener scripts de usuario
+userscripts.noneInstalled=No tiene instalado ningún script de usuario
+warning.returnfrommain="return" desde el ámbito principal no está soportado normalmente. DeberÃa arreglar esto en su script
diff --git a/extension/locale/he/description.properties b/extension/locale/he/description.properties
new file mode 100644
index 00000000..07eecd37
--- /dev/null
+++ b/extension/locale/he/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish ×”×•× ×”×ž×©×š פיתוח ל־Greasemonkey, המציע:
+amo.description.line2=כל מה ש־Greasemonkey עושה (×©×”×•× ×œ×ž×¢×©×” שימושי)
+amo.description.line3=מעדכן: קבצי script של משתמש × ×™×ª× ×™× ×œ×¢×“×›×•×Ÿ ב־Scriptish ב×מצעות @updateURL
+amo.description.line4=הרבה ×—×“×©×™× ×”× ×ž×˜×”Ö¾× ×ª×•× ×™× ×©×œ @מפתחות
+amo.description.line5=הרבה ×—×“×©×™× ×”× ×ª×•×¡×¤×•×ª לממשק ×”×™×™×©×•× ×©×œ GM_
+amo.description.line6=×‘×™×¦×•×¢×™× ×ž×¢×•×œ×™×: מהיר יותר, קוד בסיס × ×§×™ יותר ×©×ž× ×¦×œ ×ת ×”×™×ª×¨×•× ×•×ª בכל הגירס×ות ×”××—×¨×•× ×•×ª של Firefox בצורה מירבית
+amo.description.line7=×בטחה מעולה : Scriptish מספק ×ª×›×•× ×•×ª ×בטחה ×©×œ× ×ª×ž×¦× ×‘×ž×§×•× ×חר
+amo.description.line8=בדיקות: ×”×ž×¤×ª×—×™× ×¢×•×©×™× ×”×¨×‘×” בדיקות, כדי להבטיח תפקוד × ×›×•×Ÿ של Scriptish ול×תר ×ת כל הב××’×™× ×ž×•×§×“× ×™×•×ª×¨
+amo.developerComments.line1=ל־Scriptish יש כל מה שיש ל־Greasemonkey, ועוד ×ª×›×•× ×•×ª רבות ×©× ×™×ª×Ÿ ×œ×§×¨×•× ××•×“×•×ª× ×‘: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=×× × ×©×œ×— דיווח על כל ב××’ ×ו בקשת תוספת לכתובת הב××”: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=×× ×™×© לך ש×לות × ×•×¡×¤×•×ª, × × ×œ×©×œ×•×— ××•×ª× ×œ×¨×©×™×ž×ª התפוצה: http://groups.google.com/group/scriptish
+amo.summary=×ž× ×•×¢ script של משתמש הגדול ביותר ברשת.
+extensions.scriptish@erikvold.com.description=×ž× ×”×œ הרחבה של קבצי Script עבור Firefox
diff --git a/extension/locale/he/scriptish.properties b/extension/locale/he/scriptish.properties
new file mode 100644
index 00000000..8a12c4ee
--- /dev/null
+++ b/extension/locale/he/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=המפתח של Scriptish מבקש סיוע בהמשך פיתוח ההרחבה ב×מצעות מתן תרומה ×§×˜× ×”.
+copyDownloadURL=העתק כתובת URL של הורדה
+copyDownloadURL.ak=×¢
+edit=ערוך
+edit.ak=×¢
+editor.couldNotLaunch=×œ× × ×™×ª×Ÿ להפעיל ×ת עורך הטקסט
+editor.pleasePickExecutable=×× × ×‘×—×¨ ×™×™×©×•× ×”×¤×¢×œ×” (כגון: notepad.exe) עבור עריכת קבצי script של משתמש.
+editor.prompt=×× × ×‘×—×¨ תחילה ×ת עורך הטקסט המועדף עליך
+editor.useScratchpad=×”×× ×‘×¨×¦×•× ×š להשתמש ב־Scratchpad כעורך הטקסט שלך?
+editor.useScratchpad.no=×œ× (×× ×™ ×בחר בעצמי)
+editor.useScratchpad.yes=כן
+error.api.badArguments=
+error.api.noResourceWithName=×œ× ×§×™×™× ×ž×©×ב בעל ש×
+error.api.noSecondArgValue=×œ× ×¦×•×™×Ÿ ערך ×©× ×™: ערך
+error.api.prefNotFound=
+error.api.reqURL=כתובת URL ×œ× ×—×•×§×™×ª
+error.api.reqURL.scheme=ערכה ×œ× ×ž×•×¨×©×ª בכתובת URL
+error.api.safeHTMLParser.url=
+error.api.unsafeAccess=הפרת גישה של Scriptish: ×ין ×פשרות ×œ×§×¨×•× unsafeWindow
+error.charset=צוין קידוד ×œ× ×—×•×§×™.
+error.dependency.loading=שגי××” ×‘×˜×¢×™× ×ª תלות
+error.dependency.local=SecurityException: בקשות ×ל כתובות מקומיות ו־chrome ×”×™× ×Ÿ ×סורות
+error.dependency.serverReturned=שגי××”! השרת החזיר שגי××”
+error.hash.algorithm=צוין ××œ×’×•×¨×™×ª× hash ×œ× ×—×•×§×™.
+error.icon.dataURL=× ×ª×•× ×™× ×œ× ×—×•×§×™×™×: כתובת URL של @סמל
+error.icon.notImage=שגי××”! ל־@סמל ×ין ×ª×ž×•× ×” מסוג MIME
+error.icon.URL=כתובת URL ×œ× ×—×•×§×™×ª של @סמל
+error.invalidCert=
+error.isInvalidValue=×”×•× ×¢×¨×š ×œ× ×—×•×§×™
+error.matchPattern.rules=@הת×מת ×”×ª×‘× ×™×ª ××™× ×” תו×מת ×ת כללי ×”×ª×‘× ×™×ª
+error.matchPattern.rules.file=קובץ ערכת @הת×מת ×ª×‘× ×™×ª ××™× ×• תו×× ×ת כללי ×”×ª×‘× ×™×ª
+error.notSecure=
+error.notSupported.Firefox=××™× ×• × ×ª×ž×š ×¢"×™ גירסה זו של Firefox
+error.openingFile=×ין ×פשרות לפתוח קובץ
+error.pattern.parsing=×ין ×פשרות לעבד ×ת ×”×ª×‘× ×™×ª
+error.pref.type=סוג מ×פיין ש××™× ×• × ×ª×ž×š. ×”×¡×•×’×™× ×”×—×•×§×™×™× ×”×: מחרוזת, פעולה בולי×× ×™×ª ×•×ž×¡×¤×¨×™× ×©×œ×ž×™× ×©×œ 32 סיביות.
+error.remoteVersionOlder=
+error.resource.dupName=×”×•× ×©× ×ž×©×ב כפול. כל @מש×ב חייב להיות בעל ×©× ×™×™×—×•×“×™.
+error.resource.syntax=תחביר ×œ× ×—×•×§×™ עבור הצהרת @מש×ב
+error.retrieving=×חזור × ×›×©×œ
+error.script.installing=שגי××” ×‘×”×ª×§× ×ª קובץ script של משתמש
+error.script.loading=שגי××” ×‘×˜×¢×™× ×ª קובץ script של משתמש
+greeting.btn.ak=ט
+greeting.msg=זהו קובץ script של משתמש. לחץ על התקן כדי להתחיל להשתמש בו.
+install=התקן
+install.domains=תחומי×:
+install.excludes=××™× ×• פועל ב:
+install.includes=פועל ב:
+install.matches=מת××™×:
+install.requires=דורש:
+install.resources=מש×בי×:
+install.showScriptSource=הצג ×ת מקור ×”Ö¾Script
+install.title=×”×ª×§× ×ª קובץ Script של משתמש
+install.warning1=קבצי script ×–×“×•× ×™×™× ×™×›×•×œ×™× ×œ×”×¤×¨ ×ת הפרטיות שלך ולפעול בשמך ×œ×œ× ×™×“×™×¢×ª×š.
+install.warning2=עליך להתקין קבצי script רק ממקורות ×ž×”×™×ž× ×™×.
+installFromFile=×”×ª×§× ×ª Script של משתמש מקובץ...
+installFromFile.ak=מ
+menu.commands=פקודות Script של משתמש...
+menu.commands.ak=פ
+menu.install=×”×ª×§× ×ª Script של משתמש...
+menu.install.ak=×”
+menu.manage=× ×™×”×•×œ קבצי Script של משתמש...
+menu.manage.ak=×
+menu.new=Script של משתמש חדש...
+menu.new.ak=×—
+menu.options.ak=×
+menu.show=הצג ×ת מקור הקובץ של Script המשתמש
+menu.show.ak=צ
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=התקן קובץ Script של משתמש זה...
+menuitem.manage=× ×™×”×•×œ קבצי Script של משתמש
+menuitem.new=Script של משתמש חדש
+moving.dependency=מעביר קובץ תלות מ...
+moving.script=מעביר תלות קובץ מ...
+newscript.author=מחבר
+newscript.description=תי×ור
+newscript.excludes=×œ× ×›×•×œ×œ (×חד בכל שורה)
+newscript.exists=Script ×‘×©× ×–×” כבר מותקן. \n×”×× ×‘×¨×¦×•× ×š להחליפו?
+newscript.id=מזהה
+newscript.includes=כולל (×חד בכל שורה)
+newscript.missing.id=×× × ×¡×¤×§ מזהה.
+newscript.missing.name=×× × ×¡×¤×§ ש×. ×”×©× ×™×•×¦×’ בתוך ×ž× ×”×œ התוספות.
+newscript.name=ש×
+newscript.namespace=×©× ×ž×¨×—×‘
+newscript.noID=×× × ×¦×™×™×Ÿ מזהה חוקי עבור ×”Ö¾script שלך.
+newscript.noName=×× × ×¡×¤×§ ×©× ×¢×‘×•×¨ ×”Ö¾script שלך.
+newscript.runat=הפעל ×›×שר
+newscript.runat.documentend=המסמך ×¡×™×™× ×œ×”×™×˜×¢×Ÿ
+newscript.runat.documentidle=המסמך הר×שון ××™× ×• פעיל
+newscript.runat.documentstart=המסמך התחיל להיטען
+newscript.runat.windowload=החלון כולל כל המדיה × ×˜×¢× ×• במלו××
+newscript.version=גירסה
+nothing.timedOut=×©×•× ×“×‘×¨ (בזמן שהוקצב)
+openFolder=פתח תיקיה
+openFolder.ak=פ
+openUserScriptsManager=פתח ×ת ×ž× ×”×œ קבצי ×”Ö¾Scripts של משתמש
+openUserScriptsManager.ak=פ
+options=×פשרויות
+options.addonsManager=×ž× ×”×œ התוספות
+options.alsoUninstallPrefs=בעת הסרת ×”×”×ª×§× ×” של קובץ script, הסר ×’× ×ת ההעדפות שלו
+options.cache=מטמון
+options.cache.enabled=קבצי script של משתמש במטמון
+options.changeEditor=×©× ×” עורך טקסט
+options.editor=עורך טקסט
+options.editor.notset=×œ× × ×‘×—×¨ עורך טקסט
+options.enableCopyDownloadURL=×פשר העתקת כתובת URL של הורדה
+options.enabledSchemes.about=×ודות תקן העברת × ×ª×•× ×™× (×¤× ×™×ž×™)
+options.enabledSchemes.advanced=×ª×§× ×™ העברת × ×ª×•× ×™× × ×•×¡×¤×™×
+options.enabledSchemes.chrome=תקן העברת × ×ª×•× ×™× ×©×œ chrome (×¤× ×™×ž×™)
+options.enabledSchemes.data=תקן העברת × ×ª×•× ×™× ×©×œ × ×ª×•× ×™×
+options.enabledSchemes.file=תקן העברת × ×ª×•× ×™× ×©×œ ×§×‘×¦×™× (×§×‘×¦×™× ×ž×§×•×ž×™×™×)
+options.enabledSchemes.ftp=תקן העברת × ×ª×•× ×™× ×©×œ FTP
+options.enabledSchemes.http=תקן העברת × ×ª×•× ×™× ×©×œ http/https
+options.enabledSchemes.main=×ª×§× ×™ העברת × ×ª×•× ×™×
+options.enabledSchemes.securityRemark=×ª×§× ×™ העברת × ×ª×•× ×™× × ×•×¡×¤×™× ×©×¢×œ×•×œ×™× ×œ×§×‘×œ גישה מל××” למערכת.
+options.enabledSchemes.securityRemark2=לפיכך ×”× ×ž×”×•×•×™× ×¡×™×›×•×Ÿ ×בטחה גדול למדי ×¢× ×§×‘×¦×™ script ש××™× ×• ×ž×”×™×ž× ×™×!
+options.enabledSchemes.unmht=תקן העברת × ×ª×•× ×™× ×©×œ unmht (×ª×‘× ×™×ª ×רכיון מוזילה)
+options.excludes.desc=××™ הכללה כללית תשבית כל script ×ו ×›×œ×œ×™× ×ž×¡×•×™×™×ž×™× ×©×œ משתמש
+options.excludes.empty=×œ× ×¦×•×™× ×” ××™ הכללה כללית
+options.excludes.remark=(ב×פשרותך לציין הכללה ×חת בכל שורה)
+options.logChrome=×œ×¨×©×•× ×”×•×“×¢×•×ª ש××™× × ×§×¨×™×˜×™×™× ×”×¨×—×‘×” למסוף שגי××”
+options.logging=רישו×
+options.logToErrorConsole=GM_log משתמש במסוף שגי××”
+options.notifications=הודעות
+options.notifications.popup.enable=הפעל הודעות מוקפצות
+options.notifications.sliding.enable=הפעל הודעות גולשות
+options.notifications.whenDisabled=×›×שר ×œ× ×–×ž×™×Ÿ, כל הודעה תופיע במסוף השגי××”.
+options.pane.advanced=מתקד×
+options.pane.excludes=שלילה
+options.pane.main=ר×שי
+options.pane.ui=ממשק
+options.requireBuiltInCerts=עדכון מ×ובטח מחייב שימוש ב××™×©×•×¨×™× ×ž×•×‘× ×™×
+options.requireSecured=דרוש מקבצי script של משתמש להתעדכן ע"י שימוש ב־HTTPS
+options.sync.desc=
+options.sync.ScriptishPrefs=
+options.sync.ScriptishPrefs.common=
+options.sync.ScriptishPrefs.editor=
+options.title=×פשרויות של Scriptish
+options.toolbarbutton=לחצן סרגל כלי×
+options.toolbarbutton.showScripts=הצג ×ת מספר ×”Ö¾scripts הפעילי×
+options.translation=תרגו×
+options.translation.useEnglish=תמיד השתמש ב×× ×’×œ×™×ª
+options.update.security=עדכון ×בטחה
+options.useDownloadURL=השתמש בכתובת URL להורדת ×¢×“×›×•× ×™× ×× ×§×™×™×ž×ª כתובת URL ×œ×œ× ×¢×“×›×•×Ÿ
+reinstall=התקן מחדש
+saving=שומר
+scratchpad.saveAsUserScript=שמור כקובץ Script של משתמש
+scratchpad.saveAsUserScript.ak=מ
+scriptOptions.disableScriptIncludes=×ž× ×¢ קובץ script והשתמש רק ב×לה ×”×ž×•×’×“×¨×™× ×¢"×™ המשתמש.
+scriptOptions.excludes=×œ× ×›×œ×•×œ (×¢"×™ המשתמש):
+scriptOptions.includes=כלול (ע"י המשתמש):
+statusbar.enabled=זמין
+statusbar.enabled.ak=×–
+statusbar.installed=הותקן בהצלחה
+statusbar.modified=×©×•× ×” ל××—×¨×•× ×”
+statusbar.noScripts.excluded=הדף ××™× ×• × ×›×œ×œ!
+statusbar.noScripts.notfound=×ין קבצי script לדף!
+statusbar.noScripts.scheme=ערכה ××™× ×” ×–×ž×™× ×”!
+statusbar.updated=עודכן בהצלחה
+sync=
+tooltip.loading=טוען...
+Uninstall=הסר
+untitledScript=קובץ Script ×œ×œ× ×©×
+Update=עדכן
+userscript=Script של משתמש
+userscripts=Script של משתמש
+userscripts.get=קבל קבצי script של משתמש
+userscripts.noneInstalled=×ין ברשותך קבצי scripts של משתמש ×ž×•×ª×§× ×™×
+warning.returnfrommain=מצב "חוזר" ×ž×ª×—×•× ×¨×שי בדרך כלל ××™× ×” × ×ª×ž×š. עליך לתקן ×–×ת בקובץ ×”Ö¾script של המשתמש.
diff --git a/extension/locale/hu/description.properties b/extension/locale/hu/description.properties
new file mode 100644
index 00000000..9a2ff3b4
--- /dev/null
+++ b/extension/locale/hu/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=A Scriptish a Greasemonkey továbbfejlesztése, és a következÅ‘t kÃnálja:
+amo.description.line2=Mindent, amit a Greasemonkey tud (ami éppenséggel elég hasznos)
+amo.description.line3=FrissÃtés: a parancsfájlok frissÃthetÅ‘ek a Scriptish-ben a @updateURL használatával
+amo.description.line4=Sok új metadata @kulcs
+amo.description.line5=Sok új kiegészÃtés a GM_ API-hoz
+amo.description.line6=Nagyobb teljesÃtmény: Gyorsabb, tisztább kódbázis, mely kihasználja a legújabb Firefox verziók adta lehetÅ‘ségeket
+amo.description.line7=Nagyobb biztonság: A Scriptish olyan biztonsági funkciókat szolgáltat, melyeket máshol nem talál meg
+amo.description.line8=Tesztelés: A fejlesztÅ‘k rengeteg tesztet végeznek a helyes működés biztosÃtása és a hibák gyorsabb felderÃtése érdekében
+amo.developerComments.line1=A Scriptish tartalmazza mindazt, amit a Greasemonkey, továbbá sok új funkciót, melyekről itt olvashat: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=Kérjük a hibákat és a javaslatokat itt jelezze: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=Ha bármely kérdése van, kérjük küldje el azokat a levelezési listára: http://groups.google.com/group/scriptish
+amo.summary=A legnagyszerűbb parancsfájl-kezelő az Interneten.
+extensions.scriptish@erikvold.com.description=Parancsfájl-kezelő a Firefoxhoz
diff --git a/extension/locale/hu/scriptish.properties b/extension/locale/hu/scriptish.properties
new file mode 100644
index 00000000..6e8721b4
--- /dev/null
+++ b/extension/locale/hu/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=A parancsfájl készÃtÅ‘je támogatást kér a parancsfájl fejlesztésének folytatásához.
+copyDownloadURL=Letöltési link másolása
+copyDownloadURL.ak=m
+edit=Szerkesztés
+edit.ak=S
+editor.couldNotLaunch=SzerkesztÅ‘ indÃtása sikertelen.
+editor.pleasePickExecutable=Kérem válasszon egy futtatható alkalmazást a parancsfájlok szerkesztéséhez.
+editor.prompt=Kérem először válassza ki az Ön által preferált szerkesztőt
+editor.useScratchpad=A Scratchpad-et szeretné használni szerkesztőként?
+editor.useScratchpad.no=Nem (kiválasztok egy sajátot)
+editor.useScratchpad.yes=Igen
+error.api.badArguments=
+error.api.noResourceWithName=Nincs erőforrás ezen a néven:
+error.api.noSecondArgValue=Második argumentum nincs meghatározva: Érték
+error.api.prefNotFound=
+error.api.reqURL=Érvénytelen URL
+error.api.reqURL.scheme=Az URL formátuma érvénytelen
+error.api.safeHTMLParser.url=
+error.api.unsafeAccess=Scriptish hozzáférési hiba: unsafeWindow nem képes hÃvni
+error.charset=Érvénytelen karakterosztály.
+error.dependency.loading=Hiba a függőség betöltése során
+error.dependency.local=SecurityException: lokális és chrome URL-ek lekérése nem engedélyezett
+error.dependency.serverReturned=Hiba! A szerver válasza:
+error.hash.algorithm=Érvénytelen hash-elő algoritmus.
+error.icon.dataURL=Érvénytelen adat: @icon URL-ja
+error.icon.notImage=Hiba! @icon nem rendelkezik kép MIME tÃpussal
+error.icon.URL=Érvénytelen URL az @icon-hoz
+error.invalidCert=
+error.isInvalidValue=érvénytelen érték
+error.matchPattern.rules=@match minta nem felel meg a szabályoknak
+error.matchPattern.rules.file=Fájl séma @match minta nem fele meg a szabályoknak
+error.notSecure=
+error.notSupported.Firefox=nem támogatott a Firefox ezen verziója által.
+error.openingFile=Fájl megnyitása sikertelen
+error.pattern.parsing=Minta feldolgozása sikertelen
+error.pref.type=Nem támogatott tÃpus. Érvényes tÃpusok: karakterlánc, logikai és 32 bites egész.
+error.remoteVersionOlder=
+error.resource.dupName=egy duplikált erőforrás név. Minden @resource-nek egyedi névvel kell rendelkeznie.
+error.resource.syntax=Érvénytelen szintaktis @resource deklarációhoz
+error.retrieving=Beszerzése sikertelen:
+error.script.installing=Hiba a parancsfájl telepÃtése során
+error.script.loading=Hiba a parancsfájl betöltése során
+greeting.btn.ak=T
+greeting.msg=Ez egy parancsfájl. Kattintson a telepÃtésre a parancsfájl használatának megkezdéséhez.
+install=TelepÃtés
+install.domains=dömének:
+install.excludes=nem fut a következőn:
+install.includes=fut a következőn:
+install.matches=illeszkedik:
+install.requires=elvárások:
+install.resources=erőforrások:
+install.showScriptSource=Parancsfájl forrásának megjelenÃtése
+install.title=Parancsfájl telepÃtése
+install.warning1=Kártékony parancsfájlok hozzáférhetnek az Ön személyes adataihoz és működhetnek az Ön tudta nélkül.
+install.warning2=Csak megbÃzható forrásból telepÃtsen parancsfájlt!
+installFromFile=Parancsfájl telepÃtése fájlból…
+installFromFile.ak=f
+menu.commands=Parancsfájl műveletei…
+menu.commands.ak=P
+menu.install=Parancsfájl telepÃtése…
+menu.install.ak=t
+menu.manage=Parancsfájlok kezelése…
+menu.manage.ak=k
+menu.new=Új parancsfájl…
+menu.new.ak=j
+menu.options.ak=B
+menu.show=Parancsfájl forrásának megtekintése
+menu.show.ak=f
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=Parancsfájl telepÃtése…
+menuitem.manage=Parancsfájlok kezelése…
+menuitem.new=Új parancsfájl
+moving.dependency=Függőségi fájl áthelyezése
+moving.script=Parancsfájl átnevezése
+newscript.author=KészÃtÅ‘
+newscript.description=LeÃrás
+newscript.excludes=Kizárt oldalak (soronként egy)
+newscript.exists=Már telepÃtve van parancsfájl ilyen néven.\nFelülÃrja?
+newscript.id=ID
+newscript.includes=Tartalmazott oldalak (soronként egy)
+newscript.missing.id=Kérem adjon meg egy ID-t.
+newscript.missing.name=Kérem adjon meg egy nevet. Ez a név fog megjelenni a KiegészÃtÅ‘kezelÅ‘ben.
+newscript.name=Név
+newscript.namespace=Névtér
+newscript.noID=Kérem adjon meg egy érvényes ID-t a szkripthez.
+newscript.noName=Kérem adjon meg egy nevet a szkripthez.
+newscript.runat=Mikor fusson
+newscript.runat.documentend=Az oldal betöltésének befejeztekor
+newscript.runat.documentidle=Amikor az oldal először van üresjáratban
+newscript.runat.documentstart=Az oldal betöltésének kezdetekor
+newscript.runat.windowload=Amikor az ablak az összes média elemmel együtt betöltésre kerül
+newscript.version=Verzió
+nothing.timedOut=semmi (időtúllépés)
+openFolder=Könyvtár megnyitása
+openFolder.ak=K
+openUserScriptsManager=Parancsfájl kezelő megnyitása
+openUserScriptsManager.ak=m
+options=BeállÃtások
+options.addonsManager=KiegészÃtÅ‘kezelÅ‘
+options.alsoUninstallPrefs=A parancsfájl eltávolÃtásakor a hozzá tartozó beállÃtásokat is törölje
+options.cache=GyorsÃtótár
+options.cache.enabled=Parancsfájlok gyorsÃtótárazása
+options.changeEditor=Szerkesztő megváltoztatása
+options.editor=Szerkesztő
+options.editor.notset=Nincs szerkesztő kiválasztva
+options.enableCopyDownloadURL=Letöltési URL másolásának engedélyezése
+options.enabledSchemes.about=about protokoll (belső)
+options.enabledSchemes.advanced=További protokollok
+options.enabledSchemes.chrome=chrome protokoll (belső)
+options.enabledSchemes.data=data protokoll
+options.enabledSchemes.file=file protokoll (lokális fájlok)
+options.enabledSchemes.ftp=ftp protokoll
+options.enabledSchemes.http=http/https protokoll
+options.enabledSchemes.main=Protokollok
+options.enabledSchemes.securityRemark=A további protokollok teljes rendszer jogosultságokkal rendelkezhetnek.
+options.enabledSchemes.securityRemark2=Ãgy nem megbÃzható parancsfájlokkal használva komoly biztonsági kockázatot jelentenek!
+options.enabledSchemes.unmht=unmht protokoll (Mozilla archÃvum formátum)
+options.excludes.desc=A globális kivételek minden parancsfájl vagy felhasználó által meghatározott szabályt felülÃrnak
+options.excludes.empty=Nincsenek globális kivételek meghatározva
+options.excludes.remark=(Soronként egyet adhat meg)
+options.logChrome=Nem kritikus kiterjesztés üzenetek naplózása a Hibakonzolban
+options.logging=Naplózás
+options.logToErrorConsole=A GM_log használja a Hibakonzolt
+options.notifications=ÉrtesÃtések
+options.notifications.popup.enable=Felugró értesÃtések engedélyezése
+options.notifications.sliding.enable=Beúszó értesÃtések engedélyezése
+options.notifications.whenDisabled=Ha ez nincs engedélyezve, minden értesÃtés a Hibakonzolban jelenik meg.
+options.pane.advanced=Haladó
+options.pane.excludes=Globális kivételek
+options.pane.main=Ãltalános
+options.pane.ui=Felhasználói felület
+options.requireBuiltInCerts=A biztonsági frissÃtésekhez beépÃtett tanúsÃtványok szükségesek
+options.requireSecured=A parancsfájlok frissÃtése a HTTPS protokollon keresztül történjen
+options.sync.desc=
+options.sync.ScriptishPrefs=
+options.sync.ScriptishPrefs.common=
+options.sync.ScriptishPrefs.editor=
+options.title=Scriptish beállÃtások
+options.toolbarbutton=Eszköztár gomb
+options.toolbarbutton.showScripts=AktÃv parancsfájlok számának megjelenése
+options.translation=HonosÃtás
+options.translation.useEnglish=Mindig az angol szöveget használja
+options.update.security=FrissÃtések biztonsága
+options.useDownloadURL=Letöltési URL használata a frissÃtéshez, ha frissÃtési URL nincs meghatározva
+reinstall=ÚjratelepÃtés
+saving=Mentés
+scratchpad.saveAsUserScript=Mentés parancsfájlként
+scratchpad.saveAsUserScript.ak=p
+scriptOptions.disableScriptIncludes=Parancsfájl tartalmazási mintáinak letiltása és csak a felhasználó által megadottakat használja.
+scriptOptions.excludes=Felhasználói kizárások:
+scriptOptions.includes=Felhasználói tartalmazások:
+statusbar.enabled=Engedélyezve
+statusbar.enabled.ak=E
+statusbar.installed=telepÃtése sikeres
+statusbar.modified=módosÃtva
+statusbar.noScripts.excluded=Oldal kizárva!
+statusbar.noScripts.notfound=Nincsenek parancsfájlok az oldalhoz!
+statusbar.noScripts.scheme=Séma kikapcsolva!
+statusbar.updated=frissÃtése sikeres
+sync=
+tooltip.loading=Betöltés…
+Uninstall=EltávolÃtás
+untitledScript=Névtelen parancsfájl
+Update=FrissÃtés
+userscript=Parancsfájl
+userscripts=Parancsfájlok
+userscripts.get=Töltsön le parancsfájlokat
+userscripts.noneInstalled=Nincsen telepÃtett parancsfájl
+warning.returnfrommain="return" utasÃtás használata a fÅ‘ scope-ban nem támogatott. Ezt ki kell javÃtania a parancsfájlban.
diff --git a/extension/locale/ja-JP/description.properties b/extension/locale/ja-JP/description.properties
new file mode 100644
index 00000000..3cecca56
--- /dev/null
+++ b/extension/locale/ja-JP/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish 㯠Greasemonkey ã®ãƒ•ォーク(派生)ã§ã™ã€‚ãã®æ©Ÿèƒ½ã¯...:
+amo.description.line2=Greasemonkey ã®å…¨æ©Ÿèƒ½ï¼ˆã“れホント便利)
+amo.description.line3=更新機能:@updateURL を指定ã™ã‚‹ã“ã¨ã§ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トを更新ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™
+amo.description.line4=å¤šæ•°ã®æ–°ãŸãª メタデータ(@keys)
+amo.description.line5=å¤šæ•°ã®æ–°ãŸãª è¿½åŠ GM_ API
+amo.description.line6=優れãŸãƒ‘フォーマンス:最新㮠Firefox を使ã„倒ã—ãŸã€ã‚ˆã‚Šé€Ÿãã€ã‚ˆã‚Šã‚¯ãƒªãƒ¼ãƒ³ãªã‚³ãƒ¼ãƒ‰ãƒ™ãƒ¼ã‚¹
+amo.description.line7=優れãŸã‚»ã‚ュリティ:Scriptish ã¯ä»–ã«ãªã„ã‚»ã‚ュリティ機能をæä¾›ã—ã¾ã™
+amo.description.line8=テスト:開発者㯠Scriptish ãŒæ£ã—ãæ©Ÿèƒ½ã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ãŸã‚Šã€ã‚ˆã‚Šæ—©ããƒã‚°ã‚’見ã¤ã‘ã‚‹ãŸã‚ã«å¤šãã®ãƒ†ã‚¹ãƒˆã‚’利用ã—ã¦ã„ã¾ã™
+amo.developerComments.line1=Scriptish 㯠Greasemonkey ã¨åŒã˜ã“ã¨ãŒã§ãã¾ã™ã€‚è¿½åŠ æ©Ÿèƒ½ã®è©³ç´°ï¼šhttp://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=ãƒã‚°ã‚„新機能ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆï¼šhttps://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=ä»–ã®è³ªå•ã¯ãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆï¼šhttp://groups.google.com/group/scriptish
+amo.summary=インターãƒãƒƒãƒˆä¸Šã§æœ€ã‚‚優れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トエンジン。
+extensions.scriptish@erikvold.com.description=Firefox 用ユーザースクリプトマãƒãƒ¼ã‚¸ãƒ£ãƒ¼
diff --git a/extension/locale/ja-JP/scriptish.properties b/extension/locale/ja-JP/scriptish.properties
index 38b0c284..fbd5518e 100644
--- a/extension/locale/ja-JP/scriptish.properties
+++ b/extension/locale/ja-JP/scriptish.properties
@@ -1,77 +1,170 @@
-copyDownloadURL=ダウンãƒãƒ¼ãƒ‰ã™ã‚‹URLã®ã‚³ãƒ”ー
+contributions.description=ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—ト開発者ã¯é–‹ç™ºç¶™ç¶šã®ãŸã‚ã€å°‘é¡ã®å¯„付ã«ã‚ˆã‚‹æ”¯æ´ã‚’求ã‚ã¦ã„ã¾ã™ã€‚
+copyDownloadURL=ダウンãƒãƒ¼ãƒ‰ URL をコピー
+copyDownloadURL.ak=C
edit=編集
-editor.couldNotLaunch=エディタã®èµ·å‹•ã«å¤±æ•—ã—ã¾ã—ãŸã€‚
-editor.pleasePickExecutable=ユーザースクリプトを編集ã™ã‚‹ã«ã¯å®Ÿè¡Œå¯èƒ½ãªã‚¢ãƒ—リケーションをé¸ã‚“ã§ä¸‹ã•ã„。
-editor.prompt=最åˆã«ãŠå¥½ã¿ã®ã‚¨ãƒ‡ã‚£ã‚¿ã‚’指定ã—ã¦ãã ã•ã„。
-error.api.clipboard.type=GM_setClipboardã«ã‚ˆã£ã¦ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„åž‹ã§ã™ã€‚
-error.api.noResourceWithName=
-error.api.noSecondArgValue=
-error.api.reqURL.scheme=
-error.api.reqURL=䏿£ãªURLã§ã™ã€‚
-error.api.unsafeAccess=Scriptishアクセスé•å: unsafeWindowã¯ä»¥ä¸‹ã‚’呼ã³å‡ºã™ã“ã¨ãŒå‡ºæ¥ã¾ã›ã‚“ 
-error.charset=䏿£ãªã‚ャラセットãŒå®šç¾©ã•れã¾ã—ãŸã€‚
-error.dependency.loading=ä¾å˜é–¢ä¿‚ã®èªã¿è¾¼ã¿ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸã€‚
-error.dependency.local=SecurityException: ãƒãƒ¼ã‚«ãƒ«ã¨chromeURLã¸ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ç¦æ¢ã•れã¦ã„ã¾ã™ã€‚
-error.dependency.serverReturned=エラー! サーãƒãƒ¼ã¯ä»¥ä¸‹ã‚’è¿”ã—ã¾ã—ãŸã€€
-error.hash.algorithm=䏿£ãªhash algorithmãŒå®šç¾©ã•れã¾ã—ãŸã€‚
-error.icon.dataURL=䏿£ãªãƒ‡ãƒ¼ã‚¿ã§ã™: @icon用ã®URL 
-error.icon.notImage=エラー! @icon ã¯ç”»åƒã®MIMEタイプをæŒã¡ã¾ã›ã‚“。
-error.icon.URL=@icon用ã®URLãŒä¸æ£ã§ã™ã€‚
-error.isInvalidValue=ã¯ä¸æ£ãªå€¤ã§ã™ã€‚
-error.matchPattern.rules.file=
-error.matchPattern.rules=@matchパターンãŒãƒ‘ターンã®ãƒ«ãƒ¼ãƒ«ã«åˆã„ã¾ã›ã‚“。
-error.notSupported.Firefox=ã¯ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®Firefoxã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。
-error.observerNotFound=
-error.openingFile=ファイルを開ãã“ã¨ãŒå‡ºæ¥ã¾ã›ã‚“。
-error.pattern.parsing=パターンã¯ãƒ‘ースã•れã¾ã›ã‚“ã§ã—ãŸã€‚
-error.pref.type=é¸æŠžã•れãŸåž‹ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 æ£ã—ã„åž‹ã¯æ¬¡ã®ã‚‚ã®ã§ã™: string, bool, 32ビットintegersã§ã™ã€‚
-error.resource.dupName=ã¯é‡è¤‡ã—ãŸã‚Šã‚½ãƒ¼ã‚¹åã§ã™ã€‚ ã©ã®@resourceも一æ„ã®åå‰ã‚’æŒã¤ã¹ãã§ã™ã€‚
-error.resource.syntax=@resource宣言ã®ã‚·ãƒ³ã‚¿ãƒƒã‚¯ã‚¹ãŒä¸æ£ã§ã™ã€‚
-error.retrieving=Failed to retrieve
-error.script.installing=ユーザースクリプトã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚
-error.script.loading=ユーザースクリプトã®ãƒãƒ¼ãƒ‰ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚
-greeting.btn=インストール
+edit.ak=E
+editor.couldNotLaunch=エディタã®èµ·å‹•ã«å¤±æ•—ã—ã¾ã—ãŸ
+editor.pleasePickExecutable=ユーザースクリプトを編集ã™ã‚‹ã‚¢ãƒ—リケーションを指定ã—ã¦ãã ã•ã„
+editor.prompt=ã‚¨ãƒ‡ã‚£ã‚¿ã‚’é¸æŠžã—ã¦ãã ã•ã„
+editor.useScratchpad=エディタã¨ã—ã¦ã‚¹ã‚¯ãƒ©ãƒƒãƒãƒ‘ッドを使用ã—ã¾ã™ã‹ï¼Ÿ
+editor.useScratchpad.no=No(自分ã§ã‚¨ãƒ‡ã‚£ã‚¿ã‚’指定)
+editor.useScratchpad.yes=Yes
+error.api.badArguments=
+error.api.noResourceWithName=åå‰ä»˜ãリソースãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“
+error.api.noSecondArgValue=2番目ã®å¼•æ•°ãŒæŒ‡å®šã•れã¦ã„ã¾ã›ã‚“:Value
+error.api.prefNotFound=
+error.api.reqURL=䏿£ãª URL ã§ã™
+error.api.reqURL.scheme=許å¯ã•れã¦ã„ãªã„スã‚ーム(scheme)を URL ã§æŒ‡å®šã—ã¦ã„ã¾ã™
+error.api.safeHTMLParser.url=GM_safeHTMLParser ã®å¼•æ•° "URL" をパース(parse)ã§ãã¾ã›ã‚“
+error.api.unsafeAccess=Scriptish アクセスé•å: unsafeWindow ã¯ä»¥ä¸‹ã‚’呼ã³å‡ºã™ï¼ˆcall)ã“ã¨ãŒã§ãã¾ã›ã‚“
+error.charset=䏿£ãªæ–‡å—ã‚»ãƒƒãƒˆãŒæŒ‡å®šã•れã¾ã—ãŸ
+error.dependency.loading=ä¾å˜é–¢ä¿‚ã®èªã¿è¾¼ã¿ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ
+error.dependency.local=ã‚»ã‚ュリティ例外: ãƒãƒ¼ã‚«ãƒ«åŠã³ chrome URL ã¸ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ç¦æ¢ã•れã¦ã„ã¾ã™
+error.dependency.serverReturned=ã‚¨ãƒ©ãƒ¼ï¼ ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®å¿œç”:
+error.hash.algorithm=䏿£ãªãƒãƒƒã‚·ãƒ¥ãƒ»ã‚¢ãƒ«ã‚´ãƒªã‚ºãƒ ãŒæŒ‡å®šã•れã¾ã—ãŸ
+error.icon.dataURL=䏿£ãªãƒ‡ãƒ¼ã‚¿ï¼š @icon 用 URL
+error.icon.notImage=ã‚¨ãƒ©ãƒ¼ï¼ @icon ãŒç”»åƒã® MIME タイプã§ã¯ã‚りã¾ã›ã‚“
+error.icon.URL=@icon ã® URL ãŒä¸æ£ã§ã™
+error.invalidCert=䏿£ãª SSL 証明書ã§ã™ã€‚\nå¿…è¦ã«å¿œã˜ã¦ [Scriptish オプション]>[詳細]>[æ›´æ–°ã«é–¢ã™ã‚‹ã‚»ã‚ュリティ] を調節ã—ã¦ã¿ã¦ãã ã•ã„。
+error.isInvalidValue=ã¯ä¸æ£ãªå€¤ã§ã™
+error.matchPattern.rules=@match ã®ãƒ‘ターンãŒè¨˜æ³•ã«é©åˆã—ã¦ã„ã¾ã›ã‚“
+error.matchPattern.rules.file=ファイル・スã‚ーム(scheme)㮠@match パターンãŒè¨˜æ³•ã«é©åˆã—ã¦ã„ã¾ã›ã‚“
+error.notSecure=安全ã§ã¯ãªã„ URL ã§ã™ã€‚\nå¿…è¦ã«å¿œã˜ã¦ [Scriptish オプション]>[詳細]>[æ›´æ–°ã«é–¢ã™ã‚‹ã‚»ã‚ュリティ] を調節ã—ã¦ã¿ã¦ãã ã•ã„。
+error.notSupported.Firefox=ã¯ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã® Firefox ã§ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“
+error.openingFile=ファイルを開ãã“ã¨ãŒå‡ºæ¥ã¾ã›ã‚“ã§ã—ãŸ
+error.pattern.parsing=パターンを構文解æžï¼ˆparse)ã§ãã¾ã›ã‚“ã§ã—ãŸ
+error.pref.type=サãƒãƒ¼ãƒˆã—ã¦ãªã„è¨å®šã®åž‹ã§ã™ã€‚æ£ã—ã„åž‹ã¯æ¬¡ã®é€šã‚Šï¼š string, bool, 32-bit integers
+error.remoteVersionOlder=リモートã«ãƒãƒ¼ã‚«ãƒ«ã‚ˆã‚Šæ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç•ªå·ã®ã‚¹ã‚¯ãƒªãƒ—トã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ
+error.resource.dupName=ã¯ãƒªã‚½ãƒ¼ã‚¹åã¨ã—ã¦é‡è¤‡ã—ã¦ã„ã¾ã™ã€‚@resource ã®åå‰ã¯ä¸€æ„ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“
+error.resource.syntax=@resource å®£è¨€ã®æ§‹æ–‡ãŒä¸æ£ã§ã™
+error.retrieving=å–å¾—ã«å¤±æ•—ã—ã¾ã—ãŸ
+error.script.installing=ユーザースクリプトã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ
+error.script.loading=ユーザースクリプトã®èªã¿è¾¼ã¿ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ
greeting.btn.ak=I
-greeting.msg=ユーザースクリプトã§ã™ã€‚使用を開始ã™ã‚‹ã«ã¯ã‚¯ãƒªãƒƒã‚¯ã—ã¦ä¸‹ã•ã„。
-install.excludes=スクリプト無効:
-install.installButton=インストール
-install.matches=一致:
-install.includes=スクリプト有効:
+greeting.msg=ã“れã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トã§ã™ã€‚使用を開始ã™ã‚‹ã«ã¯ã€Œã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã€ã‚’クリック
+install=インストール
+install.domains=domains:
+install.excludes=does not run on:
+install.includes=runs on:
+install.matches=matches:
+install.requires=requires:
+install.resources=resources:
install.showScriptSource=スクリプトã®ã‚½ãƒ¼ã‚¹ã‚’表示
install.title=ユーザースクリプトã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«
-install.warning1=悪æ„ã®ã‚るスクリプトãŒãƒ—ライãƒã‚·ãƒ¼ã‚’侵害ã—ã¦çŸ¥ã‚‰ãªã„ã†ã¡ã«å‹•作ã™ã‚‹ã‹ã‚‚ã—れã¾ã›ã‚“。
-install.warning2=ä¿¡é ¼ã§ãるソースã‹ã‚‰ã®ã¿ã‚¹ã‚¯ãƒªãƒ—トをインストールã™ã‚‹ã¹ãã§ã™ã€‚
-menu.commands=ユーザースクリプトコマンド...
-menu.install=ユーザースクリプトã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«...
-menu.manage=ユーザースクリプト管ç†...
-menu.new=æ–°è¦ã‚¹ã‚¯ãƒªãƒ—ト...
-menu.show=ソースを見る
-menuitem.install=ã“ã®ã‚¹ã‚¯ãƒªãƒ—トをインストール
-menuitem.manage=ユーザースクリプト管ç†
-menuitem.new=æ–°ã—ã„ユーザースクリプト
-newscript.description=説明
-newscript.excludes=除外サイト (1行ãšã¤æŒ‡å®š)
-newscript.exists=ãã®åå‰ã®ã‚¹ã‚¯ãƒªãƒ—トã¯ã™ã§ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã™ã€‚\n上書ãã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹?
-newscript.includes=有効サイト (1行ãšã¤æŒ‡å®š)
-newscript.name=åå‰
-newscript.namespace=åå‰ç©ºé–“(URI)
-newscript.noID=スクリプトã®IDを確èªã—ã¦ãã ã•ã„。
-newscript.noName=スクリプトã«åå‰ã‚’ã¤ã‘ã¦ä¸‹ã•ã„。
+install.warning1=悪æ„ã®ã‚るスクリプトã¯ã‚ãªãŸã®çŸ¥ã‚‰ã¬é–“ã«ãƒ—ライãƒã‚·ãƒ¼ã‚’侵害ã™ã‚‹ã“ã¨ãŒã‚りã¾ã™
+install.warning2=ä¿¡é ¼ã§ãã‚‹å ´æ‰€ã‹ã‚‰ã®ã¿ã‚¹ã‚¯ãƒªãƒ—トをインストールã™ã¹ãã§ã™
+installFromFile=ファイルã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トをインストールã™ã‚‹...
+installFromFile.ak=U
+menu.commands=ユーザースクリプトã®ã‚³ãƒžãƒ³ãƒ‰...
+menu.commands.ak=C
+menu.install=ユーザースクリプトをインストール...
+menu.install.ak=I
+menu.manage=ユーザースクリプト管ç†â€¦
+menu.manage.ak=M
+menu.new=æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—ト...
+menu.new.ak=N
+menu.options.ak=O
+menu.show=ユーザースクリプトã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’表示
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トをインストール...
+menuitem.manage=ユーザースクリプトを管ç†
+menuitem.new=æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—ト
+moving.dependency=ä¾å˜é–¢ä¿‚ファイルを次ã‹ã‚‰ç§»å‹•:
+moving.script=スクリプトファイルを次ã‹ã‚‰ç§»å‹•:
+newscript.author=作者
+newscript.description=スクリプトã®èª¬æ˜Ž
+newscript.excludes=Excludes(1行1ã¤ãšã¤ï¼‰
+newscript.exists=åŒã˜åå‰ã®ã‚¹ã‚¯ãƒªãƒ—ãƒˆãŒæ—¢ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã™ã€‚\n上書ãã—ã¾ã™ã‹ï¼Ÿ
+newscript.id=ID(一æ„ã«è˜åˆ¥ã™ã‚‹æ–‡å—列)
+newscript.includes=Includes(1行1ã¤ãšã¤ï¼‰
+newscript.missing.id=ID を指定ã—ã¦ãã ã•ã„
+newscript.missing.name=スクリプトåを指定ã—ã¦ãã ã•ã„。アドオン・マãƒãƒ¼ã‚¸ãƒ£ãƒ¼å†…ã§ã®è¡¨ç¤ºã§ä½¿ç”¨ã—ã¾ã™
+newscript.name=スクリプトå
+newscript.namespace=åå‰ç©ºé–“
+newscript.noID=ã‚¹ã‚¯ãƒªãƒ—ãƒˆã«æ£ã—ã„ ID を指定ã—ã¦ãã ã•ã„
+newscript.noName=スクリプトã«åå‰ã‚’ã¤ã‘ã¦ãã ã•ã„
+newscript.runat=スクリプト実行タイミング
+newscript.runat.documentend=Document ã®èªã¿è¾¼ã¿å®Œäº†æ™‚
+newscript.runat.documentidle=Document ã®åˆå›žã‚¢ã‚¤ãƒ‰ãƒ«æ™‚
+newscript.runat.documentstart=Document ã®èªã¿è¾¼ã¿é–‹å§‹æ™‚
+newscript.runat.windowload=全メディアをå«ã‚€ Window ãŒå®Œå…¨ã«èªã¿è¾¼ã¾ã‚ŒãŸæ™‚
+newscript.version=ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+nothing.timedOut=ãªã—(タイムアウト)
openFolder=フォルダを開ã
+openFolder.ak=O
+openUserScriptsManager=ユーザースクリプトマãƒãƒ¼ã‚¸ãƒ£ã‚’é–‹ã
+openUserScriptsManager.ak=O
options=オプション
-options.addonManager=アドオン管ç†
-options.alsoUninstallPrefs=関連è¨å®šã‚‚アンインストールã™ã‚‹
+options.addonsManager=アドオン・マãƒãƒ¼ã‚¸ãƒ£ãƒ¼
+options.alsoUninstallPrefs=スクリプトã®ã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«æ™‚ã«è¨å®šã‚‚削除
+options.cache=ã‚ャッシュ
+options.cache.enabled=ユーザースクリプトをã‚ャッシュ
options.changeEditor=エディタ変更
options.editor=エディタ
-options.enableCopyDownloadURL= ダウンãƒãƒ¼ãƒ‰URLコピー有効化
-options.useDownloadURL=ダウンãƒãƒ¼ãƒ‰URLã®æŒ‡å®šãŒãªã„å ´åˆã‚‚URLを使ã„ã¾ã™ã‹ï¼Ÿ
+options.editor.notset=ã‚¨ãƒ‡ã‚£ã‚¿ãŒæŒ‡å®šã•れã¦ã„ã¾ã›ã‚“
+options.enableCopyDownloadURL=ダウンãƒãƒ¼ãƒ‰ URL ã®ã‚³ãƒ”ーを有効
+options.enabledSchemes.about=about プãƒãƒˆã‚³ãƒ«ï¼ˆå†…部)
+options.enabledSchemes.advanced=利用プãƒãƒˆã‚³ãƒ«ã®è¿½åŠ
+options.enabledSchemes.chrome=chrome プãƒãƒˆã‚³ãƒ«ï¼ˆå†…部)
+options.enabledSchemes.data=データ・プãƒãƒˆã‚³ãƒ«
+options.enabledSchemes.file=ファイル・プãƒãƒˆã‚³ãƒ«ï¼ˆãƒãƒ¼ã‚«ãƒ«ãƒ•ァイル)
+options.enabledSchemes.ftp=ftp プãƒãƒˆã‚³ãƒ«
+options.enabledSchemes.http=http/https プãƒãƒˆã‚³ãƒ«
+options.enabledSchemes.main=利用プãƒãƒˆã‚³ãƒ«
+options.enabledSchemes.securityRemark=è¿½åŠ ã—ãŸãƒ—ãƒãƒˆã‚³ãƒ«ã§ã‚·ã‚¹ãƒ†ãƒ ã«ãƒ•ルアクセスå¯èƒ½ã«ãªã‚‹ã“ã¨ãŒã‚りã¾ã™
+options.enabledSchemes.securityRemark2=ã¤ã¾ã‚Šã€ä¿¡ç”¨ã§ããªã„スクリプトã¨ä½µç”¨ã™ã‚‹ã¨ç›¸å½“ãªã‚»ã‚ュリティ・リスクã«ãªã‚‹ã®ã§æ³¨æ„ï¼
+options.enabledSchemes.unmht=unmht プãƒãƒˆã‚³ãƒ«ï¼ˆMozilla Archive フォーマット)
+options.excludes.desc=Global excludes ã¯ã‚¹ã‚¯ãƒªãƒ—トやユーザー指定ルールより優先
+options.excludes.empty=global excludes ã¯æŒ‡å®šã•れã¦ã„ã¾ã›ã‚“
+options.excludes.remark=(1行1ã¤ãšã¤é™¤å¤–ã™ã‚‹ã‚‚ã®ã‚’指定)
+options.logChrome=é‡å¤§ï¼ˆCritical)ã§ãªã„エクステンションã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’エラーコンソールã«å‡ºåŠ›
+options.logging=ãƒã‚°
+options.logToErrorConsole=GM_log ã§ã‚¨ãƒ©ãƒ¼ã‚³ãƒ³ã‚½ãƒ¼ãƒ«ã‚’使用
+options.notifications=通知
+options.notifications.popup.enable=ãƒãƒƒãƒ—アップ通知ã™ã‚‹
+options.notifications.sliding.enable=スライド通知ã™ã‚‹
+options.notifications.whenDisabled=無効ã®å ´åˆã¯ã‚¨ãƒ©ãƒ¼ã‚³ãƒ³ã‚½ãƒ¼ãƒ«ã«å‡ºåŠ›ã•れã¾ã™ã€‚
+options.pane.advanced=詳細
+options.pane.excludes=Global Excludes
+options.pane.main=メイン
+options.pane.ui=ユーザーインタフェース
+options.requireBuiltInCerts=å®‰å…¨ãªæ›´æ–°ã« built-in 証明書を利用
+options.requireSecured=HTTPS ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¹ã‚¯ãƒªãƒ—トを更新
+options.sync.desc=Firefox Sync サービスåŠã³ã€ãã®è¨å®šã‚’有効ã«ã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ï¼
+options.sync.ScriptishPrefs=Scriptish è¨å®š
+options.sync.ScriptishPrefs.common=Scriptish æ‹¡å¼µã®è¨å®šå€¤ã‚’åŒæœŸã™ã‚‹
+options.sync.ScriptishPrefs.editor=エディターè¨å®šã‚’åŒæœŸã™ã‚‹
+options.title=Scriptish オプション
+options.toolbarbutton=ツールãƒãƒ¼ã®ãƒœã‚¿ãƒ³
+options.toolbarbutton.showScripts=アクティブãªã‚¹ã‚¯ãƒªãƒ—ト数を表示
+options.translation=翻訳
+options.translation.useEnglish=常ã«è‹±èªžã‚’使用
+options.update.security=æ›´æ–°ã«é–¢ã™ã‚‹ã‚»ã‚ュリティ
+options.useDownloadURL=æ›´æ–° URL ã®æŒ‡å®šãŒç„¡ã„å ´åˆã¯ä»£ã‚りã«ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰æ™‚ã® URL を利用
+reinstall=å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«
+saving=ä¿å˜
+scratchpad.saveAsUserScript=ユーザースクリプトã¨ã—ã¦ä¿å˜
+scratchpad.saveAsUserScript.ak=U
+scriptOptions.disableScriptIncludes=スクリプト内 include を無効。ユーザー指定ã®ã¿ã‚’利用
+scriptOptions.excludes=ユーザー指定 Exclude:
+scriptOptions.includes=ユーザー指定 Include:
statusbar.enabled=有効
statusbar.enabled.ak=E
-statusbar.installed=インストールæˆåŠŸ
-statusbar.modified=変更ã•れã¾ã—ãŸ
-statusbar.noScripts=ç¾åœ¨æœ‰åйãªã‚¹ã‚¯ãƒªãƒ—トãŒã‚りã¾ã›ã‚“!
-statusbar.updated=æ£å¸¸ã«æ›´æ–°ã•れã¾ã—ãŸ
-tooltip.loading=èªã¿è¾¼ã¿ä¸...
+statusbar.installed=ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«æˆåŠŸã—ã¾ã—ãŸ
+statusbar.modified=ã¯å¤‰æ›´ã•れã¾ã—ãŸ
+statusbar.noScripts.excluded=ページãŒé™¤å¤–(exclude)ã•れã¾ã—ãŸï¼
+statusbar.noScripts.notfound=ã“ã®ãƒšãƒ¼ã‚¸ç”¨ã®ã‚¹ã‚¯ãƒªãƒ—トã¯ã‚りã¾ã›ã‚“ï¼
+statusbar.noScripts.scheme=スã‚ーム(Scheme)ãŒç„¡åйã«ãªã£ã¦ã„ã¾ã™ï¼
+statusbar.updated=ã¯æ£å¸¸ã«æ›´æ–°ã•れã¾ã—ãŸ
+sync=Sync
+tooltip.loading=èªã¿è¾¼ã¿ä¸â€¦
Uninstall=アンインストール
+untitledScript=å称未定スクリプト
Update=æ›´æ–°
+userscript=ユーザースクリプト
userscripts=ユーザースクリプト
+userscripts.get=ユーザースクリプトをå–å¾—
+userscripts.noneInstalled=ユーザースクリプトãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã›ã‚“
+warning.returnfrommain=メインスコープã‹ã‚‰ã®"リターン"ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ãªã„ã®ã§ä¿®æ£ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚
diff --git a/extension/locale/pl/description.properties b/extension/locale/pl/description.properties
new file mode 100644
index 00000000..a13a3652
--- /dev/null
+++ b/extension/locale/pl/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish jest współbieżny do Greasemonkey, oferuje:
+amo.description.line2=Wszystkie cechy Greasemonkey ( co jest bardzo przydatne )
+amo.description.line3=Aktualizacja: Skrypty Użytkownika mogą zostać zaktualizowane w Scriptish używając @updateURL
+amo.description.line4=Wiele nowych danych @keys
+amo.description.line5=Wiele nowych dodatków do GM_API
+amo.description.line6=Zwiększoną Wydajność: Szybsza, przejrzystsza baza kodów, która wykorzystuje wszystko to, co mają do zaoferowania ostatnie wersje Firefox-a
+amo.description.line7=Zwiększone Bezpieczeństwo: Scriptfish dostarcza niespotykanych dotąd funkcji bezpieczeństwa
+amo.description.line8=Testowanie: Programiści przeprowadzali liczne testy, aby mieć pewność prawidłowego działania Scriptish-a oraz przyspieszyć wykrywalność licznych błędów.
+amo.developerComments.line1=Scriptish posiada wszystkie zalety Greasemonkey plus wiele innych, o których możesz poczytać na: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=Zgłoszenia dotyczące jakichkolwiek błędów oraz funkcji przesyłać na adres: https://github.com/scriptish/scriptish/issues.
+amo.developerComments.line3=Jeśli masz pytania, proszę przesłać na adres grupy dyskusyjnej: http://groups.google.com/group/scriptish
+amo.summary=Największy silnik skryptowy dla użytkownika w Internecie.
+extensions.scriptish@erikvold.com.description=Script Extension Manager dla Firefox-a
diff --git a/extension/locale/pl/scriptish.properties b/extension/locale/pl/scriptish.properties
new file mode 100644
index 00000000..fc86ee0b
--- /dev/null
+++ b/extension/locale/pl/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=Twórca tego skryptu prosi o drobne wsparcie, w celu kontynuowania procesu rozwoju.
+copyDownloadURL=Kopiuj adres pobierania
+copyDownloadURL.ak=C
+edit=Edycja
+edit.ak=E
+editor.couldNotLaunch=Nie można załadować edytora.
+editor.pleasePickExecutable=Proszę wybrać aplikację przeznaczoną do edycji skryptu użytkownika.
+editor.prompt=Najpierw proszę wybrać swój preferowany edytor tekstu
+editor.useScratchpad=Czy chcesz użyć Scratchpad-a jako edytor?
+editor.useScratchpad.no=Nie ( sam wybiorÄ™ )
+editor.useScratchpad.yes=Tak
+error.api.badArguments=
+error.api.noResourceWithName=Brak źródeł z nazwą
+error.api.noSecondArgValue=Drugi argument nie został określony: Wartość
+error.api.prefNotFound=
+error.api.reqURL=Błędny URL
+error.api.reqURL.scheme=Niedozwolony schemat URL
+error.api.safeHTMLParser.url=Argument "URL" z Parsera GM_safeHTMLParser nie może zostać przetworzony
+error.api.unsafeAccess=Naruszenie zasad dostępu Scriptish: nie można wywołać niebezpiecznego Okna
+error.charset=Określony zestaw znaków jest nieprawidłowy.
+error.dependency.loading=Błąd ładowania zależności
+error.dependency.local=Wyjątek Bezpieczeństwa: Żądanie URL lokalnych i chrome`a jest niedozwolone.
+error.dependency.serverReturned=Błąd! Serwer zwrócił
+error.hash.algorithm=Błędnie określony algorytm hash.
+error.icon.dataURL=Błąd danych: URL dla @icon
+error.icon.notImage=Błąd! @icon nie posiada obrazu typu MIME
+error.icon.URL=Błędny URL dla @icon
+error.invalidCert=Nieprawidłowy certyfikat SSL.\nJeżeli jest on wymagany, to włącz wszystkie certyfikaty poprzez aktywację odpowiednich ustawień w Opcje / Zaawansowane / Aktualizuj Zabezpieczenia.
+error.isInvalidValue=jest błędną wartością
+error.matchPattern.rules=wzór @match nie odpowiada regułom wzoru
+error.matchPattern.rules.file=Schemat wzoru @match dla pliku nie odpowiada regułom wzoru
+error.notSecure=Niezabezpieczony URI.\nJeżeli jest wymagany, to włącz aktualizacje nie-HTTPS-owe poprzez aktywację ustawień w Opcje / Zaawansowane / Aktualizuj Zabezpieczenia.
+error.notSupported.Firefox=nie jest wspierany przez tÄ… wersjÄ™ Firefox-a
+error.openingFile=Nie można otworzyć pliku
+error.pattern.parsing=Wzór nie mógł zostać przetworzony
+error.pref.type=Niewspierany typ preferencji. ObowiÄ…zujÄ…ce typy: string, bool oraz 32-bitowy integer.
+error.remoteVersionOlder=Zdalny skrypt nie posiada nowszej wersji niż obecna.
+error.resource.dupName=jest duplikatem nazwy źródła. Każde @resource musi zawierać unikatową nazwę.
+error.resource.syntax=Błąd składni deklaracji @resource
+error.retrieving=Nie odzyskano
+error.script.installing=Błąd podczas instalowania skryptu użytkownika
+error.script.loading=Błąd podczas ładowania skryptu użytkownika
+greeting.btn.ak=I
+greeting.msg=To jest skrypt użytkownika. Wciśnij Zainstaluj do rozpoczęcia używania.
+install=Zainstaluj
+install.domains=domeny:
+install.excludes=nie działa na:
+install.includes=działa na:
+install.matches=pasuje:
+install.requires=wymaga:
+install.resources=źródła:
+install.showScriptSource=Pokaż Źródło Skryptu
+install.title=Instalacja Skryptu Użytkownika
+install.warning1=Złośliwe skrypty mogą naruszać twoją prywatność i działać w Twoim imieniu bez twojej wiedzy.
+install.warning2=Instaluj skrypty z zaufanych źródeł.
+installFromFile=Zainstaluj Skrypt Użytkownika z Pliku...
+installFromFile.ak=U
+menu.commands=Komendy Skryptu Użytkownika...
+menu.commands.ak=C
+menu.install=Zainstaluj Skrypt Użytkownika
+menu.install.ak=I
+menu.manage=Zarządzaj Skryptami Użytkownika...
+menu.manage.ak=M
+menu.new=Nowy Skrypt Użytkownika...
+menu.new.ak=N
+menu.options.ak=O
+menu.show=Przejrzyj Źródło Skryptu Użytkownika
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=Zainstaluj Ten Skrypt Użytkownika...
+menuitem.manage=Zarządzaj Skryptami Użytkownika
+menuitem.new=Nowy Skrypt Użytkownika
+moving.dependency=Przeniesienie zależności plikowych z
+moving.script=Przeniesienie pliku skryptu z
+newscript.author=Autor
+newscript.description=Opis
+newscript.excludes=WyjÄ…tki (jeden na liniÄ™)
+newscript.exists=Skrypt o tej nazwie został już zainstalowany. Nadpisać?
+newscript.id=ID
+newscript.includes=Zawiera ( JednÄ… na linie )
+newscript.missing.id=Proszę wprowadzić ID.
+newscript.missing.name=Proszę wprowadzić nazwę. Nazwa będzie widoczna w nazwie Menadżera Dodatków.
+newscript.name=Nazwa
+newscript.namespace=Przestrzeń nazw
+newscript.noID=Proszę wprowadzić prawidłowe ID dla swojego skryptu.
+newscript.noName=Proszę wprowadzić nazwę dla swojego skryptu.
+newscript.runat=Rozpocznij kiedy
+newscript.runat.documentend=Koniec ładowania dokumentu
+newscript.runat.documentidle=Bezczyny dokumenty
+newscript.runat.documentstart=Dokument zaczyna się ładować
+newscript.runat.windowload=Okno wraz mediami, są w pełni załadowane
+newscript.version=Wersja
+nothing.timedOut=nic ( upłynął limit czasu )
+openFolder=Otwórz Folder
+openFolder.ak=O
+openUserScriptsManager=Otwórz Menadżera Skryptów Użytkownika
+openUserScriptsManager.ak=O
+options=Opcje
+options.addonsManager=Menadżer Dodatków
+options.alsoUninstallPrefs=Podczas deinstalacji skryptu, usuń również jego ustawienia
+options.cache=Cache
+options.cache.enabled=Cache skryptów użytkownika
+options.changeEditor=Zmień Edytor
+options.editor=Edytor
+options.editor.notset=Nie wybrano edytora
+options.enableCopyDownloadURL=Włącz kopiowanie adresów URL pobierania
+options.enabledSchemes.about=o protokole ( wewnętrznym )
+options.enabledSchemes.advanced=Dodatkowe protokoły
+options.enabledSchemes.chrome=protokół chrome ( wewnętrzny )
+options.enabledSchemes.data=protokół danych
+options.enabledSchemes.file=protokół pliku ( pliki lokalne )
+options.enabledSchemes.ftp=protokół ftp
+options.enabledSchemes.http=protokół http/https
+options.enabledSchemes.main=Protokoły
+options.enabledSchemes.securityRemark=Dodatkowe protokoły mogą posiadać pełny dostęp do systemu.
+options.enabledSchemes.securityRemark2=Dlatego też stanowią spore zagrożenie bezpieczeństwa z niezaufanymi skryptami!
+options.enabledSchemes.unmht=protokół unmht ( format archiwizacji Mozilli )
+options.excludes.desc=Wyjątki globalne nadpiszą każdy skrypt albo reguły zdefiniowane przez użytkownika
+options.excludes.empty=Nie zdefiniowano żadnych wyjątków globalnych
+options.excludes.remark=(Możesz zdefiniować jeden wyjątek na linię)
+options.logChrome=Zapisuj niekrytyczne wiadomości rozszerzenia do konsoli błędów
+options.logging=Logowanie
+options.logToErrorConsole=GM_log używa Konsoli Błędów
+options.notifications=Powiadomienia
+options.notifications.popup.enable=Włącz powiadomienia popup
+options.notifications.sliding.enable=Włącz powiadomienia przesuwane
+options.notifications.whenDisabled=Kiedy wyłączone, wszelkie powiadomienia będą wyświetlane w konsoli błędów.
+options.pane.advanced=Zaawansowane
+options.pane.excludes=WyjÄ…tki globalne
+options.pane.main=Główne
+options.pane.ui=Interfejs Użytkownika
+options.requireBuiltInCerts=Aktualizacja bezpieczeństwa wymaga użycia wbudowanych certyfikatów
+options.requireSecured=Wymagane skrypty użytkownika do aktualizacji przez HTTPS
+options.sync.desc=Usługa Firefox Sync i jej "Preferencje" muszą być włączone!
+options.sync.ScriptishPrefs=Preferencje Scriptish
+options.sync.ScriptishPrefs.common=Synchronizuj wspólne preferencje rozszerzeń Scriptish
+options.sync.ScriptishPrefs.editor=Synchronizuj preferencje Edytora
+options.title=Opcje Scriptish
+options.toolbarbutton=Pasek narzędzi
+options.toolbarbutton.showScripts=Pokaż liczbę aktywnych skryptów
+options.translation=Tłumaczenie
+options.translation.useEnglish=Zawsze używaj angielskiego
+options.update.security=Aktualizacja Bezpieczeństwa
+options.useDownloadURL=Używaj adresu pobierania jeśli nie ma adresu aktualizacji
+reinstall=Reinstalacja
+saving=Zapisywanie
+scratchpad.saveAsUserScript=Zapisz jako Skrypt Użytkownika
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=Wyłącz ten skrypt zawierający wzory i używaj zdefiniowane przez użytkownika.
+scriptOptions.excludes=Wykluczone przez Użytkownika:
+scriptOptions.includes=Zawarte przez Użytkownika:
+statusbar.enabled=Włączone
+statusbar.enabled.ak=E
+statusbar.installed=poprawnie zainstalowano
+statusbar.modified=zmodyfikowano
+statusbar.noScripts.excluded=Strona została wykluczona!
+statusbar.noScripts.notfound=Brak skryptów dla strony!
+statusbar.noScripts.scheme=Schemat został wyłączony!
+statusbar.updated=poprawnie zaktualizowano
+sync=Synchronizuj
+tooltip.loading=Åadowanie...
+Uninstall=Odinstalowanie
+untitledScript=Skrypt bez nazwy
+Update=Aktualizacja
+userscript=Skrypt Użytkownika
+userscripts=Skrypty Użytkownika
+userscripts.get=Zdobądź skrypty użytkownika
+userscripts.noneInstalled=Nie posiadasz zainstalowanych skryptów użytkownika
+warning.returnfrommain="return" z głównego zakresu nie jest wspierany. Powinieneś to poprawić w swoim skrypcie użytkownika.
diff --git a/extension/locale/pt-BR/description.properties b/extension/locale/pt-BR/description.properties
new file mode 100644
index 00000000..f85c7f22
--- /dev/null
+++ b/extension/locale/pt-BR/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=O Scriptish é um fork do Greasemonkey que oferece:
+amo.description.line2=Tudo que o Greasemonkey faz (que é realmente útil)
+amo.description.line3=Atualização: os scripts podem ser atualizados usando @updateURL
+amo.description.line4=Muitos metadados @keys novos
+amo.description.line5=Muitos acréscimos à API GM_
+amo.description.line6=Desempenho superior: um código mais rápido e mais limpo, que tira proveito de tudo o que as versões recentes do Firefox têm a oferecer
+amo.description.line7=Segurança superior: o Scriptish fornece recursos de segurança que você não vai encontrar em outro lugar
+amo.description.line8=Testes: os desenvolvedores empregam uma série de testes para garantir o correto funcionamento do Scriptish e encontrar falhas o mais breve possÃvel
+amo.developerComments.line1=O Scriptish tem todos os recursos que o Greasemonkey tem e muitos mais, que você pode descobrir em: http://github.com/scriptish/scriptish/wiki .
+amo.developerComments.line2=Por favor, envie informações sobre falhas ou solicitações de novos recursos em: https://github.com/scriptish/scriptish/issues .
+amo.developerComments.line3=Se você tiver qualquer dúvida, envie sua pergunta para a lista de discussão: http://groups.google.com/group/scriptish .
+amo.summary=O maior mecanismo de scripts de usuário da Internet.
+extensions.scriptish@erikvold.com.description=Um gerenciador de scripts para o Firefox.
diff --git a/extension/locale/pt-BR/scriptish.properties b/extension/locale/pt-BR/scriptish.properties
new file mode 100644
index 00000000..34ffb07a
--- /dev/null
+++ b/extension/locale/pt-BR/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=O desenvolvedor deste complemento pede sua ajuda para dar suporte ao desenvolvimento através de uma pequena doação.
+copyDownloadURL=Copiar endereço de download
+copyDownloadURL.ak=C
+edit=Editar
+edit.ak=E
+editor.couldNotLaunch=Não foi possÃvel iniciar o editor.
+editor.pleasePickExecutable=Por favor, escolha um aplicativo para ser usado como editor de scripts.
+editor.prompt=Escolha seu editor de texto preferido
+editor.useScratchpad=Você gostaria de usar o Scratchpad como seu editor?
+editor.useScratchpad.no=Não (eu vou escolher outro)
+editor.useScratchpad.yes=Sim
+error.api.badArguments=
+error.api.noResourceWithName=Nenhum recurso com o nome
+error.api.noSecondArgValue=Segundo argumento não especificado: Valor
+error.api.prefNotFound=
+error.api.reqURL=Endereço inválido
+error.api.reqURL.scheme=Esquema não permitido em URL
+error.api.safeHTMLParser.url=
+error.api.unsafeAccess=Violação de acesso do Scriptish: unsafeWindow não pode chamar
+error.charset=O conjunto de caracteres especificado é inválido.
+error.dependency.loading=Erro carregando dependência
+error.dependency.local=Exceção de segurança: não são permitidas solicitações a endereços locais e de chrome
+error.dependency.serverReturned=Erro! O servidor retornou
+error.hash.algorithm=O algoritmo de hash especificado é inválido.
+error.icon.dataURL=Dados inválidos: endereço para @icon
+error.icon.notImage=Erro! @icon não tem um tipo MIME de imagem
+error.icon.URL=Endereço inválido para @icon
+error.invalidCert=
+error.isInvalidValue=é um nome inválido
+error.matchPattern.rules=o padrão de @match não está de acordo com as regras.
+error.matchPattern.rules.file=o padrão de @match do esquema "file:" não está de acordo com as regras.
+error.notSecure=
+error.notSupported.Firefox=não é suportado por esta versão do Firefox
+error.openingFile=Não foi possÃvel abrir o arquivo
+error.pattern.parsing=Não foi possÃvel analisar o padrão
+error.pref.type=Tipo de preferência não suportado. Os tipos válidos são: string, bool e integers de 32 bits.
+error.remoteVersionOlder=
+error.resource.dupName=é um nome de recurso duplicado. Cada item de @resource precisa ter um nome exclusivo.
+error.resource.syntax=sintaxe inválida para a declaração @resource.
+error.retrieving=Falha ao obter
+error.script.installing=Erro ao instalar script
+error.script.loading=Erro ao carregar script
+greeting.btn.ak=I
+greeting.msg=Este é um script de usuário. Clique no botão "Instalar" para começar a usá-lo.
+install=Instalar
+install.domains=domÃnios:
+install.excludes=não se executa em:
+install.includes=funciona em (@include):
+install.matches=funciona em (@match):
+install.requires=necessita de:
+install.resources=recursos:
+install.showScriptSource=Exibir código-fonte do script
+install.title=Instalação de Script
+install.warning1=Scripts nocivos podem violar a sua privacidade e agir em seu nome sem o seu conhecimento.
+install.warning2=Você só deve instalar scripts de fontes em que você confia.
+installFromFile=Instalar script de um arquivo...
+installFromFile.ak=S
+menu.commands=Comandos de script
+menu.commands.ak=C
+menu.install=Instalar script
+menu.install.ak=I
+menu.manage=Gerenciar scripts
+menu.manage.ak=G
+menu.new=Novo script...
+menu.new.ak=N
+menu.options.ak=O
+menu.show=Exibir código-fonte do script
+menu.show.ak=E
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=Instalar este script...
+menuitem.manage=Gerenciar scripts
+menuitem.new=Novo script
+moving.dependency=Movendo arquivo de dependência:
+moving.script=Movendo arquivo de script:
+newscript.author=Autor
+newscript.description=Descrição
+newscript.excludes=Exclusões (uma por linha)
+newscript.exists=Um script com esse nome já está instalado.\nDeseja substituir?
+newscript.id=Identificador
+newscript.includes=Inclusões (uma por linha)
+newscript.missing.id=Informe um identificador.
+newscript.missing.name=Informe um nome para ser exibido no gerenciador de complementos.
+newscript.name=Nome
+newscript.namespace=Espaço de nomes
+newscript.noID=Por favor, informe um identificador válido para o seu script.
+newscript.noName=Por favor, informe um nome para o seu script.
+newscript.runat=Executar quando
+newscript.runat.documentend=o documento terminar de carregar
+newscript.runat.documentidle=o documento estiver ocioso
+newscript.runat.documentstart=o documento começar a carregar
+newscript.runat.windowload=a janela estiver completamente carregada
+newscript.version=Versão
+nothing.timedOut=nada (excedido o tempo limite)
+openFolder=Abrir pasta
+openFolder.ak=A
+openUserScriptsManager=Abrir gerenciador de scripts
+openUserScriptsManager.ak=A
+options=Opções
+options.addonsManager=Gerenciador de complementos
+options.alsoUninstallPrefs=Ao desinstalar um script, remover também as configurações dele
+options.cache=Cache
+options.cache.enabled=Usar cache para scripts
+options.changeEditor=Alterar editor
+options.editor=Editor
+options.editor.notset=Nenhum editor selecionado
+options.enableCopyDownloadURL=Habilitar cópia do endereço de download
+options.enabledSchemes.about=Protocolo about (interno)
+options.enabledSchemes.advanced=Protocolos adicionais
+options.enabledSchemes.chrome=Protocolo chrome (interno)
+options.enabledSchemes.data=Protocolo data
+options.enabledSchemes.file=Protocolo file (arquivos locais)
+options.enabledSchemes.ftp=Protocolo ftp
+options.enabledSchemes.http=Protocolo http/https
+options.enabledSchemes.main=Protocolos
+options.enabledSchemes.securityRemark=Os protocolos adicionais podem ter acesso total ao seu computador.
+options.enabledSchemes.securityRemark2=Portanto, eles representam um risco de segurança considerável com scripts não confiáveis!
+options.enabledSchemes.unmht=Protocolo unmht (formato de arquivamento da Mozilla)
+options.excludes.desc=Exclusões globais substituem qualquer regra especificada por scripts ou pelo usuário.
+options.excludes.empty=Nenhuma exclusão global
+options.excludes.remark=(Informe uma exclusão por linha)
+options.logChrome=Registrar mensagens não-crÃticas da extensão no console de erros
+options.logging=Log
+options.logToErrorConsole=GM_log usa o console de erros
+options.notifications=Notificações
+options.notifications.popup.enable=Habilitar notificações popup
+options.notifications.sliding.enable=Habilitar notificações deslizantes
+options.notifications.whenDisabled=Se esta opção estiver desabilitada, todas as notificações serão registradas no console de erros.
+options.pane.advanced=Avançado
+options.pane.excludes=Exclusões Globais
+options.pane.main=Principal
+options.pane.ui=Interface
+options.requireBuiltInCerts=Exigir que as atualizações seguras usem os certificados internos
+options.requireSecured=Exigir que os scripts atualizem usando HTTPS
+options.sync.desc=
+options.sync.ScriptishPrefs=
+options.sync.ScriptishPrefs.common=
+options.sync.ScriptishPrefs.editor=
+options.title=Opções do Scriptish
+options.toolbarbutton=Botão da barra de ferramentas
+options.toolbarbutton.showScripts=Exibir contador de scripts ativos
+options.translation=Tradução
+options.translation.useEnglish=Sempre exibir no idioma inglês
+options.update.security=Segurança na atualização
+options.useDownloadURL=Usar endereço de download para atualizações se não houver endereço de atualização
+reinstall=Reinstalar
+saving=Salvando
+scratchpad.saveAsUserScript=Salvar como script
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=Desativar as inclusões originais deste script e usar somente as inclusões personalizadas.
+scriptOptions.excludes=Exclusões personalizadas:
+scriptOptions.includes=Inclusões personalizadas:
+statusbar.enabled=Ativado
+statusbar.enabled.ak=A
+statusbar.installed=instalado com sucesso
+statusbar.modified=modificado
+statusbar.noScripts.excluded=Scripts desabilitados nesta página!
+statusbar.noScripts.notfound=Nenhum script para esta página!
+statusbar.noScripts.scheme=Scripts desabilitados para este protocolo!
+statusbar.updated=atualizado com sucesso
+sync=
+tooltip.loading=Carregando...
+Uninstall=Desinstalação
+untitledScript=Script sem nome
+Update=Atualização
+userscript=Script
+userscripts=Scripts
+userscripts.get=Obter scripts
+userscripts.noneInstalled=Você não possui nenhum script instalado.
+warning.returnfrommain=Não é geralmente suportado usar "return" no escopo principal. Você precisa corrigir isso no seu script.
diff --git a/extension/locale/ru-RU/description.properties b/extension/locale/ru-RU/description.properties
new file mode 100644
index 00000000..6efc290a
--- /dev/null
+++ b/extension/locale/ru-RU/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish – Ñто форк Greasemonkey, предлагающий:
+amo.description.line2=Ð’ÑÑ‘, что делает Greasemonkey (что на Ñамом деле полезно)
+amo.description.line3=Обновление: ПользовательÑкие Ñкрипты могут быть обновлены в Scriptish Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ @updateURL
+amo.description.line4=Много новых ключей метаданных
+amo.description.line5=Много новых дополнений к GM_ API
+amo.description.line6=Ð’Ñ‹ÑÐ¾ÐºÐ°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть: более быÑÑ‚Ñ€Ð°Ñ Ð¸ чиÑÑ‚Ð°Ñ ÐºÐ¾Ð´Ð¾Ð²Ð°Ñ Ð±Ð°Ð·Ð°, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¸Ñпользует вÑÑ‘, что могут предложить поÑледние верÑии Firefox
+amo.description.line7=ПревоÑÑ…Ð¾Ð´Ð½Ð°Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°ÑноÑть: Scriptish обеÑпечивает функции безопаÑноÑти, которые вы не найдёте где-либо ещё
+amo.description.line8=ТеÑтирование: разработчики делают множеÑтво теÑтов, чтобы гарантировать правильное функционирование Scriptish и найти какие-либо ошибки как можно Ñкорее
+amo.developerComments.line1=Scriptish имеет вÑÑ‘, что и Greasemonkey, а также множеÑтво функций, о которых вы можете прочитать на: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=ПожалуйÑта, приÑылайте любые ошибки или Ð¿Ð¾Ð¶ÐµÐ»Ð°Ð½Ð¸Ñ Ð½Ð°: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=ЕÑли у Ð²Ð°Ñ ÐµÑть другие вопроÑÑ‹, пожалуйÑта, приÑылайте их в ÑпиÑок раÑÑылки: http://groups.google.com/group/scriptish
+amo.summary=Лучший движок пользовательÑких Ñкриптов в Интернете.
+extensions.scriptish@erikvold.com.description=Менеджер пользовательÑких Ñкриптов Ð´Ð»Ñ Firefox
diff --git a/extension/locale/ru-RU/scriptish.properties b/extension/locale/ru-RU/scriptish.properties
index ceff8bf6..f1947536 100644
--- a/extension/locale/ru-RU/scriptish.properties
+++ b/extension/locale/ru-RU/scriptish.properties
@@ -1,70 +1,170 @@
-edit=Редактировать
-editor.couldNotLaunch=Ðе могу запуÑтить редактор.
-editor.pleasePickExecutable=ПожалуйÑта, выберите приложение Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкриптов
-editor.prompt=ПожалуйÑта Ñначала выберите ваш редактор Ñкриптов
-greeting.btn=УÑтановить
-greeting.msg=Ðто юзерÑкрипт. Ðажмите "УÑтановить" чтобы начать иÑпользовать его.
+contributions.description=Разработчик Ñтого пользовательÑкого Ñкрипта проÑит Ð²Ð°Ñ Ð¿Ð¾Ð¼Ð¾Ñ‡ÑŒ поддержать его дальнейшее развитие, Ñделав небольшое пожертвование.
+copyDownloadURL=Скопировать URL загрузки
+copyDownloadURL.ak=C
+edit=Изменить
+edit.ak=E
+editor.couldNotLaunch=Ðе удалоÑÑŒ запуÑтить редактор.
+editor.pleasePickExecutable=ПожалуйÑта, выберите приложение Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкриптов.
+editor.prompt=ПожалуйÑта, выберите ваш любимый редактор Ñкриптов
+editor.useScratchpad=Ð’Ñ‹ хотите иÑпользовать Scratchpad в качеÑтве редактора?
+editor.useScratchpad.no=Ðет (Ñ Ð²Ñ‹Ð±ÐµÑ€Ñƒ Ñвой​​)
+editor.useScratchpad.yes=Да
+error.api.badArguments=
+error.api.noResourceWithName=Ðет реÑурÑа Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем
+error.api.noSecondArgValue=Второй аргумент - значение - не задан
+error.api.prefNotFound=
+error.api.reqURL=Ðеверный URL
+error.api.reqURL.scheme=Ð—Ð°Ð¿Ñ€ÐµÑ‰Ñ‘Ð½Ð½Ð°Ñ Ñхема в URL
+error.api.safeHTMLParser.url=Ðе удалоÑÑŒ разобрать аргумент «URL» из GM_safeHTMLParser
+error.api.unsafeAccess=Ðарушение доÑтупа Scriptish: unsafeWindow не может работать Ñ
+error.charset=ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ°.
+error.dependency.loading=Ошибка при загрузке завиÑимоÑти
+error.dependency.local=ИÑключение безопаÑноÑти: запроÑÑ‹ к локальным и chrome URL запрещены
+error.dependency.serverReturned=Ошибка! Сервер вернул
+error.hash.algorithm=Ðеверный алгоритм хешированиÑ.
+error.icon.dataURL=Ðеверные данные: URL Ð´Ð»Ñ @icon
+error.icon.notImage=Ошибка! @icon не имеет тип Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ MIME
+error.icon.URL=Ðеверный URL Ð´Ð»Ñ @icon
+error.invalidCert=Ðеверный Ñертификат SSL.\nЕÑли необходимо разрешить вÑе Ñертификаты, включите опцию в наÑтройках Scriptish / Дополнительные / БезопаÑноÑть обновлениÑ.
+error.isInvalidValue=- неверное значение
+error.matchPattern.rules=Шаблон @match не ÑоответÑтвует правилам шаблона
+error.matchPattern.rules.file=Файл Ñхемы @match не ÑоответÑтвует правилам шаблона
+error.notSecure=ÐебезопаÑный URI.\nЕÑли необходимо разрешить не-HTTPS обновлениÑ, включите опцию в наÑтройках Scriptish / Дополнительные / БезопаÑноÑть обновлениÑ.
+error.notSupported.Firefox=не поддерживаетÑÑ Ð² Ñтой верÑии Firefox
+error.openingFile=Ðе удалоÑÑŒ открыть файл
+error.pattern.parsing=Шаблон не парÑитÑÑ
+error.pref.type=Ðеподдерживаемый тип наÑтройки. ДопуÑтимые типы: Ñтрока, логичеÑкий и 32-битное целое чиÑло.
+error.remoteVersionOlder=Удалённый Ñкрипт имеет более Ñтарую верÑию, чем ваш текущий Ñкрипт.
+error.resource.dupName=- повторÑющееÑÑ Ð¸Ð¼Ñ Ñ€ÐµÑурÑа. Имена реÑурÑов должны быть уникальными.
+error.resource.syntax=Ðеправильный ÑинтакÑÐ¸Ñ Ð² обьÑвлении @resource
+error.retrieving=Ðе удалоÑÑŒ получить
+error.script.installing=Ошибка при уÑтановке пользовательÑкого Ñкрипта
+error.script.loading=Ошибка при загрузке пользовательÑкого Ñкрипта
+greeting.btn.ak=I
+greeting.msg=Ðто пользовательÑкий Ñкрипт. Ðажмите «УÑтановить», чтобы начать иÑпользовать его.
+install=УÑтановить
+install.domains=домены:
install.excludes=не запуÑкать на:
-install.installButton=УÑтановить
-install.matches=Ñовпадений:
install.includes=запуÑк на:
+install.matches=Ñовпадений:
+install.requires=требуетÑÑ:
+install.resources=реÑурÑÑ‹:
install.showScriptSource=Показать иÑходный код Ñкрипта
install.title=УÑтановщик Ñкриптов
-install.warning1=ВредоноÑные Ñкрипты могут нарушить Вашу конфиденциальноÑть и дейÑтвовать от Вашего имени без Вашего ÑоглаÑиÑ.
-install.warning2=Ð’Ñ‹ должны уÑтанавливать Ñкрипты только из доверенных иÑточников.
-menu.commands=Команды Ñкрипта...
-menu.install=УÑтановить Ñкрипт...
-menu.manage=Управление пользовательÑкими Ñкриптами...
-menu.new=Создать Ñкрипт...
+install.warning1=ВредоноÑные Ñкрипты могут нарушить вашу конфиденциальноÑть и дейÑтвовать от вашего имени без вашего ÑоглаÑиÑ.
+install.warning2=УÑтанавливайте Ñкрипты только из доверенных иÑточников.
+installFromFile=УÑтановить пользовательÑкий Ñкрипт из файла…
+installFromFile.ak=U
+menu.commands=Команды Ñкрипта…
+menu.commands.ak=C
+menu.install=УÑтановить Ñкрипт…
+menu.install.ak=I
+menu.manage=Управление Ñкриптами…
+menu.manage.ak=M
+menu.new=Создать Ñкрипт…
+menu.new.ak=N
+menu.options.ak=O
menu.show=ПоÑмотреть иÑходный код Ñкрипта
-menuitem.install=УÑтановить Ñтот Ñкрипт...
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=УÑтановить Ñтот Ñкрипт…
menuitem.manage=Управление Ñкриптами
menuitem.new=Ðовый Ñкрипт
+moving.dependency=Перемещение завиÑимых файлов из
+moving.script=Перемещение файла Ñкрипта из
+newscript.author=Ðвтор
newscript.description=ОпиÑание
-newscript.excludes=ИÑключать Ñтраницы (Одно правило на Ñтрочку)
+newscript.excludes=ИÑключать Ñтраницы (одна на Ñтроку)
newscript.exists=Скрипт Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем уже уÑтановлен.\nПерезапиÑать?
-newscript.includes=Включать Ñтраницы (Одно правило на Ñтрочку)
+newscript.id=Идентификатор
+newscript.includes=Включать Ñтраницы (одна на Ñтроку)
+newscript.missing.id=ПожалуйÑта, укажите идентификатор.
+newscript.missing.name=Укажите имÑ, которое будет отображатьÑÑ Ð² Менеджере дополнений.
newscript.name=ИмÑ
-newscript.noID=ПожалуйÑта, введите идентификатор Ñкрипта
-newscript.noName=ПожалуйÑта, задайте Ð¸Ð¼Ñ Ñкрипта.
-openFolder=Открыть папку Ñкрипта.
+newscript.namespace=ПроÑтранÑтво имён
+newscript.noID=ПожалуйÑта, введите допуÑтимый идентификатор Ñкрипта.
+newscript.noName=ПожалуйÑта, введите Ð¸Ð¼Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ Ñкрипта.
+newscript.runat=ЗапуÑк при
+newscript.runat.documentend=Завершении загрузки документа
+newscript.runat.documentidle=Первом проÑтое документа
+newscript.runat.documentstart=Ðачале загрузки документа
+newscript.runat.windowload=Полной загрузке окна, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð²Ñе медиаданные
+newscript.version=ВерÑиÑ
+nothing.timedOut=ничего (иÑтекло)
+openFolder=Открыть папку
+openFolder.ak=O
+openUserScriptsManager=Открыть менеджер Ñкриптов
+openUserScriptsManager.ak=O
options=ÐаÑтройки
-options.alsoUninstallPrefs=УдалÑть также аÑÑоциированные наÑтройки
+options.addonsManager=Менеджер дополнений
+options.alsoUninstallPrefs=При удалении Ñкрипта также удалÑть его наÑтройки
+options.cache=КÑш
+options.cache.enabled=КÑшировать пользовательÑкие Ñкрипты
options.changeEditor=Изменить редактор
options.editor=Редактор
-options.useDownloadURL=ИÑпользуйте URL Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ в качеÑтве запаÑного URL Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ (еÑли он не определен)?
+options.editor.notset=Редактор не выбран
+options.enableCopyDownloadURL=Разрешить копирование URL загрузки
+options.enabledSchemes.about=Протокол about (внутренний)
+options.enabledSchemes.advanced=Дополнительные протоколы
+options.enabledSchemes.chrome=Протокол chrome (внутренний)
+options.enabledSchemes.data=Протокол данных
+options.enabledSchemes.file=Файловый протокол (локальные файлы)
+options.enabledSchemes.ftp=Протокол FTP
+options.enabledSchemes.http=Протокол HTTP/HTTPS
+options.enabledSchemes.main=Протоколы
+options.enabledSchemes.securityRemark=Дополнительные протоколы могут иметь полный доÑтуп к ÑиÑтеме, поÑтому
+options.enabledSchemes.securityRemark2=предÑтавлÑÑŽÑ‚ значительную угрозу безопаÑноÑти от ненадёжных Ñкриптов!
+options.enabledSchemes.unmht=Протокол UnMHT (формат архива Mozilla)
+options.excludes.desc=Глобальное иÑключение переопределит любой Ñкрипт или правила пользователÑ
+options.excludes.empty=Глобальные иÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð½Ðµ заданы
+options.excludes.remark=(Можно указать одно иÑключение на Ñтроку)
+options.logChrome=ЗапиÑывать некритичеÑкие ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² конÑоли ошибок
+options.logging=Ведение журнала
+options.logToErrorConsole=GM_log иÑпользует конÑоль ошибок
+options.notifications=УведомлениÑ
+options.notifications.popup.enable=Включить вÑплывающие уведомлениÑ
+options.notifications.sliding.enable=Включить ÑкользÑщие уведомлениÑ
+options.notifications.whenDisabled=Когда отключено, любые ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ поÑвлÑтьÑÑ Ð² конÑоли ошибок.
+options.pane.advanced=Дополнительные
+options.pane.excludes=Глобальные иÑключениÑ
+options.pane.main=ОÑновные
+options.pane.ui=ИнтерфейÑ
+options.requireBuiltInCerts=ИÑпользовать вÑтроенные Ñертификаты Ð´Ð»Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного обновлениÑ
+options.requireSecured=Разрешить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñкриптов только по протоколу HTTPS
+options.sync.desc=Служба Ñнхронизации Firefox и её Ð¾Ð¿Ñ†Ð¸Ñ Â«ÐаÑтройки» должны быть включены!
+options.sync.ScriptishPrefs=ÐŸÑ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Scriptish
+options.sync.ScriptishPrefs.common=Синхронизировать общие наÑтройки Scriptish
+options.sync.ScriptishPrefs.editor=Синхронизировать наÑтройки редактора
+options.title=ÐаÑтройки Scriptish
+options.toolbarbutton=Кнопка панели инÑтрументов
+options.toolbarbutton.showScripts=Показывать чиÑло активных Ñкриптов
+options.translation=Перевод
+options.translation.useEnglish=Ð’Ñегда иÑпользовать английÑкий Ñзык
+options.update.security=БезопаÑноÑть обновлениÑ
+options.useDownloadURL=ИÑпользовать URL Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ обновлений, еÑли не указан URL обновлениÑ
+reinstall=ПереуÑтановить
+saving=Сохранение
+scratchpad.saveAsUserScript=Сохранить как пользовательÑкий Ñкрипт
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=Отключить заданные в Ñкрипте Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¸ иÑпользовать только Ñти.
+scriptOptions.excludes=ПользовательÑкие иÑключениÑ:
+scriptOptions.includes=ПользовательÑкие включениÑ:
statusbar.enabled=Включено
+statusbar.enabled.ak=E
statusbar.installed=уÑпешно уÑтановлен
-statusbar.modified=изменен
-statusbar.noScripts=Ðет Ñкриптов Ð´Ð»Ñ Ñтраницы!
-statusbar.updated=уÑпешно обновлен(Ñ‹)
-tooltip.loading=Грузим ...
+statusbar.modified=изменён
+statusbar.noScripts.excluded=Страница иÑключена!
+statusbar.noScripts.notfound=Ðет Ñкриптов Ð´Ð»Ñ Ñтраницы!
+statusbar.noScripts.scheme=Схема отключена!
+statusbar.updated=уÑпешно обновлён
+sync=СинхронизациÑ
+tooltip.loading=Загрузка…
Uninstall=Удалить
+untitledScript=БезымÑнный Ñкрипт
Update=Обновить
+userscript=ПользовательÑкий Ñкрипт
userscripts=ПользовательÑкие Ñкрипты
-error.api.clipboard.type=Тип данных не подходит Ð´Ð»Ñ GM_setClipboard.
-error.api.noResourceWithName=Ðет реÑурÑа Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем
-error.api.noSecondArgValue=Второй аргумент - значение - не задан
-error.api.reqURL.scheme=Схема URL запрещена
-error.api.reqURL=Ðеверный URL
-error.api.unsafeAccess=Ðарушение доÑтупа Scriptish: из unsafeWindow не позволено работать Ñ
-error.charset=ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ°.
-error.dependency.loading=Ðе могу загрузить завиÑимоÑть
-error.dependency.local=ИÑключение безопаÑноÑти: запрещено вызывать локальные пути
-error.dependency.serverReturned=Ошибка! Сервер вернул
-error.hash.algorithm=Ðеверный аргоритм хешированиÑ.
-error.icon.dataURL=Ðеверный URL Ð´Ð»Ñ @icon
-error.icon.URL=Ðеверный URL Ð´Ð»Ñ @icon
-error.isInvalidValue= - неверное значение
-error.matchPattern.rules.file=File scheme @match pattern does not conform to pattern rules
-error.matchPattern.rules=@match pattern does not conform to pattern rules
-error.notSupported.Firefox=не поддерживаетÑÑ Ð² Ñтой верÑии Огненного ЛиÑа
-error.observerNotFound=Observer not found
-error.openingFile=Ðе могу открыть файл
-error.pattern.parsing=Шаблон не парÑитÑÑ
-error.pref.type=Ðеподдерживаемый тип наÑтройки. Правильные типы: Ñтрока, булев и 32хбитное целое.
-error.resource.dupName= - повторÑющееÑÑ Ð¸Ð¼Ñ Ð´Ð»Ñ Ñ€ÐµÑурÑа. Имена реÑурÑов должны быть уникальными.
-error.resource.syntax=Ðеправильный ÑинтакÑÐ¸Ñ Ð² обьÑвлении @resource
-error.retrieving=Failed to retrieve
-error.script.installing=Ошибка при уÑтановке юзерÑкрипта
-error.script.loading=Ошибка при загрузке юзерÑкрипта
+userscripts.get=Получить пользовательÑкие Ñкрипты
+userscripts.noneInstalled=ПользовательÑкие Ñкрипты не уÑтановлены
+warning.returnfrommain=«возврат» из оÑновной облаÑти обычно не поддерживаютÑÑ. Ð’Ñ‹ должны иÑправить Ñто в пользовательÑком Ñкрипте.
diff --git a/extension/locale/sv-SE/description.properties b/extension/locale/sv-SE/description.properties
new file mode 100644
index 00000000..0731e874
--- /dev/null
+++ b/extension/locale/sv-SE/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish är en fristående förgrening av Greasemonkey och erbjuder:
+amo.description.line2=Allting som Greasemonkey erbjuder (som är användbart, vill säga)
+amo.description.line3=Uppdatering: Användarskript kan uppdateras i Scriptish m.h.a. @updateURL
+amo.description.line4=Flera nya metadatanycklar (@keys)
+amo.description.line5=Flera nya tillägg till GM_-API:n
+amo.description.line6=Överlägsen prestanda: En snabbare, renare kodbas, som drar fördel av allt det som tidigare Firefox-versioner har haft att erbjuda
+amo.description.line7=Överlägsen säkerhet: Scriptish tillhandahåller säkerhetsfunktioner som du inte hittar någon annanstans
+amo.description.line8=Testning: Utvecklarna använder sig av ett flertal tester för att säkerställa att Scriptish fungerar korrekt och för att hitta eventuella fel så tidigt som möjligt
+amo.developerComments.line1=Scriptish har allt som Greasemonkey har, plus flera funktioner som du kan läsa mer om på: http://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=Var vänlig skicka in alla buggrapporter och förslag på nya funktioner via: https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=Om du har frågor, skicka dem till e-postlistan: http://groups.google.com/group/scriptish
+amo.summary=Den bästa användarskriptmotorn på Internet.
+extensions.scriptish@erikvold.com.description=En skripttilläggshanterare för Firefox
diff --git a/extension/locale/sv-SE/scriptish.properties b/extension/locale/sv-SE/scriptish.properties
new file mode 100644
index 00000000..9f60be2a
--- /dev/null
+++ b/extension/locale/sv-SE/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=Utvecklaren av detta användarskript ber om ditt stöd genom en liten donation för att kunna fortsätta utveckla det.
+copyDownloadURL=Kopiera hämtningsadress
+copyDownloadURL.ak=K
+edit=Redigera
+edit.ak=e
+editor.couldNotLaunch=Det gick inte att starta redigeringsprogrammet.
+editor.pleasePickExecutable=Var vänlig välj ett körbart program att använda för redigering av användarskript.
+editor.prompt=Var vänlig välj först det textredigeringsprogram du föredrar
+editor.useScratchpad=Vill du använda Scratchpad som din textredigerare?
+editor.useScratchpad.no=Nej, jag väljer min egen
+editor.useScratchpad.yes=Ja
+error.api.badArguments=
+error.api.noResourceWithName=Det finns ingen resurs med namnet
+error.api.noSecondArgValue=Det andra argumentet har inte angivits: Värde
+error.api.prefNotFound=
+error.api.reqURL=Ogiltig URL
+error.api.reqURL.scheme=Otillåtet schema i URL
+error.api.safeHTMLParser.url=Argumentet "URL" för GM_safeHTMLParser kunde inte parsas
+error.api.unsafeAccess=Ã…tkomstfel i Scriptish: unsafeWindow kan inte anropa
+error.charset=Ogiltig teckenuppsättning angavs.
+error.dependency.loading=Fel vid inläsning av beroende
+error.dependency.local=Säkerhetsundantag: Förfrågningar till lokala URL:er och chrome-URL:er är förbjudna
+error.dependency.serverReturned=Fel! Servern returnerade
+error.hash.algorithm=Ogiltig hashalgoritm angavs.
+error.icon.dataURL=Ogiltig data: URL för @icon
+error.icon.notImage=Fel! @icon har ingen MIME-bildtyp
+error.icon.URL=Ogiltig URL för @icon
+error.invalidCert=Ogiltigt SSL-certifikat.\nOm så önskas kan alla certifikat tillåtas genom att aktivera alternativet i Scriptish inställningar > Avancerat > Uppdateringssäkerhet.
+error.isInvalidValue=är ett ogiltigt värde
+error.matchPattern.rules=@match-mönster uppfyller inte mönsterreglerna
+error.matchPattern.rules.file=@match-mönstret för filschema uppfyller inte mönsterreglerna
+error.notSecure=Osäker URI.\nOm så önskas kan icke-HTTPS-uppdateringar tillåtas genom att aktivera alternativet i Scriptish inställningar > Avancerat > Uppdateringssäkerhet.
+error.notSupported.Firefox=stöds inte i denna version av Firefox
+error.openingFile=Det gick inte att öppna filen
+error.pattern.parsing=Det gick inte att parsa mönstret
+error.pref.type=Inställningstypen stöds inte. Giltiga typer är: sträng, boolesk och 32-bitars heltal.
+error.remoteVersionOlder=Fjärrskriptet har inget senare versionsnummer än ditt nuvarande skript.
+error.resource.dupName=är en resursnamnsdubblett. Varje @resource måste ha ett unikt namn.
+error.resource.syntax=Ogiltig syntax för @resource-deklaration
+error.retrieving=Det gick inte att hämta
+error.script.installing=Fel vid installation av användarskript
+error.script.loading=Fel vid inläsning av användarskript
+greeting.btn.ak=I
+greeting.msg=Detta är ett användarskript. Klicka på \"Installera\" för att börja använda det.
+install=Installera
+install.domains=domäner:
+install.excludes=kan inte köras på:
+install.includes=kan köras på:
+install.matches=matchningar:
+install.requires=kräver:
+install.resources=resurser:
+install.showScriptSource=Visa skriptets källa
+install.title=Installation av användarskript
+install.warning1=Skadliga skript kan kränka din integritet och agera för din räkning utan din vetskap.
+install.warning2=Du bör endast installera skript från källor som du litar på.
+installFromFile=Installera användarskript från fil…
+installFromFile.ak=f
+menu.commands=Användarskriptkommandon…
+menu.commands.ak=A
+menu.install=Installera användarskript…
+menu.install.ak=I
+menu.manage=Hantera användarskript…
+menu.manage.ak=H
+menu.new=Nytt användarskript…
+menu.new.ak=N
+menu.options.ak=ä
+menu.show=Visa användarskriptets källa
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=c
+menuitem.install=Installera detta användarskript…
+menuitem.manage=Hantera användarskript
+menuitem.new=Nytt användarskript
+moving.dependency=Flyttar beroendefil från
+moving.script=Flyttar skriptfil från
+newscript.author=Skapat av
+newscript.description=Beskrivning
+newscript.excludes=Undantagna (ett per rad)
+newscript.exists=Ett skript med samma namn är redan installerat.\nVill du ersätta det?
+newscript.id=ID
+newscript.includes=Inkluderade (ett per rad)
+newscript.missing.id=Var vänlig ange ett ID.
+newscript.missing.name=Var vänlig ange ett namn. Namnet kommer att visas i tilläggshanteraren.
+newscript.name=Namn
+newscript.namespace=Namnområde
+newscript.noID=Var vänlig ange ett giltigt ID för ditt skript.
+newscript.noName=Var vänlig ange ett namn på ditt skript.
+newscript.runat=Kör när
+newscript.runat.documentend=Dokumentet har lästs in
+newscript.runat.documentidle=Dokumentet är först i väntelistan
+newscript.runat.documentstart=Dokumentet börjar läsas in
+newscript.runat.windowload=Fönster inkl. alla medier har lästs in helt och hållet
+newscript.version=Version
+nothing.timedOut=ingenting (tidsgränsen uppnåddes)
+openFolder=Öppna mapp
+openFolder.ak=Ö
+openUserScriptsManager=Öppna användarskripthanteraren
+openUserScriptsManager.ak=Ö
+options=Inställningar
+options.addonsManager=Tilläggshanteraren
+options.alsoUninstallPrefs=Vid avinstallation av ett skript, ta även bort dess inställningar
+options.cache=Cache
+options.cache.enabled=Cachelagra användarskript
+options.changeEditor=Byt redigeringsprogram
+options.editor=Redigeringsprogram
+options.editor.notset=Inget redigeringsprogram har valts
+options.enableCopyDownloadURL=Aktivera kopiering av hämtningsadress
+options.enabledSchemes.about=about-protokoll (internt)
+options.enabledSchemes.advanced=Ytterligare protokoll
+options.enabledSchemes.chrome=chrome-protokoll (internt)
+options.enabledSchemes.data=dataprotokoll
+options.enabledSchemes.file=filprotokoll (lokala filer)
+options.enabledSchemes.ftp=FTP-protokoll
+options.enabledSchemes.http=HTTP/HTTPS-protokoll
+options.enabledSchemes.main=Protokoll
+options.enabledSchemes.securityRemark=Alla ytterligare protokoll kan ha full åtkomst till systemet.
+options.enabledSchemes.securityRemark2=Därmed utgör de en säkerhetsrisk tillsammans med ej betrodda skript!
+options.enabledSchemes.unmht=UNMHT-protokoll (format för Mozilla Archive)
+options.excludes.desc=Globala undantag kommer att åsidosätta alla skript och användardefinierade regler
+options.excludes.empty=Inga globala undantag har angivits
+options.excludes.remark=(Du kan ange ett undantag per rad)
+options.logChrome=Logga icke-kritiska tilläggsmeddelanden till Felkonsolen
+options.logging=Loggning
+options.logToErrorConsole=GM_log använder Felkonsolen
+options.notifications=Aviseringar
+options.notifications.popup.enable=Aktivera popup-aviseringar
+options.notifications.sliding.enable=Aktivera glidande aviseringar
+options.notifications.whenDisabled=Om inaktiverat, kommer alla aviseringar att visas i Felkonsolen.
+options.pane.advanced=Avancerat
+options.pane.excludes=Globala undantag
+options.pane.main=Allmänt
+options.pane.ui=Användargränssnitt
+options.requireBuiltInCerts=Säker uppdatering kräver användning av inbyggda certifikat
+options.requireSecured=Kräv att användarskript uppdateras via HTTPS
+options.sync.desc=Tjänsten Firefox Sync och dess alternativ "Inställningar" måste vara aktiverade!
+options.sync.ScriptishPrefs=Inställningar för Scriptish
+options.sync.ScriptishPrefs.common=Synkronisera allmänna inställningar för tillägget Scriptish
+options.sync.ScriptishPrefs.editor=Synkronisera redigeringsinställningarna
+options.title=Inställningar för Scriptish
+options.toolbarbutton=Verktygsfältsknapp
+options.toolbarbutton.showScripts=Visa antalet aktiva skript
+options.translation=Översättning
+options.translation.useEnglish=Använd alltid engelska
+options.update.security=Uppdateringssäkerhet
+options.useDownloadURL=Använd hämtningsadressen för uppdateringar om ingen uppdaterings-URL finns angiven
+reinstall=Ominstallera
+saving=Sparar
+scratchpad.saveAsUserScript=Spara som användarskript
+scratchpad.saveAsUserScript.ak=p
+scriptOptions.disableScriptIncludes=Inaktivera detta skripts inkluderade mönster och använd endast de användardefinierade mönstren.
+scriptOptions.excludes=Undantagna av användaren:
+scriptOptions.includes=Inkluderade av användaren:
+statusbar.enabled=Aktiverat
+statusbar.enabled.ak=A
+statusbar.installed=har installerats
+statusbar.modified=har ändrats
+statusbar.noScripts.excluded=Sidan är undantagen!
+statusbar.noScripts.notfound=Inga skript för sidan!
+statusbar.noScripts.scheme=Schemat är inaktiverat!
+statusbar.updated=har uppdaterats
+sync=Synka
+tooltip.loading=Läser in…
+Uninstall=Avinstallera
+untitledScript=(Namnlöst skript)
+Update=Uppdatera
+userscript=Användarskript
+userscripts=Användarskript
+userscripts.get=Hämta användarskript
+userscripts.noneInstalled=Du har inga användarskript installerade
+warning.returnfrommain=Att "gå tillbaka" från huvudområdet stöds som regel inte. Du bör åtgärda det i ditt användarskript.
diff --git a/extension/locale/zh-CN/description.properties b/extension/locale/zh-CN/description.properties
new file mode 100644
index 00000000..b32375aa
--- /dev/null
+++ b/extension/locale/zh-CN/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish 是 Greasemonkey 的分支,æä¾›:
+amo.description.line2=Greasemonkey æä¾›çš„æ‰€æœ‰åŠŸèƒ½ï¼ˆéƒ½æ˜¯éžå¸¸æœ‰ç”¨çš„)
+amo.description.line3=更新:用户脚本在 Scriptish 上å¯ä»¥ä½¿ç”¨ @updateURL æ›´æ–°
+amo.description.line4=更多的 @keys å…ƒæ•°æ®æ”¯æŒ
+amo.description.line5=更多的 用户脚本API支æŒ
+amo.description.line6=优越的性能:利用一切 Firefox 4所æä¾›çš„æ›´å¿«ã€æ›´æ¸…æ´çš„代ç 基础
+amo.description.line7=出众的安全:Scriptish æä¾›ç‹¬ä¸€æ— 二的安全功能
+amo.description.line8=æµ‹è¯•ï¼šç¼–å†™ä»£ç æµ‹è¯•Scriptish 的功能和特性
+amo.developerComments.line1=Scriptish 拥有 Greasemonkey çš„æ‰€æœ‰åŠŸèƒ½ï¼Œå¢žåŠ çš„å¾ˆå¤šå…¶ä»–ç‰¹æ€§æ‚¨å¯ä»¥åˆ°è¿™é‡ŒæŸ¥çœ‹ï¼šhttp://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=请到这里æäº¤ bug 或功能需求:https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=如果您还有其他问题,请å‘é€é‚®ä»¶åˆ°è¿™é‡Œï¼šhttp://groups.google.com/group/scriptish
+amo.summary=互è”网上最伟大的用户脚本引擎。
+extensions.scriptish@erikvold.com.description=é¢å‘ Firefox 的一个脚本扩展管ç†å™¨
diff --git a/extension/locale/zh-CN/scriptish.properties b/extension/locale/zh-CN/scriptish.properties
new file mode 100644
index 00000000..54c21f09
--- /dev/null
+++ b/extension/locale/zh-CN/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=这个用户脚本的开å‘者希望您能æåŠ©ï¼Œä»¥æ”¯æŒä»–ç»§ç»å¼€å‘这个脚本。
+copyDownloadURL=å¤åˆ¶ä¸‹è½½ URL
+copyDownloadURL.ak=C
+edit=编辑
+edit.ak=E
+editor.couldNotLaunch=æ— æ³•å¯åŠ¨ç¼–è¾‘å™¨ã€‚
+editor.pleasePickExecutable=è¯·é€‰æ‹©ä¸€ä¸ªå¯æ‰§è¡Œç¨‹åºç”¨æ¥ç¼–辑用户脚本。
+editor.prompt=请选择您喜欢的文本编辑器
+editor.useScratchpad=您想使用暂å˜å™¨ä½œä¸ºæ‚¨çš„编辑器å—?
+editor.useScratchpad.no=ä¸ï¼ˆæˆ‘è¦é€‰æ‹©æˆ‘自己喜欢的)
+editor.useScratchpad.yes=是
+error.api.badArguments=
+error.api.noResourceWithName=没有该å称的资æº
+error.api.noSecondArgValue=ç¬¬äºŒä¸ªå‚æ•°æœªæŒ‡å®šï¼šå€¼
+error.api.prefNotFound=
+error.api.reqURL=æ— æ•ˆçš„ URL
+error.api.reqURL.scheme=ä¸å…许在 URL ä¸å‡ºçް scheme
+error.api.safeHTMLParser.url=æ— æ³•è§£é‡Š GM_safeHTMLParser 函数的ä¸çš„ URL傿•°
+error.api.unsafeAccess=Scriptish 访问冲çªï¼šunsafeWindow æ— æ³•è°ƒç”¨
+error.charset=指定的å—ç¬¦é›†æ— æ•ˆã€‚
+error.dependency.loading=åŠ è½½ä¾èµ–文件时å‘生错误
+error.dependency.local=å®‰å…¨å¼‚å¸¸ï¼šç¦æ¢æœ¬åœ°åŠ chrome çš„ URL 请求
+error.dependency.serverReturned=é”™è¯¯ï¼æœåŠ¡å™¨å·²è¿”å›ž
+error.hash.algorithm=æŒ‡å®šçš„æ•£åˆ—ç®—æ³•æ— æ•ˆ
+error.icon.dataURL=æ— æ•ˆçš„æ•°æ®ï¼š@icon çš„ URL
+error.icon.notImage=错误ï¼@icon æ²¡æœ‰å›¾åƒ MIME 类型
+error.icon.URL=æ— æ•ˆçš„ @icon URL
+error.invalidCert=æ— æ•ˆçš„SSLè¯ä¹¦ã€‚\n如果确定需è¦å¼€å¯ï¼Œè¯·åœ¨Scriptish设置/高级/更新安全 勾选å…许所有è¯ä¹¦ã€‚
+error.isInvalidValue=æ˜¯ä¸ªæ— æ•ˆçš„å€¼
+error.matchPattern.rules=@match 模å¼ä¸ç¬¦åˆæ¨¡å¼è§„则
+error.matchPattern.rules.file=文件 scheme @match 模å¼ä¸ç¬¦åˆæ¨¡å¼è§„则
+error.notSecure=éžå®‰å…¨URI。\n如果确定需è¦å¼€å¯ï¼Œè¯·åœ¨Scriptish设置/高级/更新安全 下开å¯å…许éžHTTPS更新。
+error.notSupported.Firefox=䏿”¯æŒè¿™ä¸€ç‰ˆæœ¬çš„ Firefox
+error.openingFile=æ— æ³•æ‰“å¼€æ–‡ä»¶
+error.pattern.parsing=模å¼ä¸èƒ½è¢«è§£æž
+error.pref.type=䏿”¯æŒçš„首选项类型。有效的类型有:å—符串ã€å¸ƒå°”å’Œ32使•´æ•°ã€‚
+error.remoteVersionOlder=更新地å€ä¸‹çš„脚本版本数å—低于本地文件。
+error.resource.dupName=是一个é‡å¤çš„资æºå称。æ¯ä¸€ä¸ª @resource 都必须有唯一的å称。
+error.resource.syntax=æ— æ•ˆçš„ @resource å£°æ˜Žè¯æ³•
+error.retrieving=检索失败
+error.script.installing=安装用户脚本时å‘生错误
+error.script.loading=åŠ è½½ç”¨æˆ·è„šæœ¬æ—¶å‘生错误
+greeting.btn.ak=I
+greeting.msg=这是一个用户脚本。è¦ä½¿ç”¨è¯¥è„šæœ¬è¯·ç‚¹å‡»â€œå®‰è£…â€ã€‚
+install=安装
+install.domains=域å:
+install.excludes=ä¸åœ¨ä¸‹åˆ—网页è¿è¡Œï¼š
+install.includes=脚本è¿è¡ŒäºŽä¸‹åˆ—网页:
+install.matches=匹é…:
+install.requires=è¦æ±‚:
+install.resources=资æºï¼š
+install.showScriptSource=显示脚本æºä»£ç
+install.title=安装用户脚本
+install.warning1=æ¶æ„脚本å¯èƒ½ä¼šä¾µçŠ¯æ‚¨çš„éšç§å¹¶åœ¨æ‚¨ä¸çŸ¥æƒ…的情况下以您的身份è¿è¡Œã€‚
+install.warning2=您应该åªå®‰è£…从å¯ä¿¡ä»»æ¥æºèŽ·å¾—çš„è„šæœ¬ã€‚
+installFromFile=从文件安装用户脚本...
+installFromFile.ak=U
+menu.commands=用户脚本命令...
+menu.commands.ak=C
+menu.install=安装用户脚本...
+menu.install.ak=I
+menu.manage=管ç†ç”¨æˆ·è„šæœ¬...
+menu.manage.ak=M
+menu.new=新建用户脚本...
+menu.new.ak=N
+menu.options.ak=O
+menu.show=查看用户脚本æºä»£ç
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=安装这个用户脚本...
+menuitem.manage=管ç†ç”¨æˆ·è„šæœ¬
+menuitem.new=新建用户脚本
+moving.dependency=从这里移除ä¾èµ–文件
+moving.script=从这里移除脚本文件
+newscript.author=作者
+newscript.description=æè¿°
+newscript.excludes=排除网å€ï¼ˆæ¯è¡Œä¸€æ¡ï¼‰
+newscript.exists=已安装了åŒå的脚本。\n覆盖åŒå脚本å—?
+newscript.id=ID
+newscript.includes=包括网å€ï¼ˆæ¯è¡Œä¸€æ¡ï¼‰
+newscript.missing.id=请æä¾› ID。
+newscript.missing.name=请æä¾›ä¸€ä¸ªå称。这个åç§°å°†æ˜¾ç¤ºåœ¨é™„åŠ ç»„ä»¶ç®¡ç†å™¨ä¸ã€‚
+newscript.name=åç§°
+newscript.namespace=命å空间
+newscript.noID=请为您的脚本æä¾›ä¸€ä¸ªæœ‰æ•ˆçš„ ID。
+newscript.noName=请为您的脚本命å。
+newscript.runat=è¿è¡Œæ—¶æœºï¼ˆ@run-at)
+newscript.runat.documentend=æ–‡æ¡£åŠ è½½å®Œæˆæ—¶
+newscript.runat.documentidle=文档首次闲置时
+newscript.runat.documentstart=æ–‡æ¡£åŠ è½½å¼€å§‹æ—¶
+newscript.runat.windowload=窗å£åŒ…å«çš„æ‰€æœ‰åª’ä½“éƒ½åŠ è½½å®Œæˆæ—¶
+newscript.version=版本
+nothing.timedOut=æ— ï¼ˆè¶…æ—¶ï¼‰
+openFolder=打开文件夹
+openFolder.ak=O
+openUserScriptsManager=打开用户脚本管ç†å™¨
+openUserScriptsManager.ak=O
+options=选项
+options.addonsManager=é™„åŠ ç»„ä»¶ç®¡ç†å™¨
+options.alsoUninstallPrefs=å¸è½½è„šæœ¬çš„åŒæ—¶ï¼Œæ¸…除有关它的首选项。
+options.cache=缓å˜
+options.cache.enabled=缓å˜ç”¨æˆ·è„šæœ¬
+options.changeEditor=æ›´æ¢ç¼–辑器
+options.editor=编辑器
+options.editor.notset=尚未选择编辑器
+options.enableCopyDownloadURL=å¯ç”¨å¤åˆ¶ä¸‹è½½ URL
+options.enabledSchemes.about=about å议(内部)
+options.enabledSchemes.advanced=é¢å¤–çš„åè®®
+options.enabledSchemes.chrome=chrome å议(内部)
+options.enabledSchemes.data=data åè®®
+options.enabledSchemes.file=file å议(本地文件)
+options.enabledSchemes.ftp=ftp åè®®
+options.enabledSchemes.http=http/https åè®®
+options.enabledSchemes.main=åè®®
+options.enabledSchemes.securityRemark=é¢å¤–çš„åè®®å¯èƒ½ä¼šæ‹¥æœ‰å®Œå…¨çš„系统访问æƒé™ã€‚
+options.enabledSchemes.securityRemark2=å› æ¤è¿™äº›ä¸å—信任的脚本å¯èƒ½é€ æˆå·¨å¤§çš„安全风险ï¼
+options.enabledSchemes.unmht=unmht å议(Mozilla å˜æ¡£æ ¼å¼ï¼‰
+options.excludes.desc=全局排除将覆盖任何脚本和用户指定的规则
+options.excludes.empty=当剿²¡æœ‰æŒ‡å®šå…¨å±€æŽ’除
+options.excludes.remark=(您å¯ä»¥æ¯è¡ŒæŒ‡å®šä¸€æ¡æŽ’除网å€ï¼‰
+options.logChrome=记录éžå…³é”®çš„æ‰©å±•ä¿¡æ¯åˆ°é”™è¯¯æŽ§åˆ¶å°
+options.logging=日志
+options.logToErrorConsole=GM_log 使用错误控制å°
+options.notifications=通知
+options.notifications.popup.enable=弹窗显示通知
+options.notifications.sliding.enable=滑动显示通知
+options.notifications.whenDisabled=当ç¦ç”¨æ—¶ï¼Œä»»ä½•通知都将显示在错误控制å°ä¸ã€‚
+options.pane.advanced=高级
+options.pane.excludes=全局排除
+options.pane.main=常规
+options.pane.ui=用户界é¢
+options.requireBuiltInCerts=安全更新需è¦ä½¿ç”¨å†…置的è¯ä¹¦
+options.requireSecured=è¦æ±‚用户脚本使用 HTTPS æ›´æ–°
+options.sync.desc=å¿…é¡»å¯ç”¨Firefox çš„SyncåŒæ¥æœåŠ¡ï¼Œå¹¶ä¸”åœ¨å…¶è®¾ç½®ä¸å¼€å¯ç›¸å…³é€‰é¡¹
+options.sync.ScriptishPrefs=Scriptish 设置
+options.sync.ScriptishPrefs.common=åŒæ¥ Scriptish 扩展的普通设置
+options.sync.ScriptishPrefs.editor=åŒæ¥ä»£ç 编辑器设置
+options.title=Scriptish 选项
+options.toolbarbutton=å·¥å…·æ æŒ‰é’®
+options.toolbarbutton.showScripts=显示活动的脚本数
+options.translation=翻译
+options.translation.useEnglish=始终使用英è¯ç•Œé¢
+options.update.security=更新安全
+options.useDownloadURL=如果没有更新 URL 则使用下载 URL 进行更新
+reinstall=é‡è£…
+saving=æ£åœ¨ä¿å˜
+scratchpad.saveAsUserScript=ä¿å˜ä¸ºç”¨æˆ·è„šæœ¬
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=ç¦ç”¨è¯¥è„šæœ¬çš„åŒ…æ‹¬æ¨¡å¼ï¼Œä»…使用用户定义的。
+scriptOptions.excludes=用户指定的排除网页:
+scriptOptions.includes=用户指定的包括网页:
+statusbar.enabled=å¯ç”¨
+statusbar.enabled.ak=E
+statusbar.installed=安装æˆåŠŸ
+statusbar.modified=已修改
+statusbar.noScripts.excluded=页é¢å·²æŽ’除ï¼
+statusbar.noScripts.notfound=页é¢ä¸Šæ²¡æœ‰è„šæœ¬åœ¨è¿è¡Œï¼
+statusbar.noScripts.scheme=Scheme å·²ç¦ç”¨ï¼
+statusbar.updated=æ›´æ–°æˆåŠŸ
+sync=åŒæ¥
+tooltip.loading=æ£åœ¨åŠ è½½...
+Uninstall=å¸è½½
+untitledScript=æ— æ ‡é¢˜çš„è„šæœ¬
+Update=æ›´æ–°
+userscript=用户脚本
+userscripts=用户脚本
+userscripts.get=获å–用户脚本
+userscripts.noneInstalled=您尚未安装任何用户脚本
+warning.returnfrommain=ä¸€èˆ¬ä¸æ”¯æŒåœ¨ä¸»æ‰§è¡ŒåŸŸé‡Œçš„“returnâ€ã€‚您需è¦åœ¨æ‚¨çš„用户脚本ä¸ä¿®å¤è¿™ä¸ªé—®é¢˜ã€‚
diff --git a/extension/locale/zh-TW/description.properties b/extension/locale/zh-TW/description.properties
new file mode 100644
index 00000000..7ebb124a
--- /dev/null
+++ b/extension/locale/zh-TW/description.properties
@@ -0,0 +1,13 @@
+amo.description.line1=Scriptish 是 Greasemonkey 的分支,æä¾›ä»¥ä¸‹åŠŸèƒ½:
+amo.description.line2=Greasemonkey 的所有功能(都是éžå¸¸æœ‰ç”¨çš„)
+amo.description.line3=更新:使用者腳本在 Scriptish 裡å¯ä»¥ç”¨ @updateURL æ›´æ–°
+amo.description.line4=新增更多的 後è¨è³‡æ–™ @keys
+amo.description.line5=新增更多的 GM_ API
+amo.description.line6=優秀的性能:憑藉新版 Firefox çš„å„ªå‹¢ï¼Œä»¥æ›´å¿«ã€æ›´ä¹¾æ·¨çš„程å¼ç¢¼æä¾›æ›´å„ªç§€çš„æ€§èƒ½
+amo.description.line7=出色的安全:Scriptish æä¾›ç¨ä¸€ç„¡äºŒçš„安全特性
+amo.description.line8=測試:開發者利用大é‡çš„æ¸¬è©¦ä¿è‰ Scriptish æ£ç¢ºçš„執行,並更快找出程å¼ç¢¼ä¸çš„錯誤
+amo.developerComments.line1=Scriptish æ“æœ‰ Greasemonkey 的所有功能,其他é¡å¤–å¢žåŠ çš„ç‰¹æ€§æ‚¨å¯ä»¥åˆ°é€™è£¡ç€è¦½ï¼šhttp://github.com/scriptish/scriptish/wiki
+amo.developerComments.line2=請到這裡æäº¤ bug 或新功能請求:https://github.com/scriptish/scriptish/issues
+amo.developerComments.line3=如果您還有其他å•題,請寄發電å郵件到這裡:http://groups.google.com/group/scriptish
+amo.summary=網際網路上最傑出的使用者腳本引擎。
+extensions.scriptish@erikvold.com.description=一個 Firefox 的腳本管ç†å™¨
diff --git a/extension/locale/zh-TW/scriptish.properties b/extension/locale/zh-TW/scriptish.properties
new file mode 100644
index 00000000..def9bec6
--- /dev/null
+++ b/extension/locale/zh-TW/scriptish.properties
@@ -0,0 +1,170 @@
+contributions.description=æ¤è…³æœ¬ä½œè€…請求您贊助,以支æŒè…³æœ¬æŒçºŒåœ°é–‹ç™¼ã€‚
+copyDownloadURL=複製下載網å€
+copyDownloadURL.ak=C
+edit=編輯
+edit.ak=E
+editor.couldNotLaunch=無法開啟編輯器。
+editor.pleasePickExecutable=è«‹é¸æ“‡ä¸€å€‹å¯åŸ·è¡Œç¨‹å¼ä¾†ç·¨è¼¯ä½¿ç”¨è€…腳本。
+editor.prompt=è«‹å…ˆé¸æ“‡æ‚¨å¸¸ç”¨çš„æ–‡å—編輯器
+editor.useScratchpad=您想使用程å¼ç¢¼ç‰‡æ®µé€Ÿè¨˜æœ¬ä½œç‚ºæ‚¨çš„編輯器嗎?
+editor.useScratchpad.no=ä¸ï¼ˆæˆ‘è¦è‡ªå·±é¸æ“‡ï¼‰
+editor.useScratchpad.yes=是
+error.api.badArguments=
+error.api.noResourceWithName=沒有該å稱的資æº
+error.api.noSecondArgValue=ç¬¬äºŒå€‹åƒæ•¸æœªæŒ‡å®šï¼šå€¼
+error.api.prefNotFound=
+error.api.reqURL=無效的網å€
+error.api.reqURL.scheme=ç¶²å€ä¸æœ‰ä¸æ”¯æ´çš„å”定
+error.api.safeHTMLParser.url=ç„¡æ³•å‰–æž GM_safeHTMLParser çš„ URL 引數
+error.api.unsafeAccess=Scriptish å˜å–失敗: unsafeWindow 無法呼å«
+error.charset=指定的å—元編碼無效。
+error.dependency.loading=載入附屬檔案時發生錯誤
+error.dependency.local=å®‰å…¨ç•°å¸¸ï¼šæœ¬åœ°åŠ chrome çš„é€£ç·šè¦æ±‚è¢«ç¦æ¢
+error.dependency.serverReturned=錯誤ï¼ä¼ºæœå™¨è¿”回
+error.hash.algorithm=指定的雜湊演算法無效。
+error.icon.dataURL=無效的資料: @icon ç¶²å€
+error.icon.notImage=錯誤ï¼@icon æ²’æœ‰åœ–åƒ MIME 類型
+error.icon.URL=無效的 @icon ç¶²å€
+error.invalidCert=無效的 SSL 憑è‰ã€‚\n如果需è¦ï¼Œå¯ç”± Scriptish é¸é … / 進階 / 安全性更新 å…許所有憑è‰ã€‚
+error.isInvalidValue=是無效的值
+error.matchPattern.rules=@match 樣å¼ä¸ç¬¦åˆæ¨£å¼è¦å‰‡
+error.matchPattern.rules.file=檔案å”定 @match 樣å¼ä¸ç¬¦åˆæ¨£å¼è¦å‰‡
+error.notSecure=ä¸å®‰å…¨çš„ URI。\n如果需è¦ï¼Œå¯ç”± Scriptish é¸é … / 進階 / 安全性更新 å…許éžHTTPS更新。
+error.notSupported.Firefox=䏿”¯æ´æ¤ç‰ˆæœ¬çš„ Firefox
+error.openingFile=無法開啟檔案
+error.pattern.parsing=樣å¼ç„¡æ³•è§£æž
+error.pref.type=䏿”¯æ´çš„é¸é …類型。有效的類型有:å—串ã€çœŸå‡å€¼å’Œ32ä½å…ƒæ•´æ•¸ã€‚
+error.remoteVersionOlder=é 端腳本沒有比本機腳本更新的版本。
+error.resource.dupName=是一個é‡è¤‡çš„資æºå稱。æ¯ä¸€å€‹ @resource åç¨±å¿…é ˆå”¯ä¸€ã€‚
+error.resource.syntax=無效的 @resource 宣告語法
+error.retrieving=檢索失敗
+error.script.installing=安è£ä½¿ç”¨è€…腳本時發生錯誤
+error.script.loading=載入使用者腳本時發生錯誤
+greeting.btn.ak=I
+greeting.msg=這是一個使用者腳本。按一下「安è£ã€é–‹å§‹ä½¿ç”¨å®ƒã€‚
+install=安è£
+install.domains=域å:
+install.excludes=ä¸åŸ·è¡Œæ–¼:
+install.includes=執行於:
+install.matches=匹é…:
+install.requires=需求:
+install.resources=資æºï¼š
+install.showScriptSource=顯示腳本程å¼ç¢¼
+install.title=使用者腳本安è£
+install.warning1=惡æ„腳本å¯èƒ½å±å®³æ‚¨çš„éš±ç§ï¼Œä¸¦åœ¨æ‚¨ä¸çŸ¥æƒ…時以您的å義執行。
+install.warning2=您應該åªå®‰è£å¯ä¿¡ä»»çš„腳本。
+installFromFile=從檔案安è£ä½¿ç”¨è€…腳本…
+installFromFile.ak=U
+menu.commands=使用者腳本指令…
+menu.commands.ak=C
+menu.install=安è£ä½¿ç”¨è€…腳本…
+menu.install.ak=I
+menu.manage=管ç†ä½¿ç”¨è€…腳本…
+menu.manage.ak=M
+menu.new=新增使用者腳本…
+menu.new.ak=N
+menu.options.ak=O
+menu.show=檢視使用者腳本程å¼ç¢¼
+menu.show.ak=V
+menu.title=Scriptish
+menu.title.ak=S
+menuitem.install=å®‰è£æ¤ä½¿ç”¨è€…腳本…
+menuitem.manage=管ç†ä½¿ç”¨è€…腳本
+menuitem.new=新增使用者腳本
+moving.dependency=移動附屬檔案,從
+moving.script=移動腳本檔案,從
+newscript.author=作者
+newscript.description=說明
+newscript.excludes=排除網å€(æ¯è¡Œä¸€æ¢)
+newscript.exists=已安è£äº†ä¸€å€‹åŒå的腳本。\n確定è¦è¦†è“‹å—Žï¼Ÿ
+newscript.id=ID
+newscript.includes=包å«ç¶²å€(æ¯è¡Œä¸€æ¢)
+newscript.missing.id=è«‹æä¾›ä¸€å€‹ ID
+newscript.missing.name=è«‹æä¾›ä¸€å€‹å稱。該åç¨±å°‡é¡¯ç¤ºåœ¨é™„åŠ å…ƒä»¶ç®¡ç†å“¡è£¡ã€‚
+newscript.name=å稱
+newscript.namespace=命å空間
+newscript.noID=è«‹ç‚ºä½ çš„è…³æœ¬æä¾›ä¸€å€‹æœ‰æ•ˆ ID
+newscript.noName=è«‹ç‚ºä½ çš„è…³æœ¬å‘½å。
+newscript.runat=執行時機
+newscript.runat.documentend=ç¶²é æ–‡ä»¶å®Œæˆè¼‰å…¥æ™‚
+newscript.runat.documentidle=ç¶²é æ–‡ä»¶é¦–次閒置時
+newscript.runat.documentstart=ç¶²é æ–‡ä»¶é–‹å§‹è¼‰å…¥æ™‚
+newscript.runat.windowload=ç¶²é æ–‡ä»¶å…§åµŒåª’é«”å‡å®Œæˆè¼‰å…¥æ™‚
+newscript.version=版本
+nothing.timedOut=無 (逾時)
+openFolder=開啟資料夾
+openFolder.ak=O
+openUserScriptsManager=開啟使用者腳本管ç†å™¨
+openUserScriptsManager.ak=O
+options=é¸é …
+options.addonsManager=é™„åŠ å…ƒä»¶ç®¡ç†å“¡
+options.alsoUninstallPrefs=當移除腳本時,一併移除它的è¨å®šå€¼
+options.cache=å¿«å–
+options.cache.enabled=å¿«å–使用者腳本
+options.changeEditor=è¨å®šç·¨è¼¯å™¨
+options.editor=編輯器
+options.editor.notset=å°šæœªé¸æ“‡ç·¨è¼¯å™¨
+options.enableCopyDownloadURL=å…許複製下載網å€
+options.enabledSchemes.about=about å”定 (內部)
+options.enabledSchemes.advanced=å…¶ä»–å”定
+options.enabledSchemes.chrome=chrome å”定 (內部)
+options.enabledSchemes.data=data å”定
+options.enabledSchemes.file=file å”定 (本機檔案)
+options.enabledSchemes.ftp=ftp å”定
+options.enabledSchemes.http=http/https å”定
+options.enabledSchemes.main=å”定
+options.enabledSchemes.securityRemark=å…¶ä»–å”定å¯èƒ½æœ‰å®Œå…¨çš„系統å˜å–權é™ã€‚
+options.enabledSchemes.securityRemark2=å› æ¤ä¸å—信任的腳本å¯èƒ½é€ æˆå·¨å¤§çš„安全風險ï¼
+options.enabledSchemes.unmht=unmht å”定 (Mozilla Archive format)
+options.excludes.desc=全域排除è¦å‰‡å°‡è¦†è“‹ä»»ä½•腳本和使用者定義的è¦å‰‡
+options.excludes.empty=沒有è¨å®šå…¨åŸŸæŽ’除è¦å‰‡
+options.excludes.remark=(您å¯ä»¥æ¯è¡ŒæŒ‡å®šä¸€æ¢æŽ’除網å€ï¼‰
+options.logChrome=在錯誤主控å°è¨˜éŒ„éžé—œéµçš„æ“´å……套件訊æ¯
+options.logging=記錄
+options.logToErrorConsole=GM_log 使用錯誤主控å°
+options.notifications=通知
+options.notifications.popup.enable=啟用彈出å¼è¨Šæ¯é€šçŸ¥
+options.notifications.sliding.enable=啟用滑動å¼è¨Šæ¯é€šçŸ¥
+options.notifications.whenDisabled=åœç”¨æ™‚,所有的訊æ¯é€šçŸ¥å°‡é¡¯ç¤ºæ–¼éŒ¯èª¤ä¸»æŽ§å°ã€‚
+options.pane.advanced=進階
+options.pane.excludes=全域排除
+options.pane.main=主è¦
+options.pane.ui=介é¢
+options.requireBuiltInCerts=å®‰å…¨æ€§æ›´æ–°å¿…é ˆä½¿ç”¨å…§å»ºçš„æ†‘è‰
+options.requireSecured=è…³æœ¬å¿…é ˆä½¿ç”¨ HTTPS æ›´æ–°
+options.sync.desc=é ˆå•Ÿç”¨ Firefox Sync æœå‹™åŠå…¶ç›¸é—œé¸é …ï¼
+options.sync.ScriptishPrefs=Scriptish åŒæ¥è¨å®š
+options.sync.ScriptishPrefs.common=åŒæ¥ Scriptish 套件的一般è¨å®š
+options.sync.ScriptishPrefs.editor=åŒæ¥ç·¨è¼¯å™¨è¨å®š
+options.title=Scriptish é¸é …
+options.toolbarbutton=工具列按鈕
+options.toolbarbutton.showScripts=顯示作用ä¸çš„腳本數
+options.translation=語言
+options.translation.useEnglish=總是使用英文
+options.update.security=安全性更新
+options.useDownloadURL=如果沒有更新網å€ï¼Œå‰‡ä½¿ç”¨ä¸‹è¼‰ç¶²å€æ›´æ–°
+reinstall=釿–°å®‰è£
+saving=儲å˜
+scratchpad.saveAsUserScript=儲å˜ç‚ºä½¿ç”¨è€…腳本
+scratchpad.saveAsUserScript.ak=u
+scriptOptions.disableScriptIncludes=åœç”¨æ¤è…³æœ¬çš„åŒ…å«æ¨£å¼ï¼Œåƒ…套用使用者定義的樣å¼ã€‚
+scriptOptions.excludes=使用者定義的排除網å€ï¼š
+scriptOptions.includes=使用者定義的包å«ç¶²å€ï¼š
+statusbar.enabled=啟用
+statusbar.enabled.ak=E
+statusbar.installed=å®‰è£æˆåŠŸ
+statusbar.modified=已修改
+statusbar.noScripts.excluded=é é¢å·²æŽ’除ï¼
+statusbar.noScripts.notfound=沒有在æ¤é é¢åŸ·è¡Œçš„腳本ï¼
+statusbar.noScripts.scheme=å”定已åœç”¨ï¼
+statusbar.updated=æ›´æ–°æˆåŠŸ
+sync=åŒæ¥
+tooltip.loading=載入ä¸â€¦
+Uninstall=移除
+untitledScript=無標題的腳本
+Update=æ›´æ–°
+userscript=使用者腳本
+userscripts=使用者腳本
+userscripts.get=å–得腳本
+userscripts.noneInstalled=您尚未安è£ä»»ä½•使用者腳本
+warning.returnfrommain=ä¸€èˆ¬ä¸æ”¯æ´å¾žä¸»ç¯„åœ"return"。您應該在您的腳本ä¸ä¿®å¾©é€™å€‹å•題。
diff --git a/extension/modules/addonprovider.js b/extension/modules/addonprovider.js
index e9d120ff..804bf384 100644
--- a/extension/modules/addonprovider.js
+++ b/extension/modules/addonprovider.js
@@ -1,13 +1,158 @@
var EXPORTED_SYMBOLS = [];
-Components.utils.import("resource://scriptish/scriptish.js");
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/config.js", ["Scriptish_config"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://gre/modules/AddonManager.jsm", ["AddonManager", "AddonManagerPrivate"]);
+
+lazyUtil(this, "notification");
+lazyUtil(this, "openManager");
+lazyUtil(this, "popupNotification");
+lazyUtil(this, "stringBundle");
+
+const Scriptish_ScriptProvider = {
+ observe: function(aSubject, aTopic, aData) {
+ aData = JSON.parse(aData);
+ let script = Scriptish_config.getScriptById(aData.id);
+
+ switch(aTopic){
+ case "scriptish-script-installed":
+ AddonManagerPrivate.callInstallListeners(
+ "onExternalInstall", null, script, null, false);
+
+ // notification that install is complete
+ var msg = "'" + script.name;
+ if (script.version) msg += " " + script.version;
+ msg += "' " + Scriptish_stringBundle("statusbar.installed");
+
+ var showedMsg = Scriptish_popupNotification({
+ id: "scriptish-install-popup-notification",
+ message: msg,
+ mainAction: {
+ label: Scriptish_stringBundle("openUserScriptsManager"),
+ accessKey: Scriptish_stringBundle("openUserScriptsManager.ak"),
+ callback: Scriptish_openManager.bind(null, null, script.id)
+ },
+ secondaryActions: [{
+ label: Scriptish_stringBundle("Uninstall"),
+ accessKey: Scriptish_stringBundle("Uninstall.ak"),
+ callback: function() {
+ script.uninstall();
+ }
+ }],
+ options: {
+ removeOnDismissal: true,
+ persistWhileVisible: true
+ }
+ });
+
+ if (!showedMsg) {
+ Scriptish_notification(msg, null, null, callback);
+ }
+
+ break;
+ case "scriptish-script-edit-enabling":
+ AddonManagerPrivate.callAddonListeners(
+ aData.enabling ? "onEnabling" : "onDisabling", script, false);
+ break;
+ case "scriptish-script-edit-enabled":
+ AddonManagerPrivate.callAddonListeners(
+ aData.enabling ? "onEnabled" : "onDisabled", script);
+ break;
+ case "scriptish-script-modified":
+ case "scriptish-script-updated":
+ if (aData.reloadUI) {
+ AddonManagerPrivate.callAddonListeners("onUninstalled", this);
+ AddonManagerPrivate.callInstallListeners(
+ "onExternalInstall", null, this, null, false);
+ }
+
+ // notification
+ var msg = "'" + script.name;
+ if (script.version) msg += " " + script.version;
+ msg += "' " + (("scriptish-script-updated" == aTopic)
+ ? Scriptish_stringBundle("statusbar.updated")
+ : Scriptish_stringBundle("statusbar.modified"));
+
+ Scriptish_popupNotification({
+ id: "scriptish-install-popup-notification",
+ message: msg,
+ mainAction: {
+ label: Scriptish_stringBundle("openUserScriptsManager"),
+ accessKey: Scriptish_stringBundle("openUserScriptsManager.ak"),
+ callback: Scriptish_openManager.bind(null, null, script.id)
+ },
+ secondaryActions: [{
+ label: Scriptish_stringBundle("Uninstall"),
+ accessKey: Scriptish_stringBundle("Uninstall.ak"),
+ callback: function() {
+ script.uninstall();
+ }
+ }],
+ options: {
+ removeOnDismissal: true,
+ persistWhileVisible: true
+ }
+ });
+
+ break;
+ case "scriptish-script-uninstalling":
+ AddonManagerPrivate.callAddonListeners("onUninstalling", script, false);
+ break;
+ case "scriptish-script-uninstalled":
+ AddonManagerPrivate.callAddonListeners("onUninstalled", script);
+ break;
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver])
+};
+
+var types = null;
+if (AddonManagerPrivate.AddonType) {
+ types = [new AddonManagerPrivate.AddonType(
+ "userscript",
+ "chrome://scriptish/locale/scriptish.properties",
+ "userscripts",
+ AddonManager.VIEW_TYPE_LIST,
+ 7000)];
+}
AddonManagerPrivate.registerProvider({
getAddonByID: function(aId, aCallback) {
- aCallback(Scriptish.config.getScriptById(aId));
+ aCallback(Scriptish_config.getScriptById(aId));
},
+
getAddonsByTypes: function(aTypes, aCallback) {
- if (aTypes && aTypes.indexOf("userscript") < 0) aCallback([]);
- else aCallback(Scriptish.config.scripts);
+ if (aTypes && aTypes.indexOf("userscript") < 0) {
+ aCallback([]);
+ } else {
+ aCallback(Scriptish_config.scripts);
+ }
+ },
+
+ getInstallsByTypes: function(aTypes, aCallback) {
+ if (aTypes && aTypes.indexOf("userscript") < 0) {
+ aCallback([]);
+ } else {
+ let updates = [];
+ Scriptish_config.scripts.forEach(function(script) {
+ if (script.updateAvailable
+ && script.updateAvailable.state == AddonManager.STATE_AVAILABLE)
+ updates.push(script.updateAvailable);
+ });
+ aCallback(updates);
+ }
}
-});
+}, types);
+
+[
+ "scriptish-script-installed",
+ "scriptish-script-edit-enabling",
+ "scriptish-script-edit-enabled",
+ "scriptish-script-modified",
+ "scriptish-script-updated",
+ "scriptish-script-uninstalling",
+ "scriptish-script-uninstalled"
+].forEach(function(i)(
+ Services.obs.addObserver(Scriptish_ScriptProvider, i, false)));
diff --git a/extension/modules/api.js b/extension/modules/api.js
index 86d074ba..ce286aba 100644
--- a/extension/modules/api.js
+++ b/extension/modules/api.js
@@ -1,12 +1,29 @@
-var EXPORTED_SYMBOLS = ["GM_API", "GM_apiSafeCallback"];
+var EXPORTED_SYMBOLS = ["GM_API", "GM_apiSafeCallback", "GM_waiveXrays", "GM_checkedPrincipal"];
const Cu = Components.utils;
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/logging.js");
-Cu.import("resource://scriptish/utils/Scriptish_notification.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
-const moduleFilename = Components.stack.filename;
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_logError", "Scriptish_logScriptError", "Scriptish_log"]);
+
+const { Style } = jetpack("sdk/stylesheet/style");
+const { attach, detach } = jetpack("sdk/content/mod");
+
+lazyUtil(this, "cryptoHash");
+lazyUtil(this, "getScriptHeader");
+lazyUtil(this, "notification");
+lazyUtil(this, "openInTab");
+lazyUtil(this, "stringBundle");
+
+lazyImport(this, "resource://scriptish/api/GM_ScriptStorage.js", ["GM_ScriptStorage"]);
+lazyImport(this, "resource://scriptish/api/GM_xmlhttpRequester.js", ["GM_xmlhttpRequester"]);
+lazyImport(this, "resource://scriptish/api/GM_Resources.js", ["GM_Resources"]);
+lazyImport(this, "resource://scriptish/api/GM_setClipboard.js", ["GM_setClipboard"]);
+
+const { add, addPrefix, check } = jetpack('scriptish/security/api-check-filenames');
+add(Components.stack.filename);
+addPrefix("resource://gre/modules/");
+addPrefix("resource://gre/components/");
+
const NS_XHTML = "http://www.w3.org/1999/xhtml";
const DOLITTLE = function(){};
@@ -15,212 +32,235 @@ function GM_apiLeakCheck(apiName) {
let stack = Components.stack;
do {
+ // TODO: do better protocol check below
// Valid stack frames for GM api calls are: native and js when coming from
// chrome:// URLs and any file name listed in _apiAcceptedFiles.
- if (2 == stack.language &&
- stack.filename != moduleFilename &&
- stack.filename != Services.scriptish.filename &&
+ if (2 == stack.language && stack.filename &&
+ !check(stack.filename) &&
stack.filename.substr(0, 6) != "chrome") {
Scriptish_logError(new Error(
- Scriptish_stringBundle("error.api.unsafeAccess") + ": " + apiName));
+ Scriptish_stringBundle("error.api.unsafeAccess") + " - " + apiName + ' by ' + stack.filename));
return false;
}
+ if (stack.filename && stack.filename === 'resource://scriptish/utils/Scriptish_injectScripts.js') {
+ // No point in checking beyond this point.
+ // Also fixed at least https://github.com/scriptish/scriptish/issues/231
+ break;
+ }
} while (stack = stack.caller);
return true;
}
-function GM_apiSafeCallback(aWin, aThis, aCb, aArgs) {
+function GM_apiSafeCallback(aWindow, aScript, aThis, aCb, aArgs) {
// Pop back onto browser scope and call event handler.
// Have to use nested function here otherwise aCallback.apply can point to
// window.setTimeout, which can be abused to get increased privileges.
- new XPCNativeWrapper(aWin, "setTimeout()")
- .setTimeout(function() aCb.apply(aThis, aArgs), 0);
+ new XPCNativeWrapper(aWindow, "setTimeout()").setTimeout(function() {
+ try {
+ aCb.apply(aThis, aArgs);
+ }
+ catch (e) {
+ Scriptish_logScriptError(e, aWindow, aScript.fileURL, aScript.id);
+ }
+ }, 0);
}
-function GM_API(aScript, aURL, aWinID, aSafeWin, aUnsafeContentWin, aChromeWin) {
+// Waive Xrays on an object
+const GM_waiveXrays = (function() {
+ if (Components.utils.waiveXrays) {
+ return function GM_waiveXrays(obj) {
+ return Components.utils.waiveXrays(obj);
+ };
+ }
+ return function GM_waiveXrays(obj) {
+ return obj;
+ };
+})();
+
+// Check that o2 has the same principal as o1 and return o2.
+// Otherwise return null.
+const GM_checkedPrincipal = (function() {
+ if (Components.utils.getObjectPrincipal) {
+ return function GM_checkedPrincipal(o1, o2) {
+ try {
+ if (!o2) {
+ return null;
+ }
+ var op1 = Components.utils.getObjectPrincipal(o1);
+ var op2 = Components.utils.getObjectPrincipal(o2);
+ if (!op1.equals(op2)) {
+ Scriptish_logError(new Error("Principal mismatch"));
+ return null;
+ }
+ return o2;
+ }
+ catch (ex) {
+ Scriptish_logError(new Error("Principal mismatch"));
+ return null;
+ }
+ };
+ }
+
+ return function GM_checkedPrincipal(o1, o2) {
+ return o2;
+ }
+})();
+
+
+// note: must not depend on aChromeWin below, it should always be optional!
+function GM_API(options) {
+ var {
+ script: aScript,
+ url: aURL,
+ winID: aWinID,
+ safeWin: aSafeWin,
+ unsafeWin: aUnsafeContentWin,
+ chromeWin: aChromeWin,
+ sandbox: sandbox
+ } = options;
+
var document = aSafeWin.document;
- var _xmlhttpRequester = null;
- var _storage = null;
- var _resources = null;
- var _logger = null;
var menuCmdIDs = [];
- var Scriptish_BrowserUI = aChromeWin.Scriptish_BrowserUI;
+ var Scriptish_BrowserUI = aChromeWin ? aChromeWin.Scriptish_BrowserUI : null;
var windowID = aWinID;
- function getXmlhttpRequester() {
- if (!_xmlhttpRequester) {
- var tools = {};
- Cu.import("resource://scriptish/api/GM_xmlhttpRequester.js", tools);
- _xmlhttpRequester = new tools.GM_xmlhttpRequester(
- aUnsafeContentWin, aURL, aScript);
- }
- return _xmlhttpRequester;
- }
- function getStorage() {
- if (!_storage) {
- var tools = {};
- Cu.import("resource://scriptish/api/GM_ScriptStorage.js", tools);
- _storage = new tools.GM_ScriptStorage(aScript);
- }
- return _storage;
- }
- function getResources() {
- if (!_resources) {
- var tools = {};
- Cu.import("resource://scriptish/api/GM_Resources.js", tools);
- _resources = new tools.GM_Resources(aScript);
+ var lazyLoaders = {};
+ lazy(lazyLoaders, "xhr", function() {
+ return new GM_xmlhttpRequester(aUnsafeContentWin, aSafeWin, aURL, aScript, sandbox);
+ });
+ lazy(lazyLoaders, "storage", function() {
+ return new GM_ScriptStorage(aScript, aSafeWin);
+ });
+ lazy(lazyLoaders, "resources", function() {
+ return new GM_Resources(aScript);
+ });
+
+ this.GM_safeHTMLParser = function GM_safeHTMLParser(aHTMLStr, aBaseURL) {
+ if (!GM_apiLeakCheck("GM_safeHTMLParser")) return;
+
+ let doc = document.implementation.createDocument("", "",
+ document.implementation.createDocumentType("html", "", ""));
+ doc.appendChild(doc.createElement("html"));
+ doc.documentElement.appendChild(doc.createElement("body"));
+
+ let baseURI;
+ let frag;
+
+ if ("undefined" !== typeof aBaseURL) {
+ try {
+ baseURI = NetUtil.newURI(aBaseURL);
+ }
+ catch(e) {
+ throw Error(Scriptish_stringBundle("error.api.safeHTMLParser.url"));
+ }
}
- return _resources;
- }
- function getLogger() {
- if (!_logger) {
- var tools = {};
- Cu.import("resource://scriptish/api/GM_ScriptLogger.js", tools);
- _logger = new tools.GM_ScriptLogger(aScript);
+ else {
+ baseURI = null;
}
- return _logger;
- }
- this.GM_addStyle = function GM_addStyle(css) {
- var head = document.getElementsByTagName("head")[0];
- if (head) {
- var style = document.createElement("style");
- style.textContent = css;
- style.type = "text/css";
- head.appendChild(style);
+ // Try to use the newer nsIParserUtils (Gecko >= 14)
+ if ("pu" in Services) {
+ frag = Services.pu.parseFragment(aHTMLStr, 0, false, baseURI, doc.body);
}
- return style;
- }
-
- this.GM_safeHTMLParser = function GM_safeHTMLParser(aHTMLStr) {
- if (!GM_apiLeakCheck("GM_safeHTMLParser")) return;
- let doc = document.implementation.createDocument(NS_XHTML, "html", null);
- let body = document.createElementNS(NS_XHTML, "body");
- doc.documentElement.appendChild(body);
- body.appendChild(Services.suhtml.parseFragment(aHTMLStr, false, null, body));
+ // Otherwise fall back to deprecated nsIScriptableUnescapeHTML
+ else {
+ frag = Services.suhtml.parseFragment(aHTMLStr, false, baseURI, doc.body);
+ }
+ doc.adoptNode(frag);
+ doc.body.appendChild(frag);
return doc;
}
- this.GM_log = function GM_log() getLogger().log.apply(getLogger(), arguments)
-
this.GM_notification =
function GM_notification(aMsg, aTitle, aIcon, aCallback) {
if (!GM_apiLeakCheck("GM_notification")) return;
if (typeof aTitle != "string") aTitle = aScript.name;
if (typeof aIcon != "string") aIcon = aScript.iconURL;
+
var callback = null;
if (typeof aCallback == "function")
- callback = function() GM_apiSafeCallback(aSafeWin, null, aCallback);
+ callback = function() GM_apiSafeCallback(aSafeWin, aScript, null, aCallback);
Scriptish_notification(aMsg, aTitle, aIcon, callback);
- }
+ };
- this.GM_setValue = function GM_setValue() {
+ this.GM_setValue = function GM_setValue(aName, aValue) {
if (!GM_apiLeakCheck("GM_setValue")) return;
- return getStorage().setValue.apply(getStorage(), arguments);
- }
+ return lazyLoaders.storage.setValue.apply(lazyLoaders.storage, arguments);
+ };
this.GM_getValue = function GM_getValue() {
if (!GM_apiLeakCheck("GM_getValue")) return;
- return getStorage().getValue.apply(getStorage(), arguments);
- }
+ return lazyLoaders.storage.getValue.apply(lazyLoaders.storage, arguments);
+ };
this.GM_deleteValue = function GM_deleteValue() {
if (!GM_apiLeakCheck("GM_deleteValue")) return;
- return getStorage().deleteValue.apply(getStorage(), arguments);
+ return lazyLoaders.storage.deleteValue.apply(lazyLoaders.storage, arguments);
+ };
+ this.GM_watchValue = function GM_watchValue(aName, aListener) {
+ if (!GM_apiLeakCheck("GM_watchValue")) return;
+ let listener = null;
+ if ("function" === typeof aListener) {
+ listener = function(aEvent) {
+ GM_apiSafeCallback(aSafeWin, aScript, null, aListener, [aEvent]);
+ };
+ }
+ return lazyLoaders.storage.watchValue.call(lazyLoaders.storage, aName, listener);
+ }
+ this.GM_unwatchValue = function GM_unwatchValue() {
+ if (!GM_apiLeakCheck("GM_unwatchValue")) return;
+ return lazyLoaders.storage.unwatchValue.apply(lazyLoaders.storage, arguments);
}
this.GM_listValues = function GM_listValues() {
if (!GM_apiLeakCheck("GM_listValues")) return;
- return getStorage().listValues.apply(getStorage(), arguments);
- }
-
- this.GM_setClipboard = function GM_setClipboard() {
- if (!GM_apiLeakCheck("GM_setClipboard")) return;
- var tools = {};
- Cu.import("resource://scriptish/api/GM_setClipboard.js", tools);
- tools.GM_setClipboard.apply(null, arguments);
- }
+ return lazyLoaders.storage.listValues.apply(lazyLoaders.storage, arguments);
+ };
- this.GM_getResourceURL = function GM_getResourceURL() {
+ this.GM_getResourceURL = function GM_getResourceURL(aName) {
if (!GM_apiLeakCheck("GM_getResourceURL")) return;
- return getResources().getResourceURL.apply(getResources(), arguments)
+ return lazyLoaders.resources.getResourceURL.apply(lazyLoaders.resources, arguments)
}
- this.GM_getResourceText = function GM_getResourceText() {
+ this.GM_getResourceText = function GM_getResourceText(aName) {
if (!GM_apiLeakCheck("GM_getResourceText")) return;
- return getResources().getResourceText.apply(getResources(), arguments)
+ return lazyLoaders.resources.getResourceText.apply(lazyLoaders.resources, arguments)
}
this.GM_getMetadata = function(aKey, aLocalVal) {
- let key = aKey.toLowerCase().trim();
- if (aLocalVal) {
- switch (key) {
- case "id":
- case "name":
- case "namespace":
- case "creator":
- case "author":
- case "description":
- case "version":
- case "jsversion":
- case "delay":
- case "noframes":
- return aScript[key];
- case "homepage":
- case "homepageurl":
- return aScript.homepageURL;
- case "updateurl":
- return aScript.updateURL;
- case "contributor":
- case "include":
- case "exclude":
- case "screenshot":
- return aScript[key + "s"];
- case "match":
- return aScript[key + "es"];
- }
- }
-
- return aScript.getScriptHeader(key);
+ if (!GM_apiLeakCheck("GM_getMetadata")) return;
+ return Scriptish_getScriptHeader(aScript, aKey, aLocalVal);
}
- this.GM_openInTab = function GM_openInTab(aURL, aReuse) {
+ this.GM_openInTab = function GM_openInTab(aURL, aLoadInBackground, aReuse) {
if (!GM_apiLeakCheck("GM_openInTab")) return;
- // Try to reuse an existing tab
- if (aReuse) {
- let browserEnumerator = Services.wm.getEnumerator("navigator:browser");
-
- while (browserEnumerator.hasMoreElements()) {
- let browserWin = browserEnumerator.getNext();
- let tabBrowser = browserWin.gBrowser;
- let i = tabBrowser.browsers.length - 1;
-
- for (; ~i; i--) {
- let browser = tabBrowser.getBrowserAtIndex(i);
- // TODO: check rel=canonical too
- if (aURL === browser.currentURI.spec) {
- tabBrowser.selectedTab = tabBrowser.tabContainer.childNodes[i];
- browserWin.focus();
- return getWindowForBrowser(browser);
- }
- }
- }
+ // fix relative links
+ aURL = NetUtil.newURI(
+ aURL, null, NetUtil.newURI(aSafeWin.location.href, null, null)).spec;
+
+ // open new tab as a child tab of the caller if Tree Style Tab
+ // ( https://addons.mozilla.org/firefox/addon/tree-style-tab/ ) there.
+ if (aChromeWin.TreeStyleTabService &&
+ aChromeWin.TreeStyleTabService.readyToOpenChildTabNow) {
+ aChromeWin.TreeStyleTabService.readyToOpenChildTabNow(aSafeWin);
}
- // Opening a new tab
- return getWindowForBrowser(aChromeWin.gBrowser
- .getBrowserForTab(aChromeWin.gBrowser.addTab(aURL)))
+ Scriptish_openInTab(aURL, aLoadInBackground, aReuse, aChromeWin);
+ return undefined; // don't return the window, this is intentional
}
this.GM_xmlhttpRequest = function GM_xmlhttpRequest() {
if (!GM_apiLeakCheck("GM_xmlhttpRequest")) return;
- let xhr = getXmlhttpRequester();
+ let xhr = lazyLoaders.xhr;
return xhr.contentStartRequest.apply(xhr, arguments);
}
- if (aSafeWin !== aSafeWin.top) {
- this.GM_unregisterMenuCommand = this.GM_registerMenuCommand = DOLITTLE;
- } else {
+ if (!Scriptish_BrowserUI || aSafeWin !== aSafeWin.top) {
+ this.GM_unregisterMenuCommand = this.GM_registerMenuCommand
+ = this.GM_disableMenuCommand = this.GM_enableMenuCommand = DOLITTLE;
+ }
+ else {
this.GM_registerMenuCommand = function GM_registerMenuCommand(
aCmdName, aCmdFunc, aAccelKey, aAccelModifiers, aAccessKey) {
if (!GM_apiLeakCheck("GM_registerMenuCommand")) return;
+
var uuid = Scriptish_BrowserUI.registerMenuCommand({
name: aCmdName,
accelKey: aAccelKey,
@@ -228,7 +268,9 @@ function GM_API(aScript, aURL, aWinID, aSafeWin, aUnsafeContentWin, aChromeWin)
accessKey: aAccessKey,
doCommand: aCmdFunc,
winID: windowID});
+
menuCmdIDs.push(uuid);
+
return uuid;
}
@@ -238,19 +280,35 @@ function GM_API(aScript, aURL, aWinID, aSafeWin, aUnsafeContentWin, aChromeWin)
menuCmdIDs.splice(i, 1);
return Scriptish_BrowserUI.unregisterMenuCommand(aUUID, windowID);
}
+
+ this.GM_enableMenuCommand = function GM_enableMenuCommand(aUUID) {
+ var i = menuCmdIDs.indexOf(aUUID);
+ if (!~i) return false; // check the uuid is for a cmd made by the same script
+ return Scriptish_BrowserUI.enableMenuCommand(aUUID, windowID);
+ }
+
+ this.GM_disableMenuCommand = function GM_disableMenuCommand(aUUID) {
+ var i = menuCmdIDs.indexOf(aUUID);
+ if (!~i) return false; // check the uuid is for a cmd made by the same script
+ return Scriptish_BrowserUI.disableMenuCommand(aUUID, windowID);
+ }
+
+ this.GM_addStyle = function GM_addStyle(aSource) {
+ if (!GM_apiLeakCheck("GM_addStyle")) return;
+ attach(Style({ source: aSource }), aSafeWin);
+ }
}
this.GM_cryptoHash = function GM_cryptoHash() {
if (!GM_apiLeakCheck("GM_cryptoHash")) return;
- var tools = {};
- Cu.import("resource://scriptish/utils/Scriptish_cryptoHash.js", tools);
- return tools.Scriptish_cryptoHash.apply(null, arguments);
+ return Scriptish_cryptoHash.apply(null, arguments);
}
+}
- this.GM_generateUUID = function() Services.uuid.generateUUID().toString();
+GM_API.prototype.GM_generateUUID = function GM_generateUUID() (
+ Services.uuid.generateUUID().toString());
- this.GM_updatingEnabled = true;
+GM_API.prototype.GM_setClipboard = function() {
+ if (!GM_apiLeakCheck("GM_setClipboard")) return;
+ GM_setClipboard.apply(null, arguments);
}
-
-function getWindowForBrowser(browser) browser.docShell
- .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
diff --git a/extension/modules/api/GM_Resources.js b/extension/modules/api/GM_Resources.js
index 22f698d1..c569adc0 100644
--- a/extension/modules/api/GM_Resources.js
+++ b/extension/modules/api/GM_Resources.js
@@ -1,3 +1,4 @@
+"use strict";
var EXPORTED_SYMBOLS = ["GM_Resources"];
const Cu = Components.utils;
@@ -25,6 +26,6 @@ GM_Resources.prototype.getDep = function(name) {
return resource;
}
}
- throw new Error(
- Scriptish_stringBundle("error.api.noResourceWithName") + ": '" + name + "'");
+ throw Error(
+ Scriptish_stringBundle("error.api.noResourceWithName") + ": '" + name + "'");
}
diff --git a/extension/modules/api/GM_ScriptLogger.js b/extension/modules/api/GM_ScriptLogger.js
index e6b1e8bf..19e32be9 100644
--- a/extension/modules/api/GM_ScriptLogger.js
+++ b/extension/modules/api/GM_ScriptLogger.js
@@ -1,3 +1,4 @@
+"use strict";
var EXPORTED_SYMBOLS = ["GM_ScriptLogger"];
Components.utils.import("resource://scriptish/logging.js");
diff --git a/extension/modules/api/GM_ScriptStorage.js b/extension/modules/api/GM_ScriptStorage.js
index 190f3e92..11d4be24 100644
--- a/extension/modules/api/GM_ScriptStorage.js
+++ b/extension/modules/api/GM_ScriptStorage.js
@@ -1,19 +1,36 @@
var EXPORTED_SYMBOLS = ["GM_ScriptStorage"];
-const Cu = Components.utils;
-Cu.import("resource://scriptish/prefmanager.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+Components.utils.import("resource://scriptish/constants.js");
-function GM_ScriptStorage(script) {
- this.prefMan = new Scriptish_PrefManager(script.prefroot);
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_PrefManager"]);
+
+lazyUtil(this, "stringBundle");
+lazyUtil(this, "windowUnloader");
+
+const { getInnerId } = jetpack('sdk/window/utils');
+
+function GM_ScriptStorage(aScript, aSafeWin) {
+ this.prefMan = new Scriptish_PrefManager(aScript.prefroot);
+ this._watchedPrefs = Object.create(null);
+
+ // Be sure to remove any watchers when the window unloads
+ let winID = getInnerId(aSafeWin);
+ Scriptish_windowUnloader(function() {
+ let prefChanged = this._prefChanged.bind(this);
+
+ for (let name in this._watchedPrefs) {
+ this.prefMan.unwatch(name, prefChanged);
+ delete this._watchedPrefs[name];
+ }
+ }.bind(this), winID);
}
GM_ScriptStorage.prototype.setValue = function(name, val) {
if (2 !== arguments.length) {
- throw new Error(Scriptish_stringBundle("error.api.noSecondArgValue"));
+ throw Error(Scriptish_stringBundle("error.api.noSecondArgValue"));
}
- this.prefMan.setValue(name, val);
+ return this.prefMan.setValue(name, val);
};
GM_ScriptStorage.prototype.getValue = function(name, defVal) {
@@ -24,6 +41,85 @@ GM_ScriptStorage.prototype.deleteValue = function(name) {
return this.prefMan.remove(name);
};
+// Notifies any script watchers of a preference change
+GM_ScriptStorage.prototype._prefChanged = function(aName) {
+ if (!(aName in this._watchedPrefs)) {
+ return;
+ }
+ let newValue = this.getValue(aName);
+ let watchers = this._watchedPrefs[aName].watchers;
+ for (let i = 0, e = watchers.length; i < e; ++i) {
+ watchers[i].notify({
+ __exposedProps__: {
+ name: "r",
+ oldValue: "r",
+ newValue: "r"
+ },
+ name: aName,
+ oldValue: this._watchedPrefs[aName].currentValue,
+ newValue: newValue
+ });
+ }
+ this._watchedPrefs[aName].currentValue = newValue;
+}
+
+GM_ScriptStorage.prototype.watchValue = function(aName, aListener) {
+ // Make sure we were passed everything
+ if (!(aName && aListener && "function" === typeof aListener)) {
+ throw Error(Scriptish_stringBundle("error.api.badArguments"));
+ }
+
+ // Nothing to do if the preference doesn't exist
+ if (!this.prefMan.exists(aName)) {
+ throw Error(Scriptish_stringBundle("error.api.prefNotFound"));
+ }
+
+ // Generate a UUID for the watcher so it can be unwatched by the user
+ let uuid = Services.uuid.generateUUID().toString();
+ let watcher = {"notify": aListener, "uuid": uuid};
+
+ // Add to existing watchers, or set up a new pref watcher
+ if (aName in this._watchedPrefs) {
+ this._watchedPrefs[aName].watchers.push(watcher);
+ }
+ else {
+ this._watchedPrefs[aName] = {
+ currentValue: this.getValue(aName),
+ watchers: [watcher]
+ };
+
+ // Start watching the pref
+ this.prefMan.watch(aName, this._prefChanged.bind(this));
+ }
+
+ // Return the watcher's UUID so it can be unwatched by the user
+ return uuid;
+};
+
+GM_ScriptStorage.prototype.unwatchValue = function(aName, aUUID) {
+ // Nothing to do if the preference doesn't exist or isn't watched
+ if (!(aName in this._watchedPrefs && this.prefMan.exists(aName))) {
+ return false;
+ }
+
+ let watchers = this._watchedPrefs[aName].watchers;
+
+ // If given a UUID, only remove the specified watcher
+ if ("string" === typeof aUUID) {
+ for (let i = 0, e = watchers.length; i < e; ++i) {
+ if (aUUID === watchers[i].uuid) {
+ watchers.splice(i, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Otherwise remove all watchers for the preference
+ watchers.length = 0;
+ return true;
+}
+
GM_ScriptStorage.prototype.listValues = function() {
return this.prefMan.listValues();
};
diff --git a/extension/modules/api/GM_console.js b/extension/modules/api/GM_console.js
index ca697abd..020e2107 100644
--- a/extension/modules/api/GM_console.js
+++ b/extension/modules/api/GM_console.js
@@ -1,18 +1,154 @@
-var EXPORTED_SYMBOLS = ["GM_console"];
-Components.utils.import("resource://scriptish/api/GM_ScriptLogger.js");
+"use strict";
+var EXPORTED_SYMBOLS = ["GM_console", "GM_getConsoleFor"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/logging.js");
+Cu.import("resource://scriptish/utils/Scriptish_getFirebugConsole.js");
+
+// moz-4: log, warn, error, info
+// moz-5: debug (alias to log)
+// moz-6: trace (callstack)
+const log_functions = ["log", "debug", "warn", "error", "info", "trace"];
// based on http://www.getfirebug.com/firebug/firebugx.js
-const keys = [
- "debug", "warn", "error", "info", "assert", "dir", "dirxml",
- "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile",
- "profileEnd"];
-
-function GM_console(script) {
- for (var i = keys.length - 1, key; key = keys[i--];)
- this[key] = function() {};
-
- // Important to use this private variable so that user scripts can't make
- // this call something else by redefining or .
- var logger = new GM_ScriptLogger(script);
- this.log = function() { logger.log(Array.slice(arguments).join("\n")); };
+const aux_functions = ["assert", "dir", "dirxml", "group", "groupEnd", "time",
+ "timeEnd", "count", "profile", "profileEnd"
+ ];
+
+function GM_getConsoleFor(contentWindow, chromeWindow) {
+ // we dont have a chromeWindow for e10s atm, see Scriptish_injectScripts impl
+ if (chromeWindow) {
+ let (rv = Scriptish_getFirebugConsole(contentWindow, chromeWindow)) {
+ if (rv) return rv;
+ }
+
+ if (contentWindow.console) {
+ return contentWindow.console;
+ }
+ }
+
+ return {
+ log: function() Scriptish_log(Array.slice(arguments).join(" "), true)
+ };
+}
+
+function GM_console_legacy(script, contentWindow, chromeWindow) {
+ const _console = GM_getConsoleFor(contentWindow, chromeWindow);
+ const console = { __exposedProps__: {__noSuchMethod__: "r"} };
+ const prefix = "[" + (script.id || "Scriptish") + "]";
+
+ // Wrap log functions
+ // Redirect any missing log function to .log
+ for (let i = 0, e = log_functions.length; i < e; ++i) {
+ let fn = log_functions[i];
+ console.__exposedProps__[fn] = "r";
+ if (fn in _console) {
+ console[fn] = _console[fn].bind(_console, prefix);
+ }
+ else if (fn == "trace") {
+ console.trace = function() {
+ let args = Array.slice(arguments);
+ let msg = "";
+
+ // Skip the top two frames
+ let stack = Components.stack.caller;
+ if (stack && (stack = stack.caller)) {
+ for (let i = 0; i < 10 && stack; ++i, stack = stack.caller) {
+ msg += "\n[@" + stack.filename + ":" + stack.lineNumber + "]";
+ }
+ args.push(msg);
+ }
+ console.log.apply(console, args);
+ };
+ }
+ else {
+ console[fn] = console.log.bind(console);
+ }
+ }
+
+ // Wrap aux functions
+ for (let i = 0, e = aux_functions.length; i < e; ++i) {
+ let fn = aux_functions[i];
+ if (fn in console) {
+ console[fn] = _console[fn].bind(_console);
+ console.__exposedProps__[fn] = "r";
+ }
+ }
+ Object.defineProperty(console, "__noSuchMethod__", {
+ value: function(id, args) {
+ if (aux_functions.indexOf(id) != -1) {
+ let fn = _console[id] || (function() {});
+ return fn.apply(_console, args);
+ }
+ console.log("No such method in console", id);
+ }
+ });
+
+ return console;
+}
+
+function GM_console(sandbox, script, contentWindow, chromeWindow) {
+ if (!("createObjectIn" in Cu) || !("exportFunction" in Cu)) {
+ return GM_console_legacy(script, contentWindow, chromeWindow);
+ }
+
+ const _console = GM_getConsoleFor(contentWindow, chromeWindow);
+ const console = Cu.createObjectIn(sandbox, {defineAs: "console"});
+ const prefix = "[" + (script.id || "Scriptish") + "]";
+
+ // Wrap log functions
+ // Redirect any missing log function to .log
+ for (let i = 0, e = log_functions.length; i < e; ++i) {
+ let fn = log_functions[i];
+ if (fn in _console) {
+ Cu.exportFunction(_console[fn].bind(_console, prefix), console, {
+ defineAs: fn
+ });
+ }
+ else if (fn == "trace") {
+ let trace = function() {
+ let args = Array.slice(arguments);
+ let msg = "";
+
+ // Skip the top two frames
+ let stack = Components.stack.caller;
+ if (stack && (stack = stack.caller)) {
+ for (let i = 0; i < 10 && stack; ++i, stack = stack.caller) {
+ msg += "\n[@" + stack.filename + ":" + stack.lineNumber + "]";
+ }
+ args.push(msg);
+ }
+ console.log.apply(console, args);
+ };
+ Cu.exportFunction(trace, console, {defineAs: "trace"});
+ }
+ else {
+ Cu.exportFunction(_console.log.bind(_console, prefix), console, {
+ defineAs: fn
+ });
+ }
+ }
+
+ // Wrap aux functions
+ for (let i = 0, e = aux_functions.length; i < e; ++i) {
+ let fn = aux_functions[i];
+ if (fn in _console) {
+ Cu.exportFunction(_console[fn].bind(_console, prefix), console, {
+ defineAs: fn
+ });
+ }
+ }
+
+ const nsm = function(id, args) {
+ if (aux_functions.indexOf(id) != -1) {
+ let fn = _console[id] || (function() {});
+ return fn.apply(_console, args);
+ }
+ console.log("No such method in console", id);
+ };
+ Cu.exportFunction(nsm, console, {
+ defineAs: "__noSuchMethod__"
+ });
+
+ return console;
}
diff --git a/extension/modules/api/GM_sandboxScripts.js b/extension/modules/api/GM_sandboxScripts.js
new file mode 100644
index 00000000..3e02be12
--- /dev/null
+++ b/extension/modules/api/GM_sandboxScripts.js
@@ -0,0 +1,15 @@
+"use strict";
+const EXPORTED_SYMBOLS = ["GM_SandboxScripts"];
+
+if (!('XMLHttpRequest' in this)) {
+ this.XMLHttpRequest = Components.Constructor(
+ "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
+}
+
+const GM_SandboxScripts = (function(r) {
+ r = new XMLHttpRequest();
+ r.overrideMimeType('text/javascript'); // don't try to parse as XML
+ r.open('GET', 'resource://scriptish/api/sandboxScripts.js', false);
+ r.send(null);
+ return r.responseText;
+})();
diff --git a/extension/modules/api/GM_setClipboard.js b/extension/modules/api/GM_setClipboard.js
index e60d45bb..8d082225 100644
--- a/extension/modules/api/GM_setClipboard.js
+++ b/extension/modules/api/GM_setClipboard.js
@@ -1,8 +1,3 @@
var EXPORTED_SYMBOLS = ["GM_setClipboard"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
-function GM_setClipboard(aData, aType) {
- if (aType && aType != "text")
- throw "'" + aType + "' " + Scriptish_stringBundle("error.api.clipboard.type");
- Services.cb.copyString(aData);
-}
+const { set: GM_setClipboard } = jetpack("sdk/clipboard");
diff --git a/extension/modules/api/GM_xmlhttpRequester.js b/extension/modules/api/GM_xmlhttpRequester.js
index 3269416d..6393247e 100644
--- a/extension/modules/api/GM_xmlhttpRequester.js
+++ b/extension/modules/api/GM_xmlhttpRequester.js
@@ -1,17 +1,92 @@
+"use strict";
var EXPORTED_SYMBOLS = ["GM_xmlhttpRequester"];
-(function(inc){
- inc("resource://scriptish/constants.js");
- inc("resource://scriptish/logging.js");
- inc("resource://scriptish/utils/Scriptish_stringBundle.js");
- inc("resource://scriptish/api.js");
-})(Components.utils.import)
-const MIME_JSON = /^(application|text)\/(?:x-)?json/i
+const Cu = Components.utils;
-function GM_xmlhttpRequester(unsafeContentWin, originUrl, aScript) {
+Components.utils.import("resource://scriptish/constants.js");
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+lazyImport(this, "resource://scriptish/api.js",
+ ["GM_apiSafeCallback", "GM_waiveXrays", "GM_checkedPrincipal"]);
+lazyUtil(this, "stringBundle");
+
+const MIME_JSON = /^(application|text)\/(?:x-)?json/i;
+
+/**
+ * Abstract base class for (chained) request notification callback overrides
+ *
+ * Use such overrides sparely, as the individual request performance might
+ * degrade quite a bit.
+ *
+ * @param req XMLHttpRequest (chrome)
+ * @author Nils Maier
+ */
+function NotificationCallbacks(req) {
+ throw Error("trying to initiate an abstract NotificationCallbacks");
+}
+NotificationCallbacks.prototype = {
+ init: function(req) {
+ // rewrite notification callbacks
+ this._channel = req.channel;
+ this._notificationCallbacks = this._channel.notificationCallbacks;
+ this._channel.notificationCallbacks = this;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterfaceRequestor]),
+ getInterface: function(iid) {
+ try {
+ return this.query(iid);
+ }
+ catch (ex) {
+ return this.queryOriginal(iid);
+ }
+ },
+ queryOriginal: function(iid) {
+ if (this._notificationCallbacks) {
+ return this._notificationCallbacks.getInterface(iid);
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+/**
+ * Ignore (specific) redirects
+ * @param req XMLHttpRequest (chrome)
+ * @author Nils Maier
+ */
+function IgnoreRedirect(req, ignoreFlags) {
+ this.init(req);
+ this.ignoreFlags = ignoreFlags;
+}
+IgnoreRedirect.prototype = subclass(NotificationCallbacks.prototype, {
+ query: XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),
+ asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
+ if (this.ignoreFlags & flags) {
+ // must throw here, not call callback.onRedirectVerifyCallback,
+ // or else it will completely cancel the request
+ throw Cr.NS_ERROR_UNEXPECTED;
+ }
+
+ try {
+ let ces = this.queryOriginal(Ci.nsIChannelEventSink);
+ if (ces) {
+ ces.asyncOnChannelRedirect(oldChannel, newChannel, flags, callback);
+ return;
+ }
+ }
+ catch (ex) {}
+
+ callback.onRedirectVerifyCallback(Cr.NS_OK);
+ }
+});
+
+
+function GM_xmlhttpRequester(unsafeContentWin, safeWin, originUrl, aScript,
+ sandbox) {
this.unsafeContentWin = unsafeContentWin;
+ this.safeWin = safeWin;
this.originUrl = originUrl;
this.script = aScript;
+ this.sandbox = sandbox;
}
// this function gets called by user scripts in content security scope to
@@ -23,20 +98,19 @@ function GM_xmlhttpRequester(unsafeContentWin, originUrl, aScript) {
// can't support mimetype because i think it's only used for forcing
// text/xml and we can't support that
GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
- Scriptish_log("> GM_xmlhttpRequest.contentStartRequest");
-
try {
// Validate and parse the (possibly relative) given URL.
var uri = NetUtil.newURI(details.url, null, NetUtil.newURI(this.originUrl));
var url = uri.spec;
- } catch (e) {
+ }
+ catch (e) {
// A malformed URL won't be parsed properly.
- throw new Error(Scriptish_stringBundle("error.api.reqURL") + ": " + details.url);
+ throw Error(Scriptish_stringBundle("error.api.reqURL") + ": " + details.url);
}
// check if the script is allowed to access the url
if (!this.script.matchesDomain(url))
- throw new Error(
+ throw Error(
"User script is attempting access to restricted domain '" + uri.host + "'",
this.script.fileURL);
@@ -46,16 +120,20 @@ GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
case "http":
case "https":
case "ftp":
+ case "data":
+ case "moz-blob":
+ case "blob":
var req = Instances.xhr;
this.chromeStartRequest(url, details, req);
break;
default:
- throw new Error(Scriptish_stringBundle("error.api.reqURL.scheme") + ": " + details.url);
+ throw Error(Scriptish_stringBundle("error.api.reqURL.scheme") + ": " + details.url);
}
- Scriptish_log("< GM_xmlhttpRequest.contentStartRequest");
-
return {
+ __exposedProps__: {
+ abort: "r"
+ },
abort: function() {
req.abort();
}
@@ -66,23 +144,49 @@ GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
// that it can access other domains without security warning
GM_xmlhttpRequester.prototype.chromeStartRequest =
function(safeUrl, details, req) {
- Scriptish_log("> GM_xmlhttpRequest.chromeStartRequest");
-
this.setupRequestEvent(this.unsafeContentWin, req, "onload", details);
+ this.setupRequestEvent(this.unsafeContentWin, req, "onloadend", details);
this.setupRequestEvent(this.unsafeContentWin, req, "onerror", details);
- this.setupRequestEvent(
- this.unsafeContentWin, req, "onreadystatechange", details);
+ this.setupRequestEvent(this.unsafeContentWin, req, "onprogress", details);
+ this.setupRequestEvent(this.unsafeContentWin, req, "onreadystatechange", details);
if (details.mozBackgroundRequest) req.mozBackgroundRequest = true;
req.open(
- details.method, safeUrl, true, details.user || "", details.password || "");
+ details.method || "GET",
+ safeUrl,
+ true,
+ details.user || "",
+ details.password || ""
+ );
if (details.overrideMimeType) req.overrideMimeType(details.overrideMimeType);
if (details.ignoreCache)
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // bypass cache
+ if (details.ignoreRedirect)
+ new IgnoreRedirect(req,
+ Ci.nsIChannelEventSink.REDIRECT_TEMPORARY | Ci.nsIChannelEventSink.REDIRECT_PERMANENT);
+ if (details.ignoreTempRedirect)
+ new IgnoreRedirect(req, Ci.nsIChannelEventSink.REDIRECT_TEMPORARY);
+ if (details.ignorePermanentRedirect)
+ new IgnoreRedirect(req, Ci.nsIChannelEventSink.REDIRECT_PERMANENT);
+
+ let redirectionLimit = null;
+ if (details.failOnRedirect) {
+ redirectionLimit = 0;
+ }
+ if ("redirectionLimit" in details) {
+ if (details.redirectionLimit < 0 || details.redirectionLimit > 10) {
+ throw Error("redirectionLimit must be within (0, 10), but it is " + details.redirectionLimit);
+ }
+ redirectionLimit = details.redirectionLimit;
+ }
+ if (redirectionLimit !== null && req.channel instanceof Ci.nsIHttpChannel) {
+ req.channel.redirectionLimit = redirectionLimit;
+ }
+
if (details.headers) {
var headers = details.headers;
@@ -92,11 +196,15 @@ GM_xmlhttpRequester.prototype.chromeStartRequest =
}
}
+ // Loads initiated from private windows should always be private as well.
+ let makePrivate = details.makePrivate || PrivateBrowsingUtils.isWindowPrivate(this.safeWin);
+ if (makePrivate && req.channel instanceof Ci.nsIPrivateBrowsingChannel) {
+ req.channel.setPrivate(true);
+ }
+
var body = details.data ? details.data : null;
if (details.binary) req.sendAsBinary(body);
else req.send(body);
-
- Scriptish_log("< GM_xmlhttpRequest.chromeStartRequest");
}
// arranges for the specified 'event' on xmlhttprequest 'req' to call the
@@ -104,17 +212,26 @@ GM_xmlhttpRequester.prototype.chromeStartRequest =
// window's security context.
GM_xmlhttpRequester.prototype.setupRequestEvent =
function(unsafeContentWin, req, event, details) {
- Scriptish_log("> GM_xmlhttpRequester.setupRequestEvent");
-
var origMimeType = details.overrideMimeType;
+ var script = this.script;
- if (details[event]) {
- req[event] = function() {
- Scriptish_log("> GM_xmlhttpRequester -- callback for " + event);
+ var realDetails = GM_waiveXrays(details);
+ var realEvent = GM_checkedPrincipal(this.sandbox, realDetails[event]);
+ if (realEvent) {
+ req[event] = function(ev) {
var responseState = {
// can't support responseXML because security won't
// let the browser call properties on it
+ __exposedProps__: {
+ responseText: "r",
+ responseJSON: "r",
+ readyState: "r",
+ responseHeaders: "r",
+ status: "r",
+ statusText: "r",
+ finalUrl: "r"
+ },
responseText: req.responseText,
readyState: req.readyState,
responseHeaders: null,
@@ -122,7 +239,15 @@ GM_xmlhttpRequester.prototype.setupRequestEvent =
statusText: null,
finalUrl: null
};
- if (4 == req.readyState && 'onerror' != event) {
+ if ("onprogress" == event) {
+ responseState.__exposedProps__.lengthComputable = "r";
+ responseState.__exposedProps__.loaded = "r";
+ responseState.__exposedProps__.total = "r";
+ responseState.lengthComputable = ev.lengthComputable;
+ responseState.loaded = ev.loaded;
+ responseState.total = ev.total;
+ }
+ else if (4 == req.readyState && "onerror" != event) {
responseState.responseHeaders = req.getAllResponseHeaders();
responseState.status = req.status;
responseState.statusText = req.statusText;
@@ -130,7 +255,7 @@ GM_xmlhttpRequester.prototype.setupRequestEvent =
|| MIME_JSON.test(details.overrideMimeType)
|| MIME_JSON.test(req.channel.contentType)) {
try {
- responseState.responseJSON = Instances.json.decode(req.responseText);
+ responseState.responseJSON = JSON.parse(req.responseText);
} catch (e) {
responseState.responseJSON = {};
}
@@ -139,11 +264,7 @@ GM_xmlhttpRequester.prototype.setupRequestEvent =
}
GM_apiSafeCallback(
- unsafeContentWin, details, details[event], [responseState]);
-
- Scriptish_log("< GM_xmlhttpRequester -- callback for " + event);
+ unsafeContentWin, script, realDetails, realEvent, [responseState]);
}
}
-
- Scriptish_log("< GM_xmlhttpRequester.setupRequestEvent");
}
diff --git a/extension/modules/api/sandboxScripts.js b/extension/modules/api/sandboxScripts.js
new file mode 100644
index 00000000..2971109e
--- /dev/null
+++ b/extension/modules/api/sandboxScripts.js
@@ -0,0 +1,83 @@
+
+var GM_updatingEnabled = true;
+
+function GM_xpath(details) {
+ var contextNode, contextDocument, paths, resolver, namespace, result;
+
+ if (typeof details == 'string') {
+ details = { path: details }
+ }
+
+ contextNode = "node" in details ? details.node : document;
+ if (!contextNode) {
+ throw new Error("The value specified for node is invalid");
+ }
+
+ if (contextNode.ownerDocument) {
+ contextDocument = contextNode.ownerDocument;
+ }
+ else if (contextNode.evaluate) {
+ // contextNode is a Document already
+ contextDocument = contextNode;
+ }
+ else {
+ throw new Error("No owning document for the specified node. Make sure you pass a valid node!");
+ }
+
+ paths = details.paths || details.path;
+ if (typeof paths == "string") {
+ paths = [paths];
+ }
+
+ if (details.resolver) {
+ if (typeof details.resolver == "string") {
+ resolver = {lookupNamespaceURI: function(p) details.resolver};
+ }
+ else if (typeof details.resolver == "function") {
+ resolver = {lookupNamespaceURI: details.resolver};
+ }
+ else if (typeof resolver.lookupNamespaceURI == "function") {
+ resolver = details.resolver;
+ }
+ else {
+ throw new Error("resolver is invalid");
+ }
+ }
+ else if (contextNode.namepaceURI) {
+ namespace = contextNode.namespaceURI;
+ resolver = {lookupNamespaceURI: function(p) namespace};
+ }
+
+ if (details.all) {
+ var rv = [], n;
+ for (var [,path] in Iterator(paths)) {
+ result = contextDocument.evaluate(
+ path,
+ contextNode,
+ resolver,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE,
+ null
+ );
+ while (n = result.iterateNext()) {
+ rv.push(n);
+ }
+ }
+ return rv;
+ }
+
+ for (var [,path] in Iterator(paths)) {
+ result = contextDocument.evaluate(
+ path,
+ contextNode,
+ resolver,
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
+ null
+ ).singleNodeValue;
+
+ if (result) {
+ return result;
+ }
+ }
+
+ return null;
+}
diff --git a/extension/modules/commonjs/alert.js b/extension/modules/commonjs/alert.js
new file mode 100644
index 00000000..c0eb4920
--- /dev/null
+++ b/extension/modules/commonjs/alert.js
@@ -0,0 +1,13 @@
+'use strict';
+
+const { Cu } = require('chrome')
+const { setTimeout } = require('sdk/timers')
+
+const { Services } = Cu.import("resource://scriptish/constants.js", {});
+
+function alert(aMsg, aTitle, aWait) {
+ if (typeof aWait == "number")
+ return setTimeout(function() alert(aMsg, aTitle), aWait);
+ Services.prompt.alert(null, aTitle || "Scriptish", aMsg+"");
+}
+exports.alert = alert;
diff --git a/extension/modules/commonjs/security/api-check-filenames.js b/extension/modules/commonjs/security/api-check-filenames.js
new file mode 100644
index 00000000..b0ebcfb1
--- /dev/null
+++ b/extension/modules/commonjs/security/api-check-filenames.js
@@ -0,0 +1,32 @@
+"use strict";
+
+let compiled = null;
+let filenames = [];
+let prefixes = [];
+
+const escapeRegex = string => string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
+
+function compile() {
+ let rv = filenames.map(e => "^" + escapeRegex(e) + "$").
+ concat(prefixes.map(e => "^" + escapeRegex(e))).
+ join("|");
+ compiled = new RegExp(rv);
+}
+
+function add(filename) {
+ filenames.push(filename);
+ compile();
+}
+exports.add = add;
+
+let prefixes = [];
+function addPrefix(prefix) {
+ prefixes.push(prefix);
+ compile();
+}
+exports.addPrefix = addPrefix;
+
+function check(filename) {
+ return compiled.test(filename);
+}
+exports.check = check;
diff --git a/extension/modules/commonjs/setup-uso-blocking.js b/extension/modules/commonjs/setup-uso-blocking.js
new file mode 100644
index 00000000..b345a555
--- /dev/null
+++ b/extension/modules/commonjs/setup-uso-blocking.js
@@ -0,0 +1,13 @@
+'use strict';
+
+const { PageMod } = require('sdk/page-mod');
+
+exports.setup = function(IDs) {
+ let mod = PageMod({
+ include: RegExp('https?://userscripts.org/scripts/[^/]*/(' + IDs.join('|') + ').*'),
+ contentScriptFile: 'resource://scriptish/page-mods/blocked-uso-scripts.js',
+ contentScriptWhen: 'end',
+ attachTo: ['existing', 'top', 'frame']
+ });
+ return mod;
+}
diff --git a/extension/modules/commonjs/uso-redirector.js b/extension/modules/commonjs/uso-redirector.js
new file mode 100644
index 00000000..edd66fe8
--- /dev/null
+++ b/extension/modules/commonjs/uso-redirector.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const { Ci, Cu } = require('chrome');
+const events = require('sdk/system/events');
+
+const { NetUtil } = Cu.import('resource://gre/modules/NetUtil.jsm', {});
+
+function applyRedirect({ subject }) {
+ const channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ const { URI } = channel;
+
+ if (URI.host == 'userscripts.org' && URI.scheme == 'http') {
+ let to = NetUtil.newURI(URI.spec);
+ to.scheme = 'https';
+
+ channel.redirectTo(to);
+ }
+}
+events.on('http-on-modify-request', applyRedirect);
diff --git a/extension/modules/config.js b/extension/modules/config.js
new file mode 100644
index 00000000..d3ba35f8
--- /dev/null
+++ b/extension/modules/config.js
@@ -0,0 +1,11 @@
+var EXPORTED_SYMBOLS = ["Scriptish_config"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/config/config.js", ["Config"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+
+const Scriptish_config = new Config("scriptish_scripts");
+Scriptish_config.load(function() {
+ Scriptish_log("Scriptish config loaded"); // TODO: force & l10n
+});
diff --git a/extension/modules/config/config.js b/extension/modules/config/config.js
index 89eab27e..e1f8f0f1 100644
--- a/extension/modules/config/config.js
+++ b/extension/modules/config/config.js
@@ -1,44 +1,40 @@
var EXPORTED_SYMBOLS = ["Config"];
-const SCRIPTISH_CONFIG = "scriptish-config.xml";
+const SCRIPTISH_CONFIG_JSON = "scriptish-config.json";
const SCRIPTISH_BLOCKLIST = "scriptish-blocklist.json";
-(function(inc) {
-inc("resource://scriptish/constants.js");
-inc("resource://scriptish/logging.js");
-inc("resource://scriptish/prefmanager.js");
-inc("resource://scriptish/scriptish.js");
-inc("resource://scriptish/utils/Scriptish_getContents.js");
-inc("resource://scriptish/utils/Scriptish_getWriteStream.js");
-inc("resource://scriptish/utils/Scriptish_getProfileFile.js");
-inc("resource://scriptish/utils/Scriptish_notification.js");
-inc("resource://scriptish/utils/Scriptish_stringBundle.js");
-inc("resource://scriptish/utils/Scriptish_convert2RegExp.js");
-inc("resource://scriptish/utils/Scriptish_cryptoHash.js");
-inc("resource://scriptish/third-party/Timer.js");
-inc("resource://scriptish/script/script.js");
-})(Components.utils.import);
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://scriptish/script/script.js", ["Script"]);
+lazyImport(this, "resource://scriptish/utils/Scriptish_isURLExcluded.js", [
+ "Scriptish_isURLExcluded",
+ "Scriptish_addExcludes",
+ "Scriptish_setExcludes",
+ "Scriptish_getExcludes"
+]);
+const { setTimeout, clearTimeout } = jetpack('sdk/timers');
+
+lazyUtil(this, "cryptoHash");
+lazyUtil(this, "injectScripts");
+lazyUtil(this, "isScriptRunnable");
+lazyUtil(this, "getContents");
+lazyUtil(this, "getProfileFile");
+lazyUtil(this, "getWriteStream");
+lazyUtil(this, "stringBundle");
function Config(aBaseDir) {
- this.timer = new Timer();
+ var self = this;
+ this.loaded = false;
this._observers = [];
this._saveTimer = null;
- this._excludes = [];
- this._excludeRegExps = [];
this._scripts = [];
this._scriptFoldername = aBaseDir;
- let configFile = this._scriptDir;
- configFile.append(SCRIPTISH_CONFIG);
- if (!configFile.exists()) {
- (configFile = this._scriptDir).append("config.xml");
- if (!configFile.exists())
- (configFile = this._scriptDir).append(SCRIPTISH_CONFIG);
- }
-
- this._configFile = configFile;
-
- this._updateSecurely = Scriptish_prefRoot.getValue("update.requireSecured");
+ this._prettyPrint = Scriptish_prefRoot.getValue("config.prettyPrint.enabled");
this._useBlocklist = Scriptish_prefRoot.getValue("blocklist.enabled");
this._blocklistURL = Scriptish_prefRoot.getValue("blocklist.url");
@@ -47,37 +43,54 @@ function Config(aBaseDir) {
(this._blocklistFile = this._scriptDir).append(SCRIPTISH_BLOCKLIST);
this._initScriptDir();
- this._load();
- (this._configFile = this._scriptDir).append(SCRIPTISH_CONFIG);
-
- Components.utils.import("resource://scriptish/addonprovider.js");
+ this._initBlocklist();
+
+ [
+ "scriptish-script-installed",
+ "scriptish-script-modified",
+ "scriptish-script-edit-enabled",
+ "scriptish-script-user-prefs-change",
+ "scriptish-script-prefs-change",
+ "scriptish-script-updated",
+ "scriptish-script-uninstalled",
+ "scriptish-script-uninstall-canceled",
+ "scriptish-script-removed",
+ "scriptish-preferences-change",
+ "scriptish-config-saved"
+ ].forEach(function(i) Services.obs.addObserver(self, i, false));
}
Config.prototype = {
- addObserver: function(observer, script) {
- var observers = script ? script._observers : this._observers;
- observers.push(observer);
- },
+ get _scriptDir() Scriptish_getProfileFile(this._scriptFoldername),
- removeObserver: function(observer, script) {
- var observers = script ? script._observers : this._observers;
- var index = observers.indexOf(observer);
- if (index == -1)
- throw new Error(Scriptish_stringBundle("error.observerNotFound"));
- observers.splice(index, 1);
+ get _tempFile() {
+ let tmp = this._scriptDir;
+ tmp.append(SCRIPTISH_CONFIG_JSON + ".tmp");
+ return tmp;
},
- _notifyObservers: function(script, event, data) {
- var observers = this._observers.concat(script._observers);
- for (var i = 0, observer; observer = observers[i]; i++) {
- observer.notifyEvent(script, event, data);
+ observe: function(aSubject, aTopic, aData) {
+ var data = JSON.parse(aData || "{}");
+ switch(aTopic) {
+ case "scriptish-script-user-prefs-change":
+ Scriptish.notify(
+ aSubject, "scriptish-script-modified", {saved:true, reloadUI:false});
+ case "scriptish-script-prefs-change":
+ case "scriptish-script-installed":
+ case "scriptish-script-modified":
+ case "scriptish-script-edit-enabled":
+ case "scriptish-script-updated":
+ case "scriptish-script-uninstalled":
+ case "scriptish-script-uninstall-canceled":
+ case "scriptish-script-removed":
+ case "scriptish-preferences-change":
+ if (data.saved) Scriptish.notify(null, "scriptish-config-saved", null);
+ break;
+ case "scriptish-config-saved":
+ this._save();
+ break;
}
},
- _changed: function(script, event, data, dontSave) {
- if (!dontSave) this._save();
- this._notifyObservers(script, event, data);
- },
-
installIsUpdate: function(script) this._find(script.id) > -1,
addScript: function(aScript) { this._scripts.push(aScript); },
@@ -109,22 +122,29 @@ Config.prototype = {
req.onload = function() {
var json = req.responseText;
try {
- var blocklist = Instances.json.decode(json);
+ var blocklist = JSON.parse(json);
} catch (e) {
return;
}
if (!blocklist.uso) return;
var hash = Scriptish_cryptoHash(json);
- if (self._blocklistHash == hash) return;
+ if (self._blocklistHash == hash)
+ return;
let file = self._blocklistFile;
// write blocklist
- var BLStream = Scriptish_getWriteStream(file);
- BLStream.write(json, json.length);
- BLStream.close();
- Scriptish_log("Updated Scriptish blocklist");
+ let converter = Instances.suc;
+ converter.charset = "UTF-8";
+ let writeStream = Scriptish_getWriteStream(file, {defer:true, safe:true});
+ NetUtil.asyncCopy(
+ converter.convertToInputStream(json), // source must be buffered!
+ writeStream,
+ function() writeStream.finish()
+ );
+
+ Scriptish_log("Updated Scriptish blocklist " + SCRIPTISH_BLOCKLIST);
self._blocklist = blocklist;
self._blocklistHash = hash;
@@ -135,147 +155,215 @@ Config.prototype = {
},
_loadBlocklist: function() {
- if (this._blocklistFile.exists()) {
- let blockListContents = Scriptish_getContents(this._blocklistFile);
- let blocklist = Instances.json.decode(blockListContents);
- this._blocklist = blocklist;
- this._blocklistHash = Scriptish_cryptoHash(blockListContents);
-
- // block scripts
- this._blockScripts();
- }
+ let self = this;
+ timeout(function() {
+ var file = self._blocklistFile;
+ if (file.exists()) {
+ Scriptish_getContents(file, 0, function(str) {
+ self._blocklist = JSON.parse(str);
+
+ self._blocklistHash = Scriptish_cryptoHash(str);
+
+ // block scripts
+ self._blockScripts();
+ });
+ }
+ });
},
- _load: function() {
- // load config
+ _loadJSON: function(aFile, aCallback) {
+ Scriptish_log("Scriptish Config._loadJSON");
var self = this;
- var configContents = Scriptish_getContents(this._configFile);
- var doc = Instances.dp.parseFromString(configContents, "text/xml");
- var nodes = doc.evaluate("/UserScriptConfig/Script | /UserScriptConfig/Exclude", doc, null, 0, null);
- var fileModified = false;
- let excludes = [];
-
- for (var node; node = nodes.iterateNext();) {
- switch (node.nodeName) {
- case "Script":
- fileModified = Script.load(this, node) || fileModified;
- break;
- case "Exclude":
- excludes.push(node.firstChild.nodeValue.trim());
- break;
+ var config = {};
+
+ if (aFile.exists()) {
+ var str = Scriptish_getContents(aFile);
+ if (!str) return aCallback(false);
+
+ try {
+ config = JSON.parse(str);
+ } catch(e) {
+ // Unable to parse the file.
+ return aCallback(false);
}
+
+ // load scripts
+ var fileModified = false, scripts = config.scripts;
+ for (var i = scripts.length - 1; ~i; i--)
+ fileModified = Script.loadFromJSON(self, scripts[i]) || fileModified;
+
+ // load global excludes
+ Scriptish_addExcludes(config.excludes);
+
+ return aCallback(true, fileModified);
}
- this.addExclude(excludes);
- // Watch for the required secure updates pref being modified
- Scriptish_prefRoot.watch("update.requireSecured", function() {
- self._updateSecurely = Scriptish_prefRoot.getValue("update.requireSecured");
+ aCallback(false);
+ },
+
+ load: function(aCallback) {
+ Scriptish_log("Scriptish Config.load");
+ var self = this;
+
+ // called after the config has been loaded
+ function callback(fileModified) {
+ let scripts = self.scripts;
+
+ // Improves first-run startup time to punt this until a script is installed
+ if (scripts.length) {
+ // Try to fetch uso usage data if some time has passed
+ let interval = Scriptish_prefRoot.getValue("update.uso.interval");
+ let lastFetch = Scriptish_prefRoot.getValue("update.uso.lastFetch");
+ let now = Math.ceil(Date.now() / 1E3);
+
+ if ((now - lastFetch) >= interval) {
+ Scriptish_prefRoot.setValue("update.uso.lastFetch", now);
+
+ // Fetch uso data
+ setTimeout(function next() {
+ if (!scripts.length)
+ return;
+ return scripts.pop().updateUSOData().then(next, next);
+ }, 500);
+ }
+ }
+
+ // Load the blocklist
+ if (self._useBlocklist) {
+ self._loadBlocklist();
+ }
+
+ // Flag config as loaded
+ self.loaded = true;
+
+ if (fileModified) self._save();
+ aCallback();
+ };
+
+ // Load the config (trying from various sources)
+ var configFile;
+
+ // source: Scriptish JSON tmp; for pre-nsISafeOutputStream compatiblity
+ self._loadJSON(self._tempFile, function(aSuccess) {
+ if (aSuccess) return callback(true);
+
+ // source: Scriptish JSON
+ // NOTE: this is the only case where we care if the file was modified
+ (configFile = self._scriptDir).append(SCRIPTISH_CONFIG_JSON);
+ self._loadJSON(configFile, function(aSuccess, aModified) {
+ if (aSuccess)
+ return callback(aModified);
+ callback(true);
+ });
+ });
+
+ Scriptish_prefRoot.watch("config.prettyPrint.enabled", function() {
+ self._prettyPrint = Scriptish_prefRoot.getValue("config.prettyPrint.enabled");
+ self._save();
});
- // Listen for the blocklist pref being modified
Scriptish_prefRoot.watch("blocklist.enabled", function() {
self._useBlocklist = Scriptish_prefRoot.getValue("blocklist.enabled");
if (self._useBlocklist) {
// Blocklist was enabled. Load the blocklist.
self._loadBlocklist();
- } else {
+ }
+ else {
// Blocklist was disabled. Clean things up.
self._blocklist = {};
}
});
+ },
- // Load the blocklist
- if (this._useBlocklist) this._loadBlocklist();
+ toJSON: function() ({
+ excludes: Scriptish_getExcludes(),
+ scripts: this.scripts.map(function(script) script.toJSON())
+ }),
- // Try to fetch uso usage data if some time has passed
- let interval = Scriptish_prefRoot.getValue("update.uso.interval");
- let lastFetch = Scriptish_prefRoot.getValue("update.uso.lastFetch");
- let now = Math.ceil((new Date()).getTime() / 1E3);
- if (now - lastFetch >= interval) {
- Scriptish_prefRoot.setValue("update.uso.lastFetch", now);
-
- // Fetch uso data
- let scripts = this._scripts;
- for (var i = scripts.length - 1; ~i; i--) {
- let script = scripts[i];
- if (!script.blocked && script.isUSOScript())
- timeout(script.updateUSOData.bind(script), i * 100);
- }
+ _usoBlockingPageMod: null,
+ _blockScripts: function() {
+ var scripts = this._scripts;
+ for (let i = scripts.length - 1; ~i; i--) {
+ scripts[i].doBlockCheck();
}
- // the delay b4 save here is very important now that config.xml is used when
- // scriptish-config.xml DNE
- if (fileModified) this._save();
+ // setup USO blocking page mod
+ if (this._usoBlockingPageMod) {
+ this._usoBlockingPageMod.destroy();
+ this._usoBlockingPageMod = null;
+ }
+ this._usoBlockingPageMod = jetpack('scriptish/setup-uso-blocking').setup(this._blocklist.uso);
},
- _blockScripts: function() {
- var scripts = this._scripts;
- for (var i = scripts.length - 1; ~i; i--) scripts[i].doBlockCheck();
- },
+ _save: function(aSaveNow) {
+ if (!this.loaded) return; // a safety check that should not be relied on
- _save: function(saveNow) {
// If we have not explicitly been told to save now, then defer execution
// via a timer, to avoid locking up the UI.
- if (!saveNow) {
+ if (!aSaveNow) {
// Reduce work in the case of many changes near to each other in time.
- if (this._saveTimer) this.timer.clearTimeout(this._saveTimer);
- this._saveTimer =
- this.timer.setTimeout(this._save.bind(this, true), 250);
+ if (this._saveTimer) clearTimeout(this._saveTimer);
+ this._saveTimer = setTimeout(this._save.bind(this, true), 250);
return;
}
delete this["_saveTimer"];
- var doc = Instances.dp.parseFromString("", "text/xml");
- var scripts = this._scripts;
- var len = scripts.length;
- var firstChild = doc.firstChild;
- let nt = "\n\t";
- function addNode(str, node) firstChild.appendChild(doc.createTextNode(str))
- && node && firstChild.appendChild(node);
-
- // add script info
- for (var i = 0, script; script = scripts[i]; i++)
- addNode(nt, script.createXMLNode(doc));
-
- // add global excludes info
- for (let [, exclude] in Iterator(this.excludes))
- addNode(nt, doc.createElement("Exclude"))
- .appendChild(doc.createTextNode(exclude));
-
- addNode("\n");
-
- var configStream = Scriptish_getWriteStream(this._configFile);
- Instances.ds.serializeToStream(doc, configStream, "utf-8");
- configStream.close();
+ if (this._isSaving)
+ return (this._pendingSave = true);
+
+ this._isSaving = true;
+
+ // make sure that the configFile is SCRIPTISH_CONFIG_JSON
+ (this._configFile = this._scriptDir).append(SCRIPTISH_CONFIG_JSON);
+
+ Scriptish_log(
+ Scriptish_stringBundle("saving") + " " + SCRIPTISH_CONFIG_JSON);
+
+ let converter = Instances.suc;
+ converter.charset = "UTF-8";
+ let json = JSON.stringify(this.toJSON(), null, this._prettyPrint ? " " : null);
+ let writeStream = Scriptish_getWriteStream(this._configFile, {defer:true, safe:true});
+ NetUtil.asyncCopy(
+ converter.convertToInputStream(json), // source must be buffered!
+ writeStream,
+ function() {
+ writeStream.finish();
+ delete this._isSaving;
+ if (this._pendingSave) {
+ delete this._pendingSave;
+ this._save();
+ }
+ }.bind(this));
},
- parse: function(source, uri, aUpdateScript) (
- Script.parse(this, source, uri, aUpdateScript)),
+ parse: function(source, aDownloadURI, aUpdateScript, aPrivate) (
+ Script.parse(this, source, aDownloadURI, aUpdateScript, aPrivate)),
install: function(aNewScript) {
- var existingIndex = this._find(aNewScript.id);
- var exists = existingIndex > -1;
- if (exists) {
- this._scripts[existingIndex].replaceScriptWith(aNewScript);
- } else {
+ let existingIndex = this._find(aNewScript.id);
+ let script = null;
+
+ if (existingIndex >= 0) {
+ script = this._scripts[existingIndex];
+ script.replaceScriptWith(aNewScript);
+ }
+ else {
aNewScript.installProcess();
this.addScript(aNewScript);
- this._changed(aNewScript, "install", null);
- AddonManagerPrivate.callInstallListeners(
- "onExternalInstall", null, aNewScript, null, false);
-
- // notification that install is complete
- var msg = "'" + aNewScript.name;
- if (aNewScript.version) msg += " " + aNewScript.version;
- msg += "' " + Scriptish_stringBundle("statusbar.installed");
- Scriptish_notification(msg, null, null, function() Scriptish.openManager());
+ script = aNewScript;
+
+ Scriptish.notify(aNewScript, "scriptish-script-installed", true);
}
+
+ // Update the USO review data
+ script.updateUSOData();
+
this.sortScripts();
},
uninstallScripts: function() {
let scripts = this._scripts;
- for (var i = scripts.length - 1; i >= 0; i--) {
+ for (var i = scripts.length; ~--i;) {
let script = scripts[i];
if (script.needsUninstall) {
this._scripts.splice(i, 1);
@@ -284,76 +372,69 @@ Config.prototype = {
}
},
- get _scriptDir() Scriptish_getProfileFile(this._scriptFoldername),
-
_initScriptDir: function() {
// create an empty configuration if none exist.
var dir = this._scriptDir;
- if (!dir.exists()) {
- // create script folder
+ if (!dir.exists())
dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+ },
- // create config.xml file
- var configStream = Scriptish_getWriteStream(this._configFile);
- var xml = "";
- configStream.write(xml, xml.length);
- configStream.close();
- }
-
+ _initBlocklist: function() {
if (!this._useBlocklist) return;
// Try to fetch a new list if blocklist is enabled and some time has passed
let interval = Scriptish_prefRoot.getValue("blocklist.interval");
let lastFetch = Scriptish_prefRoot.getValue("blocklist.lastFetch");
- let now = Math.ceil((new Date()).getTime() / 1E3);
+ let now = Math.ceil(Date.now() / 1E3);
if (now - lastFetch >= interval) {
Scriptish_prefRoot.setValue("blocklist.lastFetch", now);
this._fetchBlocklist();
}
},
- get updateSecurely() this._updateSecurely,
-
- get excludes() this._excludes.concat(),
- set excludes(excludes) {
- this._excludes = [];
- this._excludeRegExps = [];
- this.addExclude(excludes)
- },
- get excludeRegExps() this._excludeRegExps.concat(),
- addExclude: function(excludes) {
- if (!excludes) return;
- excludes = (typeof excludes == "string") ? [excludes] : excludes;
- for (let [, exclude] in Iterator(excludes)) {
- this._excludes.push(exclude);
- this._excludeRegExps.push(Scriptish_convert2RegExp(exclude));
- }
- },
-
get scripts() this._scripts.concat(),
getMatchingScripts: function(testFunc) this.scripts.filter(testFunc),
sortScripts: function() this._scripts.sort(function(a, b) b.priority - a.priority),
- injectScript: function(script) {
- var unsafeWin = this.wrappedContentWin.wrappedJSObject;
- var unsafeLoc = new XPCNativeWrapper(unsafeWin, "location").location;
- var href = new XPCNativeWrapper(unsafeLoc, "href").href;
-
- if (script.enabled && !script.needsUninstall && script.matchesURL(href))
- Services.scriptish.injectScripts([script], href, unsafeWin, this.chromeWin);
- },
updateModifiedScripts: function(scriptInjector) {
+ var self = this;
+
for (let [, script] in Iterator(this._scripts)) {
if (script.delayInjection) {
script.delayedInjectors.push(scriptInjector);
continue;
}
- if (!script.isModified()) continue;
- let parsedScript = this.parse(
- script.textContent,
- script._downloadURL && NetUtil.newURI(script._downloadURL), script);
- script.updateFromNewScript(parsedScript, scriptInjector);
+
+ if (!script.isModified())
+ continue;
+
+ let theScript = script;
+ Scriptish_getContents(script._file, 0, function(content) {
+ let parsedScript = self.parse(
+ content,
+ theScript._downloadURL && NetUtil.newURI(theScript._downloadURL),
+ theScript);
+ theScript.updateFromNewScript(parsedScript, scriptInjector);
+ });
}
- this._save();
- }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver])
}
+
+Config.prototype.initScripts = function(url, isTopWin, aCallback) {
+ let scripts = {
+ "document-start": [],
+ "document-end": [],
+ "document-idle": [],
+ "document-complete": [],
+ "window-load": []
+ };
+
+ this.getMatchingScripts(function(script) {
+ let chk = Scriptish_isScriptRunnable(script, url, isTopWin);
+ if (chk) scripts[script.runAt].push(script);
+ return chk;
+ }, [url]);
+
+ aCallback(scripts);
+};
diff --git a/extension/modules/config/configdownloader.js b/extension/modules/config/configdownloader.js
index a00fae4a..7bd1c3e8 100644
--- a/extension/modules/config/configdownloader.js
+++ b/extension/modules/config/configdownloader.js
@@ -1,10 +1,10 @@
var EXPORTED_SYMBOLS = ["Scriptish_configDownloader"];
-Components.utils.import("resource://scriptish/script/scriptdownloader.js");
Components.utils.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/script/scriptdownloader.js", ["ScriptDownloader"]);
var Scriptish_configDownloader = {
- startInstall: function(aURI, aWin) {
- new ScriptDownloader(aURI, aWin).startInstall();
+ startInstall: function(aURI, aPrivate) {
+ new ScriptDownloader(aURI, aPrivate).startInstall();
},
startViewScript: function(aURI) {
new ScriptDownloader(aURI).startViewScript();
diff --git a/extension/modules/constants.js b/extension/modules/constants.js
index a414e2d9..1c6737cd 100644
--- a/extension/modules/constants.js
+++ b/extension/modules/constants.js
@@ -1,18 +1,19 @@
var EXPORTED_SYMBOLS = [
- "Cc", "Ci", "AddonManager", "AddonManagerPrivate", "NetUtil", "XPCOMUtils",
- "Services", "Instances", "timeout"];
+ "Cc", "Ci", "Cr", "NetUtil", "XPCOMUtils", "extend", "subclass", "jetpack",
+ "Services", "Instances", "lazy", "lazyImport", "lazyUtil", "timeout"];
-const {classes: Cc, interfaces: Ci} = Components;
-const ONE_SHOT = Ci.nsITimer.TYPE_ONE_SHOT;
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, Constructor: CC} = Components;
+
+const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
+
+const global = this;
var Services = {};
(function(inc, tools){
inc("resource://gre/modules/XPCOMUtils.jsm");
inc("resource://gre/modules/NetUtil.jsm");
- inc("resource://gre/modules/AddonManager.jsm");
- inc("resource://gre/modules/Services.jsm", tools);
-
- Services.__proto__ = tools.Services;
-})(Components.utils.import, {})
+ inc("resource://gre/modules/Services.jsm" );
+ Services = Object.create(Services);
+})(Components.utils.import, {});
var Instances = {
get bis() Cc["@mozilla.org/binaryinputstream;1"]
@@ -25,8 +26,12 @@ var Instances = {
.createInstance(Ci.nsIDOMSerializer),
get fos() Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream),
+ get ftc() Cc["@mozilla.org/feed-textconstruct;1"]
+ .createInstance(Ci.nsIFeedTextConstruct),
+ get sfos() Cc["@mozilla.org/network/safe-file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream)
+ .QueryInterface(Ci.nsISafeOutputStream),
get fp() Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker),
- get json() Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON),
get lf() Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile),
get process() Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess),
get se() Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError),
@@ -34,57 +39,143 @@ var Instances = {
.createInstance(Ci.nsISupportsString),
get suc() Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter),
+ get cos() Cc["@mozilla.org/intl/converter-output-stream;1"]
+ .createInstance(Ci.nsIConverterOutputStream),
get timer() Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
get wbp() Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Ci.nsIWebBrowserPersist),
+ get xfr() Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable),
get xhr() Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest)
};
-XPCOMUtils.defineLazyGetter(Services, "scriptish", function() (
- Cc["@scriptish.erikvold.com/scriptish-service;1"]
- .getService().wrappedJSObject));
+const lazy = XPCOMUtils.defineLazyGetter.bind(XPCOMUtils);
+const lazyService = XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils);
-XPCOMUtils.defineLazyServiceGetter(
- Services, "as", "@mozilla.org/alerts-service;1", "nsIAlertsService");
-
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "ass", "@mozilla.org/appshell/appShellService;1",
"nsIAppShellService");
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "cb", "@mozilla.org/widget/clipboardhelper;1",
"nsIClipboardHelper");
-XPCOMUtils.defineLazyServiceGetter(
- Services, "eps", "@mozilla.org/uriloader/external-protocol-service;1",
- "nsIExternalProtocolService");
+lazyService(
+ Services, "cbs", "@mozilla.org/widget/clipboard;1", "nsIClipboard");
+
+lazyService(
+ Services, "cs", "@mozilla.org/consoleservice;1", "nsIConsoleService");
-if (Cc["@mozilla.org/privatebrowsing;1"]) {
- XPCOMUtils.defineLazyServiceGetter(
- Services, "pbs", "@mozilla.org/privatebrowsing;1",
- "nsIPrivateBrowsingService");
-} else {
- Services.pbs = {privateBrowsingEnabled: false};
+if (Cc["@mozilla.org/parserutils;1"]) {
+ lazyService(
+ Services, "pu", "@mozilla.org/parserutils;1", "nsIParserUtils");
}
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "sis", "@mozilla.org/scriptableinputstream;1",
"nsIScriptableInputStream");
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "suhtml", "@mozilla.org/feed-unescapehtml;1",
"nsIScriptableUnescapeHTML");
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "tld", "@mozilla.org/network/effective-tld-service;1",
"nsIEffectiveTLDService");
-XPCOMUtils.defineLazyServiceGetter(
+lazyService(
Services, "uuid", "@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
-function timeout(cb, delay) {
- Instances.timer.initWithCallback(
- { notify: function(){ cb.call(null) } }, delay || 0, ONE_SHOT);
+const _lazyModules = {};
+function lazyImport(obj, resource, symbols) {
+ if (!(resource in _lazyModules)) {
+ lazy(_lazyModules, resource, function() {
+ let _m = {};
+ try {
+ Components.utils.import(resource, _m);
+ }
+ catch (e) {
+ Components.utils.reportError("Failed to import resource: " + resource);
+ Components.utils.reportError(e);
+ throw e;
+ }
+ return _m;
+ });
+ }
+
+ for (let i = symbols.length; ~--i;) {
+ let s = symbols[i];
+ lazy(obj, s, function() _lazyModules[resource][s]);
+ }
+}
+function lazyUtil(obj, name) {
+ return lazyImport(obj, "resource://scriptish/utils/Scriptish_" + name + ".js", ["Scriptish_" + name]);
}
+
+function extend(a, o) {
+ for (var k in a) {
+ if (!o[k]) {
+ o[k] = a[k];
+ }
+ }
+ return o;
+}
+
+function subclass(clazz, prototype) {
+ let rv = Object.create(clazz);
+ for (let x of Object.getOwnPropertyNames(prototype)) {
+ Object.defineProperty(rv , x, Object.getOwnPropertyDescriptor(prototype, x));
+ }
+ return rv;
+}
+
+function descriptor(object) {
+ let value = {};
+ Object.getOwnPropertyNames(object).forEach(function(name) {
+ value[name] = Object.getOwnPropertyDescriptor(object, name)
+ });
+ return value;
+}
+
+const { Loader } = Components.utils.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
+
+const loader = Loader.Loader({
+ id: "scriptish@erikvold.com",
+ name: "Scriptish",
+ preferencesBranch: "scriptish",
+ modules: {
+ "toolkit/loader": Loader,
+ '@test/options': {}
+ },
+ paths: {
+ "devtools": "resource:///modules/devtools/",
+ "scriptish/": "resource://scriptish/commonjs/",
+ "pathfinder/": "resource://scriptish/pathfinder/",
+ "sdk/": "resource://gre/modules/commonjs/sdk/",
+ "": "resource://gre/modules/commonjs/"
+ },
+ rootURI: '',
+ metadata: {
+ 'permissions': {
+ 'private-browsing': true
+ }
+ },
+ resolve: function(id, base) {
+ if (id == "chrome" || id.startsWith("@"))
+ return id;
+ return Loader.resolve(id, base);
+ }
+});
+
+// fake requirer uri scriptish:// (it's used for relative requires and error messages)
+const module = Loader.Module("main", "scriptish://");
+const jetpack = Loader.Require(loader, module);
+
+const jpGlobals = jetpack('sdk/system/globals');
+
+// Inject globals ASAP in order to have console API working ASAP
+Object.defineProperties(loader.globals, descriptor(jpGlobals));
+
+const { setTimeout: timeout } = jetpack('sdk/timers');
diff --git a/extension/modules/content/browser.js b/extension/modules/content/browser.js
index 009aee3b..c833c61f 100644
--- a/extension/modules/content/browser.js
+++ b/extension/modules/content/browser.js
@@ -1,70 +1,79 @@
var EXPORTED_SYMBOLS = ["Scriptish_BrowserUIM"];
-const Cu = Components.utils;
-Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/logging.js");
-Cu.import("resource://scriptish/scriptish.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+Components.utils.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyUtil(this, "openManager");
-const ICON_16_ON = "chrome://scriptish/skin/icon_16.png";
-const ICON_16_OFF = "chrome://scriptish/skin/icon_16_disabled.png";
-const ICON_24_ON = "chrome://scriptish/skin/icon_24.png";
-const ICON_24_OFF = "chrome://scriptish/skin/icon_24_disabled.png";
-const ICON_32_ON = "chrome://scriptish/skin/icon_medium.png";
-const ICON_32_OFF = "chrome://scriptish/skin/icon_32_disabled.png";
+const tabs = jetpack('sdk/tabs');
+const prefs = jetpack('sdk/preferences/service');
+
+const ICON_16_ON = "chrome://scriptish/skin/scriptish16.png";
+const ICON_16_OFF = "chrome://scriptish/skin/scriptish16_disabled.png";
+const ICON_24_ON = "chrome://scriptish/skin/scriptish24.png";
+const ICON_24_OFF = "chrome://scriptish/skin/scriptish24_disabled.png";
function Scriptish_BrowserUIM(aWin, aBrowserUI) {
this.$ = function(aID) aWin.document.getElementById(aID);
this._win = aWin;
+ this._optionsWin = null;
this._browserUI = aBrowserUI;
-
- // listen for clicks on the install bar
- Services.obs.addObserver(aBrowserUI, "install-userscript", true);
}
Scriptish_BrowserUIM.prototype = {
onIconClick: function(aEvt) {
- if ("menu-button" == aEvt.originalTarget.type) return;
- switch (aEvt.button) {
- case 0:
- this.onToggleStatus();
- break;
- case 1:
- Scriptish.openManager();
- break;
- case 2:
- this.$("scriptish-tb-popup").openPopup(this.$("scriptish-button"), "before_end", 0, 0, false, false);
- break;
+ if (aEvt.button === 1 && aEvt.target.id === "scriptish-button") {
+ Scriptish_openManager();
+ aEvt.preventDefault();
+ aEvt.stopPropagation();
}
},
- onToggleStatus: function() {
- Scriptish.enabled = !Scriptish.enabled;
- },
+ onToggleStatus: function() Scriptish.enabled = !Scriptish.enabled,
refreshStatus: function() {
var tbImg = this.$("scriptish-button-brd");
var menu = this.$("scriptish_general_menu");
- var appName = Services.appinfo.name;
if (Scriptish.enabled) {
menu.setAttribute("image", ICON_16_ON);
tbImg.removeAttribute("scriptish-disabled");
- } else {
+ }
+ else {
menu.setAttribute("image", ICON_16_OFF);
tbImg.setAttribute("scriptish-disabled", "scriptish-disabled");
}
},
- openChromeWindow: function(aURL) {
+ openChromeWindow: function(aURL, aParams) {
+ aParams = aParams || null;
Services.ww.openWindow(
this._win, aURL, null, "chrome,dependent,centerscreen,resizable,dialog",
- null);
+ aParams);
},
- newUserScript: function() {
- this.openChromeWindow("chrome://scriptish/content/newscript.xul");
+ newUserScript: function(aContent) {
+ var params = null;
+ aContent = aContent || null;
+ if (aContent) {
+ params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ params.SetString(0, aContent);
+ }
+ this.openChromeWindow("chrome://scriptish/content/newscript.xul", params);
},
openOptionsWin: function() {
- this.openChromeWindow("chrome://scriptish/content/options.xul");
+ let instantApply = prefs.get("browser.preferences.instantApply", false);
+
+ if (!this._optionsWin || this._optionsWin.closed) {
+ this._optionsWin = this._win.openDialog(
+ "chrome://scriptish/content/options.xul",
+ "scriptish-options-dialog",
+ "chrome,titlebar,toolbar,resizable,centerscreen" + (instantApply ? ",dialog=no" : ",modal")
+ );
+ }
+ this._optionsWin.focus();
},
showUserscriptList: function() {
- Cu.import("resource://scriptish/addonprovider.js");
- timeout(Scriptish.openManager);
+ Scriptish_openManager();
+ },
+ reportIssue: function() {
+ tabs.open({
+ url: 'https://github.com/scriptish/scriptish/issues'
+ })
}
}
diff --git a/extension/modules/logging.js b/extension/modules/logging.js
index f1175278..1e1733aa 100644
--- a/extension/modules/logging.js
+++ b/extension/modules/logging.js
@@ -1,8 +1,8 @@
-var EXPORTED_SYMBOLS = ["Scriptish_logError", "Scriptish_log"];
-(function(inc){
- inc("resource://scriptish/constants.js");
- inc("resource://scriptish/prefmanager.js");
-})(Components.utils.import)
+var EXPORTED_SYMBOLS = ["Scriptish_logError", "Scriptish_logScriptError", "Scriptish_log"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
// Utility to create an error message in the log without throwing an error.
function Scriptish_logError(aErr, opt_warn, fileName, lineNumber) {
@@ -17,8 +17,110 @@ function Scriptish_logError(aErr, opt_warn, fileName, lineNumber) {
Services.console.logMessage(e);
}
+/**
+ * Utility to log scripts in such a way that the web console will pick it up
+ *
+ * @author nmaier
+ * @param aError {object} Any supported script error
+ * @param aWindow {window} The content window to associate the error with
+ * @param aFileURL {object} Optional. nsIURI or .toString() giving the error location
+ * @param aId {string} Optional. Script id.
+ */
+function Scriptish_logScriptError(aError, aWindow, aFileURL, aId) {
+ try {
+ let se = Instances.se;
+
+ let windowId = 0;
+ try {
+ windowId = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .currentInnerWindowID;
+ }
+ catch (e) {
+ throw Error("failed to get window id");
+ }
+
+ // defaults
+ var errorMessage = "";
+ var sourceName = null;
+ var sourceLine = "";
+ var lineNumber = 0;
+ var columnNumber = 0;
+ var flags = se.errorFlag;
+ var category = "scriptish userscript error";
+
+ // get what we can
+ if (aError instanceof Ci.nsIScriptError) {
+ var {errorMessage, sourceName, sourceLine, lineNumber, columnNumber, flags, category} = aError;
+ }
+ else if (aError instanceof Ci.nsIException) {
+ var {message:errorMessage, filename:sourceName, lineNumber, columnNumber} = aError;
+ }
+ // generic script error
+ else if (typeof aError.message !== "undefined") {
+ // do not use aError.message, as this might hide the Error prefix
+ // such as "SyntaxError: ..."
+ errorMessage = aError.toString() || errorMessage;
+
+ lineNumber = aError.lineNumber || lineNumber;
+ columnNumber = aError.columnNumber || columnNumber;
+
+ // We don't want that injected "scriptish/service.js -> realfile" stuff
+ // Currently, moz will do this for us :p
+ if (!/->/.test(aError.fileName || "->")) {
+ sourceName = aError.fileName;
+ }
+ }
+ else {
+ errorMessage = aError.toString();
+ }
+
+ // if we haven't a sourceName yet, then derive it from aFileURL
+ if (!sourceName) {
+ aFileURL = aFileURL || "[user.js]";
+ if (aFileURL instanceof Ci.nsIURI) {
+ sourceName = aFileURL.spec;
+ }
+ else {
+ sourceName = aFileURL.toString();
+ }
+ }
+
+ let stack = aError.stack || aError.location || aError;
+ if (!stack || !(stack instanceof Ci.nsIStackFrame)) {
+ stack = Components.stack;
+ if (stack) {
+ // Unwind one
+ stack = stack.caller;
+ }
+ }
+ let stackLimit = 0;
+ while (stack && (stackLimit++ < 50)) {
+ errorMessage += "\n" + stackLimit + "> " + stack;
+ stack = stack.caller;
+ }
+
+ // dispatch
+ se.initWithWindowID(
+ "[" + (aId || "Scriptish") + "] " + errorMessage,
+ sourceName,
+ sourceLine,
+ lineNumber,
+ columnNumber,
+ flags,
+ category,
+ windowId
+ );
+ Services.console.logMessage(se);
+ }
+ catch (ex) {
+ // You never know :p
+ Scriptish_logError(aError, false, aFileURL, aError.lineNumber);
+ }
+}
+
function Scriptish_log(aMsg, aForce) {
- if (!aForce && !Scriptish_prefRoot.getValue("logChrome", false)) return;
+ if (!aForce && !Scriptish_prefRoot.getBoolValue("logChrome")) return;
// make sure msg is a string, and remove NULL bytes which truncate it
- Services.console.logStringMessage((aMsg + '').replace("\0","","g"));
+ Services.console.logStringMessage(("[Scriptish] " + aMsg).replace("\0","","g"));
}
diff --git a/extension/modules/manager.js b/extension/modules/manager.js
new file mode 100644
index 00000000..4eb9359c
--- /dev/null
+++ b/extension/modules/manager.js
@@ -0,0 +1,189 @@
+var EXPORTED_SYMBOLS = ["Scriptish_manager"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://scriptish/config.js", ["Scriptish_config"]);
+
+lazyUtil(this, "injectScripts");
+lazyUtil(this, "isGreasemonkeyable");
+lazyUtil(this, "isScriptRunnable");
+lazyUtil(this, "isURLExcluded");
+lazyUtil(this, "updateModifiedScripts");
+lazyUtil(this, "windowEventTracker");
+lazyUtil(this, "windowUnloader");
+
+const { Class } = jetpack('sdk/core/heritage');
+const { on } = jetpack('sdk/system/events');
+const { getInnerId } = jetpack('sdk/window/utils');
+
+jetpack('scriptish/security/api-check-filenames').add(Components.stack.filename);
+
+const windowsTracked = Object.create(null);
+
+const Scriptish_manager = Class({
+ initialize: function() {
+ const self = this;
+
+ on("document-element-inserted", function(aEvent) {
+ let win = aEvent.subject.defaultView;
+ if (!win) return;
+ self.docReady_start.call(self, {subject: win});
+ }, true);
+
+ on("content-document-global-created", self.docReady_start.bind(self), true);
+ on("chrome-document-global-created", self.docReady_start.bind(self), true);
+ },
+
+ docReady_start: function({subject: safeWin}) {
+ // TODO: disable observer that calls this when Scriptish is disabled
+ if (!Scriptish.enabled)
+ return;
+
+ if (!('document' in safeWin && "documentElement" in safeWin.document)) {
+ return;
+ }
+
+ let id = getInnerId(safeWin);
+ if (windowsTracked[id]) {
+ return;
+ }
+ windowsTracked[id] = true;
+
+ // start tracking the window's progress
+ Scriptish_windowEventTracker(safeWin);
+
+ // try to get the windows href..
+ let href = getURLForWin(safeWin);
+
+ if (!Scriptish_isGreasemonkeyable(href))
+ return;
+
+ // if the url is a excluded url then stop
+ if (Scriptish_isURLExcluded(href))
+ return;
+
+ this.docReady(href, safeWin);
+ },
+
+ docReady: function(href, safeWin) {
+ let unsafeWin = safeWin.wrappedJSObject;
+ let id = getInnerId(safeWin);
+
+ let winClosed = false;
+ Scriptish_windowUnloader(function() {
+ winClosed = true;
+ delete windowsTracked[id];
+ }, id);
+
+ let tracker = Scriptish_windowEventTracker(safeWin);
+
+ // rechecks values that can change at any moment
+ function shouldNotRun() (
+ winClosed || !Scriptish.enabled || !Scriptish_isGreasemonkeyable(href));
+
+ // check if there are any modified scripts
+ Scriptish_updateModifiedScripts(href, safeWin, shouldNotRun);
+
+ // find matching scripts
+ Scriptish_config.initScripts(href, (safeWin === safeWin.top), function(scripts) {
+ let windowLoaded = ("load" == tracker);
+
+ if (windowLoaded || "DOMContentLoaded" == tracker) {
+ scripts["document-start"] = scripts["document-start"].concat(scripts["document-end"], scripts["document-idle"]);
+ scripts["document-end"] = scripts["document-idle"] = [];
+ }
+
+ if (windowLoaded || "readystate@complete" == tracker) {
+ scripts["document-start"] = scripts["document-start"].concat(scripts["document-complete"]);
+ scripts["document-complete"] = [];
+ }
+
+ if (windowLoaded) {
+ scripts["document-start"] = scripts["document-start"].concat(scripts["window-load"]);
+ scripts["window-load"] = [];
+ }
+
+ // inject @run-at document-start scripts
+ Scriptish_injectScripts({
+ scripts: scripts["document-start"],
+ url: href,
+ safeWin: safeWin
+ });
+
+ // handle @run-at document-end and document-idle
+ if (scripts["document-end"].length || scripts["document-idle"].length) {
+ safeWin.addEventListener("DOMContentLoaded", function listener() {
+ safeWin.removeEventListener("DOMContentLoaded", listener, true);
+ if (shouldNotRun())
+ return;
+
+ // inject @run-at document-end scripts
+ Scriptish_injectScripts({
+ scripts: scripts["document-end"],
+ url: href,
+ safeWin: safeWin
+ });
+
+ // handle @run-at document-idle
+ if (scripts["document-idle"].length) {
+ timeout(function() {
+ if (shouldNotRun())
+ return;
+
+ // inject @run-at document-idle scripts
+ Scriptish_injectScripts({
+ scripts: scripts["document-idle"],
+ url: href,
+ safeWin: safeWin
+ });
+ });
+ }
+ }, true);
+ }
+
+ // handle @run-at document-complete
+ if (scripts["document-complete"].length) {
+ safeWin.document.addEventListener("readystatechange", function listener() {
+ if ("complete" != safeWin.document.readyState)
+ return;
+ safeWin.document.removeEventListener("readystatechange", listener, true);
+ if (shouldNotRun())
+ return;
+
+ // inject @run-at document-complete scripts
+ Scriptish_injectScripts({
+ scripts: scripts["document-complete"],
+ url: href,
+ safeWin: safeWin
+ });
+ }, true);
+ }
+
+ // handle @run-at window-load
+ if (scripts["window-load"].length) {
+ safeWin.addEventListener("load", function listener() {
+ safeWin.removeEventListener("load", listener, true);
+ if (shouldNotRun())
+ return;
+
+ // inject @run-at window-load scripts
+ Scriptish_injectScripts({
+ scripts: scripts["window-load"],
+ url: href,
+ safeWin: safeWin
+ });
+ }, true);
+ }
+ });
+ }
+});
+
+function getURLForWin(safeWin) {
+ return (safeWin.location.href
+ || (safeWin.frameElement && safeWin.frameElement.src))
+ || "";
+}
diff --git a/extension/modules/menucommander.js b/extension/modules/menucommander.js
index eefbf498..16ee0b8e 100644
--- a/extension/modules/menucommander.js
+++ b/extension/modules/menucommander.js
@@ -2,7 +2,6 @@ var EXPORTED_SYMBOLS = ["Scriptish_MenuCommander"];
const Cu = Components.utils;
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/logging.js");
function Scriptish_MenuCommander(aDoc) {
this.doc = aDoc;
@@ -60,52 +59,81 @@ Scriptish_MenuCommander.prototype.registerMenuCommand =
return commandUUID;
}
-Scriptish_MenuCommander.prototype.unregisterMenuCommand = function(commandUUID) {
- var removedSomething = false;
+Scriptish_MenuCommander.prototype.modifyMenuCommand =
+ function(commandUUID, aAction) {
+ var found = false;
- // remove key
+ // modify key
var keys = this.keys;
for (var i = keys.length - 1; ~i; i--) {
if (commandUUID == keys[i].getAttribute("uuid")) {
- this.keyset.removeChild(keys[i]);
- keys.splice(i, 1);
- removedSomething = true;
+ switch (aAction) {
+ case "unregister":
+ this.keyset.removeChild(keys[i]);
+ keys.splice(i, 1);
+ break;
+ case "enable":
+ case "disable":
+ keys[i].setAttribute("disabled", aAction == "disable");
+ break;
+ default:
+ throw Error("Invalid menu command action");
+ }
+ found = true;
break;
}
}
- // remove tools menu menu item
+ // modify tools menu menu item
var toolsMenuItems = this.toolsMenuItems;
for (var i = toolsMenuItems.length - 1; ~i; i--) {
if (commandUUID == toolsMenuItems[i].getAttribute("uuid")) {
- this.toolsMenuPopup.removeChild(toolsMenuItems[i]);
- toolsMenuItems.splice(i, 1);
- removedSomething = true;
+ switch (aAction) {
+ case "unregister":
+ this.toolsMenuPopup.removeChild(toolsMenuItems[i]);
+ toolsMenuItems.splice(i, 1);
+ break;
+ case "enable":
+ case "disable":
+ toolsMenuItems[i].setAttribute("disabled", aAction == "disable");
+ break;
+ default:
+ throw Error("Invalid menu command action");
+ }
+ found = true;
break;
}
}
- // remove toolbar button menu item
+ // modify toolbar button menu item
var menuPopup = this.getTBMenu();
if (menuPopup) {
let tbMenuItems = this.tbMenuItems;
for (var i = tbMenuItems.length - 1; ~i; i--) {
if (commandUUID == tbMenuItems[i].getAttribute("uuid")) {
- try {menuPopup.removeChild(tbMenuItems[i]);}
- catch (e) {} // TB was added but not opened before the user changed pages
- tbMenuItems.splice(i, 1);
- removedSomething = true;
+ switch (aAction) {
+ case "unregister":
+ try {menuPopup.removeChild(tbMenuItems[i]);}
+ catch (e) {} // TB was added but not opened before the user changed pages
+ tbMenuItems.splice(i, 1);
+ break;
+ case "enable":
+ case "disable":
+ tbMenuItems[i].setAttribute("disabled", aAction == "disable");
+ break;
+ default:
+ throw Error("Invalid menu command action");
+ }
+ found = true;
break;
}
}
}
- return removedSomething;
+ return found;
}
Scriptish_MenuCommander.prototype.attach = function() {
- Scriptish_log("> Scriptish_MenuCommander.attach");
-
for (var i = 0; i < this.keys.length; i++)
this.keyset.appendChild(this.keys[i]);
@@ -123,8 +151,6 @@ Scriptish_MenuCommander.prototype.attach = function() {
}
Scriptish_MenuCommander.prototype.detach = function() {
- Scriptish_log("> Scriptish_MenuCommander.detach");
-
for (var i = 0; i < this.keys.length; i++)
this.keyset.removeChild(this.keys[i]);
@@ -150,8 +176,6 @@ Scriptish_MenuCommander.prototype.detach = function() {
Scriptish_MenuCommander.prototype.createMenuItem =
function(commandUUID, commandName, commandFunc, accessKey) {
- Scriptish_log("> Scriptish_MenuCommander.createMenuItem");
-
var menuItem = this.doc.createElement("menuitem");
menuItem._commandFunc = commandFunc;
menuItem.setAttribute("uuid", commandUUID);
@@ -209,3 +233,15 @@ Scriptish_MenuCommander.prototype.setDisabled = function(aStatus) {
setStatus(this.$("scriptish-tb-cmds"), aStatus);
setStatus(this.toolsMenu, aStatus);
}
+
+Scriptish_MenuCommander.prototype.destroy = function() {
+ this.doc = null;
+ this.$ = null;
+ this.keyset = null;
+ this.keys = [];
+ this.attached = null;
+ this.tbMenuItems = [];
+ this.toolsMenu = null;
+ this.toolsMenuPopup = null;
+ this.toolsMenuItems = [];
+};
diff --git a/extension/modules/page-mods/blocked-uso-scripts.js b/extension/modules/page-mods/blocked-uso-scripts.js
new file mode 100644
index 00000000..9dbd0575
--- /dev/null
+++ b/extension/modules/page-mods/blocked-uso-scripts.js
@@ -0,0 +1,19 @@
+'use strict';
+
+(function() {
+ let installDiv = document.getElementById('install_script');
+ if (!installDiv)
+ return;
+
+ let installLink = document.evaluate(".//a[@class='userjs']", installDiv, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+ if (!installLink)
+ return;
+ installLink.href = '#';
+ installLink.textContent = 'Blocked';
+ installLink.style.background = 'no-repeat scroll right -130px red';
+
+ let installHelp = document.evaluate(".//a[@class='help']", installDiv, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+ if (!installHelp)
+ return;
+ installHelp.parentNode.removeChild(installHelp);
+})();
diff --git a/extension/modules/prefmanager.js b/extension/modules/prefmanager.js
index 254eb1e4..cd0f606a 100644
--- a/extension/modules/prefmanager.js
+++ b/extension/modules/prefmanager.js
@@ -1,9 +1,10 @@
var EXPORTED_SYMBOLS = ["Scriptish_prefRoot", "Scriptish_PrefManager"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyUtil(this, "stringBundle");
const MIN_INT_32 = -0x80000000;
const MAX_INT_32 = 0x7FFFFFFF;
+const SYNC_PREFIX = "services.sync.prefs.sync.";
const Scriptish_prefRoot = new Scriptish_PrefManager();
/**
@@ -11,11 +12,12 @@ const Scriptish_prefRoot = new Scriptish_PrefManager();
* Construct an instance by passing the startPoint of a preferences subtree.
* "extensions.scriptish." prefix is assumed.
*/
-function Scriptish_PrefManager(startPoint) {
+function Scriptish_PrefManager(startPoint, syncMode) {
if (!startPoint) startPoint = "";
startPoint = "extensions.scriptish." + startPoint;
- var pref = Services.prefs.getBranch(startPoint);
+ var pref =
+ Services.prefs.getBranch((syncMode ? SYNC_PREFIX : "") + startPoint);
var observers = {};
@@ -48,7 +50,13 @@ function Scriptish_PrefManager(startPoint) {
return defaultValue != undefined ? defaultValue : null;
}
return null;
- }
+ };
+
+ this.getBoolValue = function(aName, aDefault) {
+ if (pref.PREF_BOOL == pref.getPrefType(aName))
+ return pref.getBoolPref(aName);
+ return aDefault || false;
+ };
/**
* sets the named preference to the specified value. values must be strings,
@@ -73,7 +81,7 @@ function Scriptish_PrefManager(startPoint) {
}
if (!goodType)
- throw new Error(Scriptish_stringBundle("error.pref.type"));
+ throw Error(Scriptish_stringBundle("error.pref.type"));
// underlying preferences object throws an exception if new pref has a
// different type than old one. i think we should not do this, so delete
@@ -95,11 +103,15 @@ function Scriptish_PrefManager(startPoint) {
pref.setIntPref(prefName, Math.floor(value));
break;
}
+ return value;
}
// deletes the named preference or subtree
this.remove = function(prefName) { pref.deleteBranch(prefName); }
+ // resets the preference to the default, or removes if there is none
+ this.reset = function(prefName) { pref.clearUserPref(prefName); }
+
// call a function whenever the named preference subtree changes
this.watch = function(prefName, watcher) {
// construct an observer
@@ -112,15 +124,30 @@ function Scriptish_PrefManager(startPoint) {
// store the observer in case we need to remove it later
observers[watcher] = observer;
- pref.QueryInterface(Ci.nsIPrefBranch2)
+ pref.QueryInterface(Ci.nsIPrefBranch)
.addObserver(prefName, observer, false);
}
// stop watching
this.unwatch = function(prefName, watcher) {
if (observers[watcher]) {
- pref.QueryInterface(Ci.nsIPrefBranch2)
+ pref.QueryInterface(Ci.nsIPrefBranch)
.removeObserver(prefName, observers[watcher]);
}
}
+
+ // Determine if the preference is currently set to be synchronized
+ this.isSynced = function(prefName) {
+ return this.getValue(prefName, false);
+ }
+
+ // Set a preference to be synchronized
+ this.sync = function(prefName) {
+ this.setValue(prefName, true);
+ }
+
+ // Stop a preference from being synchronized
+ this.unsync = function(prefName) {
+ this.setValue(prefName, false);
+ }
}
diff --git a/extension/modules/script/cachedresource.js b/extension/modules/script/cachedresource.js
new file mode 100644
index 00000000..d215c046
--- /dev/null
+++ b/extension/modules/script/cachedresource.js
@@ -0,0 +1,53 @@
+var EXPORTED_SYMBOLS = ["CachedResource"];
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyUtil(this, "getContents");
+
+var useCache = Scriptish_prefRoot.getValue("cache.enabled");
+Scriptish_prefRoot.watch("cache.enabled", function() {
+ useCache = Scriptish_prefRoot.getValue("cache.enabled");
+});
+
+function CachedResource() {}
+CachedResource.prototype = {
+ _textContent: null,
+ clearResourceCaches: function() {
+ this.clearCachedTextContent();
+ },
+ clearCachedTextContent: function() this._textContent = null,
+ get textContent() {
+ if (!useCache || !this._textContent) {
+ this.clearCachedTextContent();
+ let content = Scriptish_getContents(this._file);
+ return useCache ? (this._textContent = content) : content;
+ }
+
+ return this._textContent;
+ },
+ _getTextContent_callback: function(aCallback, content) {
+ if (useCache) {
+ this._textContent = content;
+ }
+ aCallback(content);
+ },
+ getTextContent: function(aCallback) {
+ if (!aCallback) {
+ return this.textContent;
+ }
+
+ if (!useCache || !this._textContent) {
+ Scriptish_getContents(
+ this._file,
+ null,
+ this._getTextContent_callback.bind(this, aCallback)
+ );
+ }
+ else {
+ aCallback(this._textContent);
+ }
+
+ // avoid not-all-paths-return warning
+ return null;
+ }
+};
diff --git a/extension/modules/script/script.js b/extension/modules/script/script.js
index c70a234a..34c57768 100644
--- a/extension/modules/script/script.js
+++ b/extension/modules/script/script.js
@@ -1,42 +1,63 @@
var EXPORTED_SYMBOLS = ["Script"];
const valueSplitter = /(\S+)(?:\s+([^\r\f\n]+))?/;
+const GRANT_REGEX = /(?:unsafeWindow|GM_[a-z]+)/gi;
const Cu = Components.utils;
Cu.import("resource://gre/modules/CertUtils.jsm");
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/prefmanager.js");
-Cu.import("resource://scriptish/logging.js");
-Cu.import("resource://scriptish/scriptish.js");
-Cu.import("resource://scriptish/utils/Scriptish_getUriFromFile.js");
-Cu.import("resource://scriptish/utils/Scriptish_getContents.js");
-Cu.import("resource://scriptish/utils/Scriptish_getTLDURL.js");
-Cu.import("resource://scriptish/utils/Scriptish_convert2RegExp.js");
-Cu.import("resource://scriptish/utils/Scriptish_notification.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
-Cu.import("resource://scriptish/script/scriptinstaller.js");
-Cu.import("resource://scriptish/script/scripticon.js");
-Cu.import("resource://scriptish/script/scriptrequire.js");
-Cu.import("resource://scriptish/script/scriptresource.js");
-Cu.import("resource://scriptish/third-party/MatchPattern.js");
-Cu.import("resource://scriptish/config/configdownloader.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log", "Scriptish_logError"]);
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://scriptish/script/cachedresource.js", ["CachedResource"]);
+lazyImport(this, "resource://scriptish/utils/PatternCollection.js", ["PatternCollection"]);
+lazyImport(this, "resource://scriptish/script/scriptinstaller.js", ["ScriptInstall"]);
+lazyImport(this, "resource://scriptish/script/scripticon.js", ["ScriptIcon"]);
+lazyImport(this, "resource://scriptish/script/scriptrequire.js", ["ScriptRequire"]);
+lazyImport(this, "resource://scriptish/script/scriptresource.js", ["ScriptResource"]);
+lazyImport(this, "resource://scriptish/script/scriptcss.js", ["ScriptCSS"]);
+lazyImport(this, "resource://scriptish/third-party/MatchPattern.js", ["MatchPattern"]);
+lazyImport(this, "resource://scriptish/config/configdownloader.js", ["Scriptish_configDownloader"]);
+lazyImport(this, "resource://gre/modules/AddonManager.jsm", ["AddonManager", "AddonManagerPrivate"]);
+
+lazyUtil(this, "isGreasemonkeyable");
+lazyUtil(this, "getUriFromFile");
+lazyUtil(this, "getContents");
+lazyUtil(this, "memoize");
+lazyUtil(this, "parser");
+lazyUtil(this, "stringBundle");
+
+const { defer } = jetpack('sdk/core/promise');
+
+const MAX_NAME_LENGTH = 60;
const metaRegExp = /\/\/[ \t]*(?:==\/?UserScript==|\@\S+(?:[ \t]+(?:[^\r\f\n]+))?)/g;
-const nonIdChars = /[^\w@\.\-_]+/g; // any char matched by this is not valid
+// any char matched by this is not valid
+const nonIdChars = /[^-_@\.\u0400-\u04FF\u3300-\u33FF\u4E00-\u9FFF\w]+/g;
+
const JSVersions = ['1.6', '1.7', '1.8', '1.8.1'];
const maxJSVer = JSVersions[2];
-const runAtValues = ["document-start", "document-end", "document-idle", "window-load"];
+const runAtValues = ["document-start", "document-end", "document-idle", "document-complete", "window-load"];
const defaultRunAt = runAtValues[1];
+const defaultAutoUpdateState = AddonManager.AUTOUPDATE_DISABLE;
const usoURLChk = /^https?:\/\/userscripts\.org\/scripts\/[^\d]+(\d+)/i;
+const RE_USERSCRIPT_HEADER_START = /\/\/[ \t]*==UserScript==/i;
+const RE_USERSCRIPT_HEADER_END = /\/\/[ \t]*==\/UserScript==/i;
+
+const BLOCKED_DOMAINS = {
+ 'usocheckup.dune.net': true,
+ 'secure.dune.net': true
+}
function noUpdateFound(aListener, aReason) {
- aListener.onNoUpdateAvailable(this);
+ if (aListener.onNoUpdateAvailable)
+ aListener.onNoUpdateAvailable(this);
if (aListener.onUpdateFinished)
aListener.onUpdateFinished(this, aReason || AddonManager.UPDATE_STATUS_NO_ERROR);
}
function updateFound(aListener, aReason) {
var AddonInstall = new ScriptInstall(this);
- this.updateAvailable = true;
+ this.updateAvailable = AddonInstall;
AddonManagerPrivate.callAddonListeners("onNewInstall", AddonInstall);
aListener.onUpdateAvailable(this, AddonInstall);
if (aListener.onUpdateFinished)
@@ -49,6 +70,8 @@ function Script(config) {
this._observers = [];
this._homepageURL = null;
+ this._contributionURL = null;
+ this._contributionAmount = null;
this._downloadURL = null;
this._updateURL = null;
this._tempFile = null; // Only for scripts not installed
@@ -62,7 +85,10 @@ function Script(config) {
this._namespace = "";
this._prefroot = null;
this._author = null;
+ this._applyBackgroundUpdates = defaultAutoUpdateState;
+ this._developers = [];
this._contributors = [];
+ this.grant = {};
this._description = null;
this._version = null;
this._icon = new ScriptIcon(this);
@@ -70,17 +96,16 @@ function Script(config) {
this._enabled = true;
this.needsUninstall = false;
this.domains = [];
- this._includes = [];
- this._excludes = [];
+ this._includes = new PatternCollection();
+ this._excludes = new PatternCollection();
this._matches = [];
- this._includeRegExps = [];
- this._excludeRegExps = [];
- this.user_includes = [];
- this.user_excludes = [];
+ this._user_includes = new PatternCollection();
+ this._user_excludes = new PatternCollection();
this._delay = null;
this.priority = 0;
this._requires = [];
this._resources = [];
+ this._css = [];
this._screenshots = [];
this._noframes = false;
this._dependFail = false
@@ -90,7 +115,7 @@ function Script(config) {
this._jsversion = null;
this["_run-at"] = null;
}
-Script.prototype = {
+Script.prototype = subclass(CachedResource.prototype, {
includesDisabled: false,
isCompatible: true,
blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
@@ -128,17 +153,27 @@ Script.prototype = {
},
appDisabled: false,
scope: AddonManager.SCOPE_PROFILE,
- applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
+ get applyBackgroundUpdates() this._applyBackgroundUpdates,
+ set applyBackgroundUpdates(aVal) {
+ this._applyBackgroundUpdates = aVal;
+ Scriptish.notify(null, "scriptish-script-prefs-change", {
+ saved: true
+ });
+ },
operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE,
get isActive() !this.userDisabled,
pendingOperations: AddonManager.PENDING_NONE,
type: "userscript",
- isUSOScript: function() (usoURLChk.test(this._downloadURL)
- || usoURLChk.test(this._homepageURL)
- || usoURLChk.test(this._updateURL)),
- /*averageRating: undefined,
- reviewCount: undefined,
- totalDownloads: undefined,*/
+ isUSOScript: function() {
+ try {
+ return usoURLChk.test(this._downloadURL)
+ || usoURLChk.test(this._homepageURL)
+ || usoURLChk.test(this._updateURL);
+ }
+ catch (e) {
+ return false;
+ }
+ },
get reviewURL() ((this.isUSOScript()) ? "http://userscripts.org/scripts/reviews/" + RegExp.$1 : ""),
get sourceURI () this._downloadURL && NetUtil.newURI(this._downloadURL),
get userDisabled() !this.enabled,
@@ -147,14 +182,13 @@ Script.prototype = {
val = !!val;
if (val === this.userDisabled) return val;
- AddonManagerPrivate.callAddonListeners(
- val ? "onEnabling" : "onDisabling", this, false);
+ var enabling = !val;
+ Scriptish.notify(this, "scriptish-script-edit-enabling", {enabling: enabling});
- this._enabled = !val;
- this._changed("edit-enabled", this._enabled);
-
- AddonManagerPrivate.callAddonListeners(
- val ? "onEnabled" : "onDisabled", this);
+ Scriptish.notify(this, "scriptish-script-edit-enabled", {
+ enabling: (this._enabled = enabling),
+ saved: true
+ });
return val;
},
@@ -170,27 +204,51 @@ Script.prototype = {
get updateDate () new Date(parseInt(this._modified)),
updateUSOData: function() {
- if (this.blocked || !this.isUSOScript()) return;
- var script = this;
- var scriptID = RegExp.$1;
- var metaURL = "http://userscripts.org/scripts/source/" + scriptID + ".meta.js";
- var req = Instances.xhr;
+ let { promise, resolve, reject } = defer();
+
+ if (this.blocked || !this.isUSOScript()) {
+ reject();
+ return promise;
+ }
+
+ const script = this;
+ const scriptID = RegExp.$1;
+ const metaURL = "https://userscripts.org/scripts/source/" + scriptID + ".meta.js";
+
+ let req = Instances.xhr;
req.overrideMimeType("text/plain");
req.open("GET", metaURL, true);
- req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // bypass cache
+
+ // bypass cache
+ req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+
+ // private channel
+ req.channel.QueryInterface(Components.interfaces.nsIPrivateBrowsingChannel).setPrivate(true);
+
req.onload = function() {
if (4 > req.readyState || (req.status != 200 && req.status != 0)
- || !req.responseText)
- return;
- var data = Script.header_parse(req.responseText);
+ || !req.responseText) {
+ return reject();
+ }
+
+ let data = Scriptish_parser(req.responseText);
if (!data["uso:rating"] || !data["uso:script"] || data["uso:script"][0] != scriptID
- || !data["uso:reviews"] || !data["uso:installs"])
- return;
+ || !data["uso:reviews"] || !data["uso:installs"]) {
+ return reject();
+ }
+
script.reviewCount = data["uso:reviews"][0] * 1;
script.averageRating = data["uso:rating"][0] * 1;
script.totalDownloads = data["uso:installs"][0] * 1;
+
+ Scriptish.notify(null, "scriptish-config-saved", null);
+
+ return resolve(req.responseText);
}
+ req.onerror = function() reject();
req.send(null);
+
+ return promise;
},
findUpdates: function(aListener, aReason) {
@@ -219,7 +277,7 @@ Script.prototype = {
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // bypass cache
// suppress "bad certificate" dialogs and fail on redirects from a bad certificate.
req.channel.notificationCallbacks =
- new BadCertHandler(!this._config.updateSecurely || !Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
+ new BadCertHandler(!Scriptish.updateSecurely || !Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
req.onload = this.checkRemoteVersion.bind(this, req, aCallback);
req.onerror = this.checkRemoteVersionErr.bind(this, aCallback);
req.send(null);
@@ -230,11 +288,11 @@ Script.prototype = {
return aCallback.call(this, false, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
// make sure that the final URI is a https url
- if (this._config.updateSecurely && "https" != req.channel.URI.scheme)
+ if (Scriptish.updateSecurely && "https" != req.channel.URI.scheme)
return aCallback.call(this, false, AddonManager.UPDATE_STATUS_SECURITY_ERROR);
// make sure that the final URI's certificate is valid
- if (this._config.updateSecurely) {
+ if (Scriptish.updateSecurely) {
try {
checkCert(req.channel, !Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
}
@@ -254,15 +312,15 @@ Script.prototype = {
aCallback.call(this, false, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR)),
uninstall: function() {
- AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
+ Scriptish.notify(this, "scriptish-script-uninstalling");
this.needsUninstall = true;
this.pendingOperations = AddonManager.PENDING_UNINSTALL;
- AddonManagerPrivate.callAddonListeners("onUninstalled", this);
+ Scriptish.notify(this, "scriptish-script-uninstalled");
},
uninstallProcess: function() {
this.removeSettings();
this.removeFiles();
- this._changed("uninstall", null);
+ Scriptish.notify(this, "scriptish-script-removed");
},
removeSettings: function() {
if (Scriptish_prefRoot.getValue("uninstallPreferences")) {
@@ -293,7 +351,9 @@ Script.prototype = {
try {
var host = NetUtil.newURI(aURL).host;
} catch (e) {
- return false;
+ // If true, we're allowing a scheme that doesn't have a host.
+ // i.e. "about:scriptish"
+ return Scriptish_isGreasemonkeyable(aURL);
}
var i = this.domains.length - 1;
@@ -302,41 +362,45 @@ Script.prototype = {
return false;
},
- matchesURL: function(aURL) {
- function testI(regExp) (
- (regExp.isTLD) ? regExp.test(Scriptish_getTLDURL(aURL)) : regExp.test(aURL));
- function testII(aMatchPattern) aMatchPattern.doMatch(aURL);
-
- // check if the doamin is ok
+ matchesURL: null,
+ _matchesURL_noincludes: function(aURL) {
+ // check if the domain is ok
if (!this.matchesDomain(aURL)) return false;
- // check if script @includes/@excludes are disabled
- if (this.includesDisabled)
- return this._user_includeRegExps.some(testI)
- && !this._user_excludeRegExps.some(testI);
-
- let includes = this._user_includeRegExps.concat(this._includeRegExps);
- let excludes = this._user_excludeRegExps.concat(this._excludeRegExps)
- .concat(Scriptish.config.excludeRegExps);
-
- return (includes.some(testI) || this._matches.some(testII))
- && !excludes.some(testI);
+ return this._user_includes.test(aURL)
+ && !this._user_excludes.test(aURL);
},
+ _matchesURL_includes: function(aURL) {
+ // check if the domain is ok
+ if (!this.matchesDomain(aURL)) return false;
- _changed: function(aEvt, aData, aDontChg) {
- this._config._changed(this, aEvt, aData, aDontChg);
+ return (this._all_includes.test(aURL)
+ || this._matches.some(function(m) m.doMatch(aURL)))
+ && !this._all_excludes.test(aURL);
},
+ _make_matchesURL: function() {
+ if (this.includesDisabled) {
+ this.matchesURL = this._matchesURL_noincludes.bind(this);
+ }
+ else {
+ this.matchesURL = this._matchesURL_includes.bind(this);
+ }
+ this.matchesURL = Scriptish_memoize(this.matchesURL, 100);
+ },
get id() {
- if (!this._id) this.id = this.name + "@" + this.namespace;
+ if (!this._id) {
+ this.id = this.name + "@" + this.namespace;
+ }
+
return this._id;
},
set id(aId) {
this._id = aId.replace(nonIdChars, ''); // remove unacceptable chars
},
- get name() this._name,
+ get name() this._name || Scriptish_stringBundle("untitledScript"),
get namespace() this._namespace,
- get prefroot() {
+ get prefroot() {
if (!this._prefroot) this._prefroot = ["scriptvals.", this.id, "."].join("");
return this._prefroot;
},
@@ -365,14 +429,25 @@ Script.prototype = {
this._creator = this._author;
}
},
+ get developers() {
+ var devs = this._developers;
+ if (!AddonManagerPrivate.AddonAuthor) return devs;
+ var ary = [];
+ for (var i = devs.length-1; ~i; i--)
+ ary.unshift(new AddonManagerPrivate.AddonAuthor(devs[i]));
+ return ary;
+ },
get contributors() {
- if (!AddonManagerPrivate.AddonAuthor) return this._contributors;
- var contributors = [];
- for (var i = this._contributors.length-1; i >= 0; i--) {
- contributors.unshift(
- new AddonManagerPrivate.AddonAuthor(this._contributors[i]));
- }
- return contributors;
+ var contribs = this._contributors;
+ if (!AddonManagerPrivate.AddonAuthor) return contribs;
+ var ary = [];
+ for (var i = contribs.length-1; ~i; i--)
+ ary.unshift(new AddonManagerPrivate.AddonAuthor(contribs[i]));
+ return ary;
+ },
+ addDeveloper: function(aVal) {
+ if (!aVal) return;
+ this._developers.push(aVal);
},
addContributor: function(aContributor) {
if (!aContributor) return;
@@ -380,7 +455,11 @@ Script.prototype = {
},
get description() this._description,
get version() this._version,
- get optionsURL() "chrome://scriptish/content/script-options.xul?id=" + this.id,
+ get optionsURL() {
+ if (this.enabled)
+ return "chrome://scriptish/content/script-options.xul?id=" + this.id;
+ return null;
+ },
get icon() this._icon,
set icon(aIcon) this._icon = aIcon,
get icon64() this._icon64,
@@ -400,43 +479,53 @@ Script.prototype = {
this._delay = ((val || val === 0) && val > 0) ? val : null;
},
- get includes() this._includes.concat(),
- get excludes() this._excludes.concat(),
- get user_includes() this._user_includes.concat(),
- getUserIncStr: function(type) this["_user_" + (type || "include") + "s"].join("\n"),
- get user_excludes() this._user_excludes.concat(),
+ get includes() this._includes.patterns,
+ get excludes() this._excludes.patterns,
+ get user_includes() this._user_includes.patterns,
+ get user_excludes() this._user_excludes.patterns,
+ getUserIncStr: function(type) this["_user_" + (type || "include") + "s"].patterns.join("\n"),
set user_includes(aPatterns) {
- this._user_includes = [];
- this._user_includeRegExps = [];
- this.addInclude(aPatterns, true)
+ this._user_includes.clear();
+ this.addInclude(aPatterns, true);
},
set user_excludes(aPatterns) {
- this._user_excludes = [];
- this._user_excludeRegExps = [];
+ this._user_excludes.clear();
this.addExclude(aPatterns, true)
},
get matches() this._matches.concat(),
- addInclude: function(aPattern, aUserVal) (
- this.addPattern(((aUserVal) ? "_user" : "") + "_include", aPattern)),
- addExclude: function(aPattern, aUserVal) (
- this.addPattern(((aUserVal) ? "_user" : "") + "_exclude", aPattern)),
- addPattern: function(aPrefix, aPattern) {
- if (!aPattern) return;
- var patterns = (typeof aPattern == "string") ? [aPattern] : aPattern;
- for (let [, pattern] in Iterator(patterns)) {
- this[aPrefix + "s"].push(pattern);
- this[aPrefix + "RegExps"].push(Scriptish_convert2RegExp(pattern));
+ addInclude: function(aPattern, aUserVal) {
+ this[aUserVal ? "_user_includes" : "_includes"].addPatterns(aPattern);
+ this.__all_includes = null;
+ },
+ addExclude: function(aPattern, aUserVal) {
+ this[aUserVal ? "_user_excludes" : "_excludes"].addPatterns(aPattern);
+ this.__all_excludes = null;
+ },
+ get _all_includes() {
+ if (!this.__all_includes) {
+ this.__all_includes = new PatternCollection();
+ this.__all_includes.addPatterns(this._includes.patterns);
+ this.__all_includes.addPatterns(this._user_includes.patterns);
}
+ return this.__all_includes;
+ },
+ get _all_excludes() {
+ if (!this.__all_excludes) {
+ this.__all_excludes = new PatternCollection();
+ this.__all_excludes.addPatterns(this._excludes.patterns);
+ this.__all_excludes.addPatterns(this._user_excludes.patterns);
+ }
+ return this.__all_excludes;
},
-
get requires() this._requires.concat(),
get resources() this._resources.concat(),
+ get css() this._css.concat(),
get noframes() this._noframes,
get jsversion() this._jsversion || maxJSVer,
get runAt() this["_run-at"] || defaultRunAt,
useDelayedInjectors: function() {
this.delayInjection = false;
- this.updateHelper();
+ Scriptish.notify(this, "scriptish-script-modified", {saved: true, reloadUI: true});
for (let [, injector] in Iterator(this.delayedInjectors)) injector(this);
this.delayedInjectors = [];
},
@@ -463,7 +552,12 @@ Script.prototype = {
}
return this._homepageURL = url;
},
+
+ get contributionURL() this._contributionURL,
+ get contributionAmount() this._contributionAmount,
+
supportURL: "",
+
get updateURL() {
if (!this.version) return null;
if (Scriptish_prefRoot.getValue("useDownloadURLForUpdateURL"))
@@ -472,14 +566,14 @@ Script.prototype = {
var url = (this._updateURL || "");
url = url.replace(/[\?#].*$/, "");
// valid updateURL?
- if (!url || !url.match(/^https?:\/\//) || !/\.(?:user|meta)\.js$/i.test(url))
+ if (!url || !/^https?:\/\//i.test(url) || !/\.(?:user|meta)\.js$/i.test(url))
return null;
// userscripts.org url?
- if (url.match(/^https?:\/\/userscripts\.org\/.*?\.(?:user|meta)\.js$/i)) {
- if (this._config.updateSecurely) url.replace(/^http:/i, "https:");
+ if (/^https?:\/\/userscripts\.org\/.*?\.(?:user|meta)\.js$/i.test(url)) {
+ if (Scriptish.updateSecurely) url = url.replace(/^http:/i, "https:");
return url.replace(/\.user\.js$/i, ".meta.js");
}
- return (!this._config.updateSecurely || /^https:/i.test(url)) ? url : null;
+ return (!Scriptish.updateSecurely || /^https:/i.test(url)) ? url : null;
},
get cleanUpdateURL() (this.updateURL+"").replace(/\.meta\.js$/i, ".user.js"),
get providesUpdatesSecurely() {
@@ -490,7 +584,7 @@ Script.prototype = {
} catch (e) {
return false;
}
- return !this._config.updateSecurely || "https" == uri.scheme;
+ return !Scriptish.updateSecurely || "https" == uri.scheme;
},
get _file() {
@@ -509,19 +603,16 @@ Script.prototype = {
},
get fileURL() Scriptish_getUriFromFile(this._file).spec,
- get textContent() Scriptish_getContents(this._file),
get size() {
- var size = this._file.fileSize;
- for each (var r in this._requires) size += r._file.fileSize;
- for each (var r in this._resources) size += r._file.fileSize;
- return size;
- },
-
- getScriptHeader: function(aKey) {
- // TODO: cache headers and clear cache when the script is modified..
- var headers = Script.header_parse(Scriptish_getContents(this._tempFile || this._file));
- return aKey ? headers[aKey] : headers;
+ if (!this._size) {
+ var size = this._file.fileSize;
+ for each (let r in this._requires) size += r._file.fileSize;
+ for each (let r in this._resources) size += r._file.fileSize;
+ for each (let r in this._css) size += r._file.fileSize;
+ this._size = size;
+ }
+ return this._size;
},
get screenshots() this._screenshots,
@@ -536,9 +627,25 @@ Script.prototype = {
name = name.substring(0, dotIndex);
}
- name = name.replace(/[^-_A-Z0-9@]+/gi, "");
+ name = name.replace(nonIdChars, "");
ext = ext.replace(/\s+/g, "_").replace(/[^-_A-Z0-9]+/gi, "");
+ // Limit long names to a reasonable length
+ if (name.length > MAX_NAME_LENGTH) {
+ // Try to preserve the namespace
+ var atIndex = name.lastIndexOf("@");
+ var beforeAt = name;
+ var tail = "";
+
+ // 37 == "@".length + approximate_USO_namespace.length
+ if (atIndex >= 0) {
+ beforeAt = name.substring(0, atIndex - 1);
+ tail = name.substring(atIndex).substr(0, 37);
+ }
+
+ name = beforeAt.substr(0, MAX_NAME_LENGTH - tail.length) + tail;
+ }
+
// If no Latin characters found - use default
if (!name) name = "user_script";
if (ext) name += "." + ext;
@@ -557,7 +664,8 @@ Script.prototype = {
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
this._filename = file.leafName;
- Scriptish_log("Moving script file from " + tempFile.path + " to " + file.path);
+ Scriptish_log(Scriptish_stringBundle("moving.script") + " "
+ + tempFile.path + " --> " + file.path);
file.remove(true);
tempFile.moveTo(file.parent, file.leafName);
@@ -566,13 +674,31 @@ Script.prototype = {
get urlToDownload() this._downloadURL,
setDownloadedFile: function(file) { this._tempFile = file; },
- get previewURL() Services.io.newFileURI(this._tempFile).spec,
-
isModified: function() {
- if (!this.fileExists()) return false;
- if (this._modified != this._file.lastModifiedTime) {
- this._modified = this._file.lastModifiedTime;
- return true;
+ let now = Date.now();
+ if (now - this._isModified_lastcheck < 1000) {
+ // prevent thrashing by stat requests
+ // it can be safely assumed, that a user does not usually change a script
+ // and reload a website more often than once in a second
+ return false;
+ }
+ this._isModified_lastcheck = now;
+
+ try {
+ let lmt = this._file.lastModifiedTime;
+ if (this._modified != lmt) {
+ this._modified = lmt;
+
+ // drop the precomputed size
+ this._size = 0;
+
+ return true;
+ }
+ }
+ catch (ex) {
+ if (!this.fileExists()) {
+ this.uninstall();
+ }
}
return false;
},
@@ -580,23 +706,25 @@ Script.prototype = {
fileExists: function() {
try {
return this._file.exists();
- } catch (e) {
+ }
+ catch (e) {
return false;
}
},
+ update: function() {
+ this.clearResourceCaches();
+ this._make_matchesURL();
+ },
+
replaceScriptWith: function(aNewScript) {
this.removeFiles();
this.updateFromNewScript(aNewScript.installProcess());
-
- // notification that update is complete
- var msg = "'" + this.name;
- if (this.version) msg += " " + this.version;
- msg += "' " + Scriptish_stringBundle("statusbar.updated");
- Scriptish_notification(msg, null, null, function() Scriptish.openManager());
- this.updateHelper();
- this._changed("update");
+ Scriptish.notify(
+ this, "scriptish-script-updated", {saved: true, reloadUI: true});
},
+
+ // Called directly when a local script is modified, and at the end of a upgrade
updateFromNewScript: function(newScript, scriptInjector) {
var tools = {};
Cu.import("resource://scriptish/utils/Scriptish_cryptoHash.js", tools);
@@ -605,41 +733,47 @@ Script.prototype = {
// Copy new values.
this.blocked = newScript.blocked;
- this.updateAvailable = false;
this.domains = newScript.domains;
+ this.grant = newScript.grant || Object.create(null);
this._includes = newScript._includes;
this._excludes = newScript._excludes;
- this._includeRegExps = newScript._includeRegExps;
- this._excludeRegExps = newScript._excludeRegExps;
+ delete this.__all_includes;
+ delete this.__all_excludes;
this._matches = newScript._matches;
this._delay = newScript._delay;
this.priority = newPriority;
this._screenshots = newScript._screenshots;
this._homepageURL = newScript.homepageURL;
- this._updateURL = newScript._updateURL;
+ this._downloadURL = newScript._downloadURL;
+ this._contributionURL = newScript.contributionURL;
+ this._contributionAmount = newScript.contributionAmount;
this.supportURL = newScript.supportURL;
+ this._updateURL = newScript._updateURL;
this._name = newScript._name;
this._namespace = newScript._namespace;
this.author = newScript._author;
+ this._developers = newScript._developers;
this._contributors = newScript._contributors;
this._description = newScript._description;
this._jsversion = newScript._jsversion;
- this["_run-at"] = newScript.runAt;
+ this["_run-at"] = newScript["_run-at"];
this._noframes = newScript._noframes;
this._version = newScript._version;
if (!scriptInjector) {
- this._file = newScript._file;
this._basedir = newScript._basedir;
this._filename = newScript._filename;
this._icon = newScript._icon;
this._icon64 = newScript._icon64;
this._requires = newScript._requires;
this._resources = newScript._resources;
+ this._css = newScript._css;
this._modified = newScript._modified;
this._dependhash = newScript._dependhash;
- if (newScript._downloadURL) this._downloadURL = newScript._downloadURL;
- } else {
+ if (newScript._downloadURL)
+ this._downloadURL = newScript._downloadURL;
+ }
+ else {
var dependhash = tools.Scriptish_cryptoHash(newScript._rawMeta);
if (dependhash != this._dependhash && !newScript._dependFail) {
this._dependhash = dependhash;
@@ -647,6 +781,7 @@ Script.prototype = {
this._icon64 = newScript._icon64;
this._requires = newScript._requires;
this._resources = newScript._resources;
+ this._css = newScript._css;
// Get rid of old dependencies.
var dirFiles = this._basedirFile.directoryEntries;
@@ -664,143 +799,71 @@ Script.prototype = {
// Redownload dependencies.
tools.Scriptish_configDownloader.refetchDependencies(this);
}
- this.modificationProcess();
- }
- if (oldPriority != newPriority) this._config.sortScripts();
- },
-
- createXMLNode: function(doc) {
- var scriptNode = doc.createElement("Script");
- var len;
-
- for (var j = 0; j < this.contributors.length; j++) {
- var contributorNode = doc.createElement("Contributor");
- contributorNode.appendChild(doc.createTextNode(this.contributors[j]));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(contributorNode);
- }
-
- for (var j = 0; j < this.domains.length; j++) {
- var node = doc.createElement("Domain");
- node.appendChild(doc.createTextNode(this.domains[j]));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(node);
- }
-
- for (var j = 0; j < this._includes.length; j++) {
- var includeNode = doc.createElement("Include");
- includeNode.appendChild(doc.createTextNode(this._includes[j]));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(includeNode);
- }
-
- for (var j = 0; j < this._excludes.length; j++) {
- var excludeNode = doc.createElement("Exclude");
- excludeNode.appendChild(doc.createTextNode(this._excludes[j]));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(excludeNode);
- }
-
- for (var j = 0; j < this._matches.length; j++) {
- var matchNode = doc.createElement("Match");
- matchNode.appendChild(doc.createTextNode(this._matches[j].pattern));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(matchNode);
- }
-
- for (let [, include] in Iterator(this._user_includes)) {
- let node = doc.createElement("UserInclude");
- node.appendChild(doc.createTextNode(include));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(node);
- }
-
- for (let [, exclude] in Iterator(this._user_excludes)) {
- let node = doc.createElement("UserExclude");
- node.appendChild(doc.createTextNode(exclude));
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(node);
- }
-
- len = this._screenshots.length;
- for (var j = 0; j < len; j++) {
- var screenshotNode = doc.createElement("Screenshot");
- var screenshot = this._screenshots[j];
- screenshotNode.appendChild(doc.createTextNode(screenshot.url));
- if (screenshot.thumbnailURL)
- screenshotNode.setAttribute("thumb", screenshot.thumbnailURL);
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(screenshotNode);
- }
-
- for (var j = 0; j < this._requires.length; j++) {
- var req = this._requires[j];
- var resourceNode = doc.createElement("Require");
- resourceNode.setAttribute("filename", req._filename);
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(resourceNode);
- }
-
- for (var j = 0; j < this._resources.length; j++) {
- var imp = this._resources[j];
- var resourceNode = doc.createElement("Resource");
-
- resourceNode.setAttribute("name", imp._name);
- resourceNode.setAttribute("filename", imp._filename);
- resourceNode.setAttribute("mimetype", imp._mimetype);
- if (imp._charset) {
- resourceNode.setAttribute("charset", imp._charset);
+ else {
+ Scriptish.notify(this, "scriptish-script-modified", {saved: true, reloadUI: true});
}
-
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(resourceNode);
}
- if (this._noframes) {
- scriptNode.appendChild(doc.createTextNode("\n\t\t"));
- scriptNode.appendChild(doc.createElement("Noframes"));
- }
+ this.update();
- scriptNode.appendChild(doc.createTextNode("\n\t"));
-
- scriptNode.setAttribute("filename", this._filename);
- scriptNode.setAttribute("id", this.id);
- scriptNode.setAttribute("name", this.name);
- scriptNode.setAttribute("namespace", this.namespace);
- scriptNode.setAttribute("author", this._author);
- scriptNode.setAttribute("blocklistState", this.blocklistState);
- scriptNode.setAttribute("description", this._description);
- scriptNode.setAttribute("version", this._version);
- scriptNode.setAttribute("delay", this._delay);
- scriptNode.setAttribute("priority", this.priority);
- scriptNode.setAttribute("icon", this.icon.filename);
- scriptNode.setAttribute("icon64", this.icon64.filename);
- scriptNode.setAttribute("enabled", this._enabled);
- scriptNode.setAttribute("basedir", this._basedir);
- scriptNode.setAttribute("modified", this._modified);
- scriptNode.setAttribute("dependhash", this._dependhash);
- if (this._jsversion) scriptNode.setAttribute("jsversion", this._jsversion);
- if (this["_run-at"]) scriptNode.setAttribute("run-at", this["_run-at"]);
- if (this.includesDisabled) scriptNode.setAttribute("includesDisabled", true);
-
- if (this.homepageURL)
- scriptNode.setAttribute("homepageURL", this.homepageURL);
- if (this._downloadURL)
- scriptNode.setAttribute("downloadURL", this._downloadURL);
- if (this._updateURL)
- scriptNode.setAttribute("updateURL", this._updateURL);
- if (this.supportURL)
- scriptNode.setAttribute("supportURL", this.supportURL);
- if (this.averageRating)
- scriptNode.setAttribute("averageRating", this.averageRating);
- if (this.reviewCount)
- scriptNode.setAttribute("reviewCount", this.reviewCount);
- if (this.totalDownloads)
- scriptNode.setAttribute("totalDownloads", this.totalDownloads);
-
- return scriptNode;
+ if (oldPriority != newPriority) this._config.sortScripts();
},
+ toJSON: function() ({
+ domains: this.domains,
+ grant: this.grant,
+ includes: this._includes.patterns,
+ excludes: this._excludes.patterns,
+ matches: this._matches.map(function(match) match.pattern),
+ user_includes: this._user_includes.patterns,
+ user_excludes: this._user_excludes.patterns,
+ screenshots: this._screenshots.map(function(screenshot) ({
+ url: screenshot.url,
+ thumbnailURL: screenshot.thumbnailURL
+ })),
+ requires: this._requires.map(function(req) req._filename),
+ resources: this._resources.map(function(res) ({
+ name: res._name,
+ filename: res._filename,
+ mimetype: res._mimetype,
+ charset: res._charset
+ })),
+ css: this._css.map(function(req) req._filename),
+ noframes: this._noframes,
+ filename: this._filename,
+ id: this.id,
+ name: this.name,
+ namespace: this.namespace,
+ author: this._author,
+ developers: this._developers,
+ contributors: this._contributors,
+ blocklistState: this.blocklistState,
+ description: this._description,
+ version: this._version,
+ delay: this._delay,
+ priority: this.priority,
+ icon: this.icon.filename,
+ icon64: this.icon64.filename,
+ enabled: this._enabled,
+ basedir: this._basedir,
+ modified: this._modified,
+ dependhash: this._dependhash,
+ jsversion: this._jsversion,
+ "run-at": this["_run-at"],
+ includesDisabled: this.includesDisabled,
+ homepageURL: this.homepageURL,
+ contributionURL: this._contributionURL,
+ contributionAmount: this._contributionAmount,
+ downloadURL: this._downloadURL,
+ updateURL: this._updateURL,
+ supportURL: this.supportURL,
+ averageRating: this.averageRating,
+ reviewCount: this.reviewCount,
+ totalDownloads: this.totalDownloads,
+ applyBackgroundUpdates: this._applyBackgroundUpdates,
+ needsUninstall: this.needsUninstall
+ }),
+
// TODO: DRY
installProcess: function() {
this._initFile(this._tempFile);
@@ -809,105 +872,59 @@ Script.prototype = {
if (this.icon.hasDownloadURL()) this.icon._initFile();
if (this.icon64.hasDownloadURL()) this.icon64._initFile();
- for (var i = 0; i < this._requires.length; i++)
+ for (let i = 0, l = this._requires.length; i < l; i++)
this._requires[i]._initFile();
- for (var i = 0; i < this._resources.length; i++)
+ for (let i = 0, l = this._resources.length; i < l; i++)
this._resources[i]._initFile();
+ for (let i = 0, l = this._css.length; i < l; i++)
+ this._css[i]._initFile();
+
var tools = {};
Cu.import("resource://scriptish/utils/Scriptish_cryptoHash.js", tools);
- if (Services.pbs.privateBrowsingEnabled) this._downloadURL = null;
+ // set up _modified and stat thrashing stuff
+ this.isModified();
+
+ this.update();
- this._modified = this._file.lastModifiedTime;
this._dependhash = tools.Scriptish_cryptoHash(this._rawMeta);
return this;
- },
-
- updateHelper: function () {
- AddonManagerPrivate.callAddonListeners("onUninstalled", this);
- AddonManagerPrivate.callInstallListeners(
- "onExternalInstall", null, this, null, false);
- },
- modificationProcess: function(noReload) {
- // notification that modification is complete
- var msg = "'" + this.name;
- if (this.version) msg += " " + this.version;
- msg += "' " + Scriptish_stringBundle("statusbar.modified");
- Scriptish_notification(msg, null, null, function() Scriptish.openManager());
-
- if (!noReload) this.updateHelper();
- this._changed("modified", null, true);
- }
-};
-
-Script.parseVersion = function Script_parseVersion(aSrc) {
- var lines = aSrc.match(/\s*\/\/ [=@].*/g);
- if (!lines) return null;
- var lnIdx = 0;
- var result = {};
- var foundMeta = false;
- var start = "// ==UserScript==";
- var end = "// ==/UserScript==";
- var version = /\/\/ \@version\s+([^\s]+)/;
-
- while ((result = lines[lnIdx++])) {
- if (result.indexOf(start) != 0) continue;
- foundMeta = true;
- break;
- }
- if (!foundMeta) return;
- while ((result = lines[lnIdx++])) {
- if (result.indexOf(end) == 0) break;
- var match = result.match(version);
- if (match !== null) return match[1];
}
- return null;
-}
+});
-// TODO: DRY this by combining it with Script.parse some way..
-Script.header_parse = function(aSource) {
- var headers = {};
+Script.prototype.addScreenShot = function(aURL, aThumbURL) {
+ if (!AddonManagerPrivate.AddonScreenshot) return;
- // read one line at a time looking for start meta delimiter or EOF
- var lines = aSource.match(metaRegExp);
- var i = 0;
- var result;
- var foundMeta = false;
-
- // used for duplicate resource name detection
- var previousResourceNames = {};
-
- if (!lines) lines = [""];
- while (result = lines[i++]) {
- if (!foundMeta) {
- if (result.indexOf("// ==UserScript==") == 0) foundMeta = true;
- continue;
- }
+ var ss = new AddonManagerPrivate.AddonScreenshot(aURL);
- if (result.indexOf("// ==/UserScript==") == 0) {
- // done gathering up meta lines
- break;
- }
+ if (aThumbURL) {
+ ss.thumbnailURL = aThumbURL;
+ }
- var match = result.match(/\/\/ \@(\S+)(?:\s+([^\r\f\n]+))?/);
- if (match === null) continue;
- var header = match[1];
- var value = match[2];
+ this._screenshots.push(ss);
+ return;
+}
- if (!headers[header]) headers[header] = [value];
- else headers[header].push(value)
- }
- return headers;
+Script.parseVersion = function Script_parseVersion(aSrc) {
+ var parsed = Scriptish_parser(aSrc);
+ if (parsed.version) return parsed.version.pop();
+ return null;
}
-Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
- var script = new Script(aConfig);
+// TODO: DRY this by combining this with Scriptish_parser some way..
+Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript, aPrivate) {
+ const script = new Script(aConfig);
- if (aURI && !Services.pbs.privateBrowsingEnabled)
+ if (aURI && aPrivate === false)
script._downloadURL = aURI.spec;
+ // sniff @grant ?
+ if (Scriptish_prefRoot.getValue("enableScriptGrantSniffing")) {
+ (aSource.match(GRANT_REGEX) || []).forEach(function(i) script.grant[i] = true);
+ }
+
// read one line at a time looking for start meta delimiter or EOF
var lines = aSource.match(metaRegExp);
var i = 0;
@@ -921,11 +938,11 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
if (!lines) lines = [""];
while (result = lines[i++]) {
if (!foundMeta) {
- if (result.match(/\/\/[ \t]*==UserScript==/i)) foundMeta = true;
+ if (result.match(RE_USERSCRIPT_HEADER_START)) foundMeta = true;
continue;
}
- if (result.match(/\/\/[ \t]*==\/UserScript==/i))
+ if (result.match(RE_USERSCRIPT_HEADER_END))
break; // done gathering up meta lines
var match = result.match(/\/\/[ \t]*\@(\S+)(?:\s+([^\r\f\n]+))?/);
@@ -933,14 +950,18 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
var header = match[1].toLowerCase();
var value = match[2];
- if (!value) {
- switch (header) {
- case "noframes":
- script["_noframes"] = true;
- continue;
- }
- } else {
+ if (value)
value = value.trimRight();
+
+ // Keys with optional values
+ switch (header) {
+ case "noframes":
+ script["_noframes"] = (!value || "true" == value || "1" == value);
+ continue;
+ }
+
+ // Keys with required values
+ if (value) {
switch (header) {
case "id":
case "delay":
@@ -954,6 +975,10 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
script.author = value;
continue;
}
+ // nobreak
+ case "developer":
+ script.addDeveloper(value);
+ continue;
case "contributor":
script.addContributor(value);
continue;
@@ -979,17 +1004,37 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
case "website":
case "homepage":
case "homepageurl":
+ try {
+ let uri = NetUtil.newURI(value);
+ script._homepageURL = uri.spec;
+ }
+ catch (e) {}
+ break;
+ case "downloadurl":
+ try {
+ let uri = NetUtil.newURI(value);
+ script._downloadURL = uri.spec;
+ }
+ catch (e) {
+ Scriptish_logError(e)
+ }
+ break;
+ case "contributionurl":
try {
var uri = NetUtil.newURI(value);
} catch (e) {
break;
}
- script._homepageURL = uri.spec;
+ script._contributionURL = uri.spec;
+ break;
+ case "contributionamount":
+ script._contributionAmount = value;
break;
case "supporturl":
try {
var uri = NetUtil.newURI(value);
- } catch (e) {
+ }
+ catch (e) {
break;
}
script.supportURL = uri.spec;
@@ -997,25 +1042,31 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
case "jsversion":
let jsVerIndx = JSVersions.indexOf(value);
if (-1 === jsVerIndx) {
- throw new Error("@jsversion " + value + " " +
+ throw Error("@jsversion " + value + " " +
Scriptish_stringBundle("error.isInvalidValue"));
- } else if (jsVerIndx > JSVersions.indexOf(maxJSVer)) {
- throw new Error("@jsversion " + value + " " +
+ }
+ else if (jsVerIndx > JSVersions.indexOf(maxJSVer)) {
+ throw Error("@jsversion " + value + " " +
Scriptish_stringBundle("error.notSupported.Firefox"));
- } else {
+ }
+ else {
script._jsversion = JSVersions[jsVerIndx];
}
continue;
case "run-at":
let runAtIndx = runAtValues.indexOf(value);
if (0 > runAtIndx)
- throw new Error("@run-at " + value + " " +
+ throw Error("@run-at " + value + " " +
Scriptish_stringBundle("error.isInvalidValue"));
script["_run-at"] = runAtValues[runAtIndx];
continue;
case "domain":
script.domains.push(value);
continue;
+ case "grant":
+ var splitValue = value.split(/[ \t]+/);
+ splitValue.forEach(i => script.grant[i] = true);
+ continue;
case "include":
script.addInclude(value);
continue;
@@ -1025,15 +1076,16 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
case "match":
script._matches.push(new MatchPattern(value));
continue;
- case 'screenshot':
- if (!AddonManagerPrivate.AddonScreenshot) continue;
+ case "screenshot":
var splitValue = value.match(valueSplitter);
+
+ // if there is a thumb url provided
if (splitValue) {
- script._screenshots.push(new AddonManagerPrivate.AddonScreenshot(
- splitValue[1], splitValue[2]));
- } else {
- script._screenshots.push(new AddonManagerPrivate.AddonScreenshot(
- value));
+ script.addScreenShot(splitValue[1], splitValue[2]);
+ }
+ // no thumb url is provided
+ else {
+ script.addScreenShot(value);
}
continue;
case "defaulticon":
@@ -1076,16 +1128,25 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
continue;
case "require":
try {
- var reqUri = NetUtil.newURI(value, null, aURI);
- var scriptRequire = new ScriptRequire(script);
+ let reqUri = NetUtil.newURI(value, null, aURI);
+
+ // if the uri domain is blocked, then ignore
+ if (BLOCKED_DOMAINS[reqUri.host]) {
+ continue;
+ }
+
+ let scriptRequire = new ScriptRequire(script);
+
scriptRequire._downloadURL = reqUri.spec;
script._requires.push(scriptRequire);
script._rawMeta += header + '\0' + value + '\0';
- } catch (e) {
+ }
+ catch (e) {
if (aUpdateScript) {
script._dependFail = true;
- } else {
- throw new Error(Scriptish_stringBundle("error.retrieving") +
+ }
+ else {
+ throw Error(Scriptish_stringBundle("error.retrieving") +
" @require: '" + value + "'");
}
}
@@ -1093,34 +1154,58 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
case "resource":
var res = value.match(valueSplitter);
if (res === null) {
- throw new Error(Scriptish_stringBundle("error.resource.syntax") +
+ throw Error(Scriptish_stringBundle("error.resource.syntax") +
": '" + value + "'");
}
var resName = res[1];
if (previousResourceNames[resName]) {
- throw new Error("'" + resName + "' " +
+ throw Error("'" + resName + "' " +
Scriptish_stringBundle("error.resource.dupName"));
- } else {
+ }
+ else {
previousResourceNames[resName] = true;
}
+
try {
- var resUri = NetUtil.newURI(res[2], null, aURI);
- var scriptResource = new ScriptResource(script);
+ let resUri = NetUtil.newURI(res[2], null, aURI);
+ let scriptResource = new ScriptResource(script);
+
scriptResource._name = resName;
scriptResource._downloadURL = resUri.spec;
script._resources.push(scriptResource);
script._rawMeta +=
header + '\0' + resName + '\0' + resUri.spec + '\0';
- } catch (e) {
+ }
+ catch (e) {
if (aUpdateScript) {
script._dependFail = true;
- } else {
- throw new Error(
+ }
+ else {
+ throw Error(
Scriptish_stringBundle("error.retrieving") +
" @resource '" + resName + "' (" + res[2] + ")");
}
}
continue;
+ case "css":
+ try {
+ let reqUri = NetUtil.newURI(value, null, aURI);
+ let scriptCSS = new ScriptCSS(script);
+
+ scriptCSS._downloadURL = reqUri.spec;
+ script._css.push(scriptCSS);
+ script._rawMeta += header + '\0' + value + '\0';
+ }
+ catch (e) {
+ if (aUpdateScript) {
+ script._dependFail = true;
+ }
+ else {
+ throw Error(Scriptish_stringBundle("error.retrieving") +
+ " @css: '" + value + "'");
+ }
+ }
+ continue;
}
}
}
@@ -1140,121 +1225,98 @@ Script.parse = function Script_parse(aConfig, aSource, aURI, aUpdateScript) {
return script;
};
-Script.load = function load(aConfig, aNode) {
+Script.loadFromJSON = function(aConfig, aSkeleton) {
var script = new Script(aConfig);
- var fileModified = false;
- let tmp;
-
- script._filename = aNode.getAttribute("filename");
- script._basedir = aNode.getAttribute("basedir") || ".";
- script._downloadURL = aNode.getAttribute("downloadURL")
- || aNode.getAttribute("installurl") || null;
- script._updateURL = aNode.getAttribute("updateURL") || null;
- script._homepageURL = aNode.getAttribute("homepageURL")
- || aNode.getAttribute("homepage") || null;
- if (aNode.getAttribute("supportURL"))
- script.supportURL = aNode.getAttribute("supportURL");
- script._jsversion = aNode.getAttribute("jsversion") || null;
- script["_run-at"] = aNode.getAttribute("run-at") || null;
- tmp = (aNode.getAttribute("includesDisabled") || "").toLowerCase();
- if (tmp) script.includesDisabled = ("false" == tmp) ? false : true;
+
+ script._filename = aSkeleton.filename;
+ script._basedir = aSkeleton.basedir;
+ script._downloadURL = aSkeleton.downloadURL;
+ script._updateURL = aSkeleton.updateURL;
+ script._homepageURL = aSkeleton.homepageURL;
+ script._contributionURL = aSkeleton.contributionURL;
+ script._contributionAmount = aSkeleton.contributionAmount;
+ script.supportURL = aSkeleton.supportURL;
+ script._jsversion = aSkeleton.jsversion;
+ script["_run-at"] = aSkeleton["run-at"];
+ script.includesDisabled = aSkeleton.includesDisabled;
if (!script.fileExists()) {
script.uninstallProcess();
return true;
}
- if (!aNode.hasAttribute("modified")
- || !aNode.hasAttribute("dependhash")
- || !aNode.hasAttribute("version")) {
- var tools = {};
- Cu.import("resource://scriptish/utils/Scriptish_cryptoHash.js", tools);
+ script._modified = aSkeleton.modified;
+ script._dependhash = aSkeleton.dependhash;
+ script._version = aSkeleton.version;
+ script._noframes = aSkeleton.noframes;
+
+ script.domains = aSkeleton.domains;
+ script.grant = aSkeleton.grant || {};
+ if (Array.isArray(aSkeleton.developers))
+ aSkeleton.developers.forEach(script.addDeveloper.bind(script));
+ if (Array.isArray(aSkeleton.contributors))
+ aSkeleton.contributors.forEach(script.addContributor.bind(script));
+ script.addInclude(aSkeleton.includes);
+ script.addExclude(aSkeleton.excludes);
+ script.addInclude(aSkeleton.user_includes, true);
+ script.addExclude(aSkeleton.user_excludes, true);
+ if (Array.isArray(aSkeleton.matches))
+ aSkeleton.matches.forEach(function(i) script._matches.push(new MatchPattern(i)));
+
+ if (Array.isArray(aSkeleton.requires)) {
+ aSkeleton.requires.forEach(function(i) {
+ var scriptRequire = new ScriptRequire(script);
+ scriptRequire._filename = i;
+ script._requires.push(scriptRequire);
+ });
+ }
- script._modified = script._file.lastModifiedTime;
- var parsedScript = Script.parse(
- aConfig, Scriptish_getContents(script._file),
- script._downloadURL && NetUtil.newURI(script._downloadURL),
- script);
- script._dependhash = tools.Scriptish_cryptoHash(parsedScript._rawMeta);
- script._version = parsedScript._version;
- fileModified = true;
- } else {
- script._modified = aNode.getAttribute("modified");
- script._dependhash = aNode.getAttribute("dependhash");
- script._version = aNode.getAttribute("version");
+ if (Array.isArray(aSkeleton.resources)) {
+ aSkeleton.resources.forEach(function(i) {
+ var scriptResource = new ScriptResource(script);
+ scriptResource._name = i.name;
+ scriptResource._filename = i.filename;
+ scriptResource._mimetype = i.mimetype;
+ scriptResource._charset = i.charset;
+ script._resources.push(scriptResource);
+ });
}
- for (var i = 0, childNode; childNode = aNode.childNodes[i]; i++) {
- switch (childNode.nodeName) {
- case "Contributor":
- script.addContributor(childNode.firstChild.nodeValue.trim());
- break;
- case "Domain":
- script.domains.push(childNode.firstChild.nodeValue.trim());
- break;
- case "Include":
- script.addInclude(childNode.firstChild.nodeValue.trim());
- break;
- case "Exclude":
- script.addExclude(childNode.firstChild.nodeValue.trim());
- break;
- case "Match":
- script._matches.push(new MatchPattern(childNode.firstChild.nodeValue.trim()));
- break;
- case "UserInclude":
- script.addInclude(childNode.firstChild.nodeValue.trim(), true);
- break;
- case "UserExclude":
- script.addExclude(childNode.firstChild.nodeValue.trim(), true);
- break;
- case "Require":
- var scriptRequire = new ScriptRequire(script);
- scriptRequire._filename = childNode.getAttribute("filename");
- script._requires.push(scriptRequire);
- break;
- case "Resource":
- var scriptResource = new ScriptResource(script);
- scriptResource._name = childNode.getAttribute("name");
- scriptResource._filename = childNode.getAttribute("filename");
- scriptResource._mimetype = childNode.getAttribute("mimetype");
- scriptResource._charset = childNode.getAttribute("charset");
- script._resources.push(scriptResource);
- break;
- case "Screenshot":
- var thumb = "";
- if (childNode.hasAttribute("thumb"))
- thumb = childNode.getAttribute("thumb");
- script._screenshots.push(new AddonManagerPrivate.AddonScreenshot(
- childNode.firstChild.nodeValue.trim(), thumb));
- break;
- case "Noframes":
- script["_" + childNode.nodeName.toLowerCase()] = true;
- break;
- }
+ if (Array.isArray(aSkeleton.css)) {
+ aSkeleton.css.forEach(function(i) {
+ let scriptCSS = new ScriptCSS(script);
+ scriptCSS._filename = i;
+ script._css.push(scriptCSS);
+ });
+ }
+
+ if (Array.isArray(aSkeleton.screenshots)) {
+ aSkeleton.screenshots.forEach(function(i) {
+ script.addScreenShot(i.url, i.thumbnailURL);
+ });
}
- script._id = aNode.getAttribute("id") || null;
- script._name = aNode.getAttribute("name");
- script._namespace = aNode.getAttribute("namespace");
- script.author = aNode.getAttribute("author");
- script._description = aNode.getAttribute("description");
- script.icon.fileURL = aNode.getAttribute("icon");
- script.icon64.fileURL = aNode.getAttribute("icon64");
- let blocklistState = parseInt(aNode.getAttribute("blocklistState"), 10);
- if (blocklistState) script.blocklistState = blocklistState;
- script._enabled = aNode.getAttribute("enabled") == true.toString();
- script.delay = aNode.getAttribute("delay");
- script.priority = parseInt(aNode.getAttribute("priority"), 10) || 0;
- if (aNode.getAttribute("averageRating"))
- script.averageRating = aNode.getAttribute("averageRating") * 1;
- if (aNode.getAttribute("reviewCount"))
- script.reviewCount = aNode.getAttribute("reviewCount") * 1;
- if (aNode.getAttribute("totalDownloads"))
- script.totalDownloads = aNode.getAttribute("totalDownloads") * 1;
+ script.id = aSkeleton.id;
+ script._name = aSkeleton.name;
+ script._namespace = aSkeleton.namespace;
+ script.author = aSkeleton.author;
+ script._description = aSkeleton.description;
+ script.icon.fileURL = aSkeleton.icon;
+ script.icon64.fileURL = aSkeleton.icon64;
+ script.blocklistState = aSkeleton.blocklistState;
+ script._enabled = aSkeleton.enabled;
+ script.delay = aSkeleton.delay;
+ script.priority = aSkeleton.priority;
+ script.averageRating = aSkeleton.averageRating;
+ script.reviewCount = aSkeleton.reviewCount;
+ script.totalDownloads = aSkeleton.totalDownloads;
+ script._applyBackgroundUpdates = aSkeleton.applyBackgroundUpdates;
+
+ script.update();
aConfig.addScript(script);
- return fileModified;
-};
+ return false;
+}
Script.parseScriptName = function(aURL) ((
/\/([^\/]+)\.user(?:-\d+)?\.js(?:[\?#].*)?$/.test(aURL || "")) ? RegExp.$1 : "")
diff --git a/extension/modules/script/scriptcss.js b/extension/modules/script/scriptcss.js
new file mode 100644
index 00000000..74132d08
--- /dev/null
+++ b/extension/modules/script/scriptcss.js
@@ -0,0 +1,8 @@
+var EXPORTED_SYMBOLS = ["ScriptCSS"];
+Components.utils.import("resource://scriptish/script/scriptdependency.js");
+
+function ScriptCSS() {
+ ScriptDependency.apply(this, arguments);
+}
+ScriptCSS.prototype = new ScriptDependency();
+ScriptCSS.prototype.constructor = ScriptCSS;
diff --git a/extension/modules/script/scriptdependency.js b/extension/modules/script/scriptdependency.js
index 8c6af92a..e696caa1 100644
--- a/extension/modules/script/scriptdependency.js
+++ b/extension/modules/script/scriptdependency.js
@@ -1,10 +1,12 @@
var EXPORTED_SYMBOLS = ["ScriptDependency"];
-(function(inc) {
-inc("resource://scriptish/constants.js");
-inc("resource://scriptish/logging.js");
-inc("resource://scriptish/utils/Scriptish_getUriFromFile.js");
-inc("resource://scriptish/utils/Scriptish_getContents.js");
-})(Components.utils.import)
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/script/cachedresource.js", ["CachedResource"]);
+
+lazyUtil(this, "getUriFromFile");
+lazyUtil(this, "getContents");
+lazyUtil(this, "stringBundle");
function ScriptDependency(aScript) {
this._script = aScript;
@@ -17,7 +19,7 @@ function ScriptDependency(aScript) {
this.updateScript = false;
}
-ScriptDependency.prototype = {
+ScriptDependency.prototype = subclass(CachedResource.prototype, {
get _file() {
var file = this._script._basedirFile;
file.append(this._filename);
@@ -25,7 +27,6 @@ ScriptDependency.prototype = {
},
get tempFile() this._tempFile,
get fileURL() Scriptish_getUriFromFile(this._file).spec,
- get textContent() Scriptish_getContents(this._file),
get downloadURL() this._downloadURL,
get downloadURLFilename() {
@@ -49,10 +50,11 @@ ScriptDependency.prototype = {
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
this._filename = file.leafName;
- Scriptish_log("Moving dependency file from " + this._tempFile.path + " to " + file.path);
+ Scriptish_log(Scriptish_stringBundle("moving.dependency") + " "
+ + this._tempFile.path + " --> " + file.path);
file.remove(true);
this._tempFile.moveTo(file.parent, file.leafName);
this._tempFile = null;
}
-}
+});
diff --git a/extension/modules/script/scriptdownloader.js b/extension/modules/script/scriptdownloader.js
index b6a30162..b90b704e 100644
--- a/extension/modules/script/scriptdownloader.js
+++ b/extension/modules/script/scriptdownloader.js
@@ -3,15 +3,20 @@ var EXPORTED_SYMBOLS = ["ScriptDownloader"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/CertUtils.jsm");
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/logging.js");
-Cu.import("resource://scriptish/prefmanager.js");
-Cu.import("resource://scriptish/scriptish.js");
-Cu.import("resource://scriptish/script/scripticon.js");
-Cu.import("resource://scriptish/utils/Scriptish_alert.js");
-Cu.import("resource://scriptish/utils/Scriptish_getWriteStream.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
-
-function ScriptDownloader(uri, contentWin) {
+
+lazyImport(this, "resource://scriptish/config.js", ["Scriptish_config"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log", "Scriptish_logError"]);
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://scriptish/script/script.js", ["Script"]);
+lazyImport(this, "resource://scriptish/script/scripticon.js", ["ScriptIcon"]);
+
+const { alert } = jetpack('scriptish/alert');
+lazyUtil(this, "getTempFile");
+lazyUtil(this, "getWriteStream");
+lazyUtil(this, "stringBundle");
+
+function ScriptDownloader(uri, aPrivate) {
this.uri_ = uri || null;
this.req_ = null;
this.script = null;
@@ -20,11 +25,11 @@ function ScriptDownloader(uri, contentWin) {
this.installOnCompletion_ = false;
this.tempFiles_ = [];
this.updateScript = false;
- this.contentWin = contentWin || null;
+ this._private = aPrivate;
}
ScriptDownloader.prototype.startInstall = function() {
this.type = "install";
- this.startDownload();
+ this.startDownload(true);
}
ScriptDownloader.prototype.startViewScript = function() {
this.type = "view";
@@ -32,28 +37,48 @@ ScriptDownloader.prototype.startViewScript = function() {
}
ScriptDownloader.prototype.startUpdateScript = function(aScriptInstaller) {
this.type = "update";
- this.secure = Scriptish.config.updateSecurely;
+ this.secure = Scriptish.updateSecurely;
this.scriptInstaller = aScriptInstaller;
this.startDownload();
return this;
}
-ScriptDownloader.prototype.startDownload = function() {
+ScriptDownloader.prototype.startDownload = function(bypassCache) {
Scriptish_log("Fetching Script");
let req = this.req_ = Instances.xhr;
req.overrideMimeType("text/plain");
req.open("GET", this.uri_.spec, true);
+
+ if (bypassCache && (req.channel instanceof Ci.nsIRequest)) {
+ req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ }
+
if (this.secure) {
// suppress "bad certificate" dialogs and fail on redirects from a bad certificate.
req.channel.notificationCallbacks =
new BadCertHandler(!Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
}
+
+ if (req.channel instanceof Ci.nsIHttpChannelInternal) {
+ req.channel.forceAllowThirdPartyCookie = true;
+ }
+
req.onerror = this.handleErr.bind(this);
req.onreadystatechange = this.chkContentTypeB4DL.bind(this);
req.onload = this.handleScriptDownloadComplete.bind(this);
req.send(null);
}
-ScriptDownloader.prototype.handleErr = function() {
- if (this.scriptInstaller) this.scriptInstaller.changed("DownloadFailed");
+ScriptDownloader.prototype.handleErr = function(aEvent, aMsg) {
+ let errMsg = Scriptish_stringBundle("error.script.loading") + ":\n"
+ + this.uri_.spec;
+ if (aEvent) {
+ errMsg += "\nHTTP " + aEvent.target.status;
+ } else if (aMsg) {
+ errMsg += "\n" + aMsg;
+ }
+ Scriptish_log(errMsg);
+ if (this.scriptInstaller) {
+ this.scriptInstaller.changed("DownloadFailed");
+ }
}
ScriptDownloader.prototype.chkContentTypeB4DL = function() {
if (this.req_.readyState != 2
@@ -61,44 +86,54 @@ ScriptDownloader.prototype.chkContentTypeB4DL = function() {
return;
// If there is a 'Content-Type' header and it contains 'text/html',
- // then do not install the file, and display it instead.
+ // then do not attempt to install the file
this.req_.abort();
- Services.scriptish.ignoreNextScript();
- if (this.contentWin) this.contentWin.location.href = this.uri_.spec;
}
ScriptDownloader.prototype.handleScriptDownloadComplete = function() {
+ Scriptish_log("Scriptish ScriptDownloader.handleScriptDownloadComplete");
let req = this.req_;
+
try {
// If loading from file, status might be zero on success
if (req.status != 200 && req.status != 0) {
- Scriptish_alert(Scriptish_stringBundle("error.script.loading") + ":\n" +
- req.status + ": " + req.statusText);
+ alert(Scriptish_stringBundle("error.script.loading") + ":\n"
+ + req.status + ": " + req.statusText);
return;
}
if (this.secure) {
// make sure that the final URI is a https url
if ("https" != req.channel.URI.scheme)
- return this.handleErr();
+ return this.handleErr(null, Scriptish_stringBundle("error.notSecure"));
// make sure that the final URI's certificate is valid
try {
checkCert(req.channel, !Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
}
catch (e) {
- return this.handleErr();
+ return this.handleErr(null, Scriptish_stringBundle("error.invalidCert"));
+ }
+ }
+
+ if (this.scriptInstaller) {
+ // make sure that the new version is greater than the old version
+ var remoteVersion = Script.parseVersion(req.responseText);
+ if (!remoteVersion || Services.vc.compare(this.scriptInstaller._script.version, remoteVersion) >= 0) {
+ return this.handleErr(
+ null, Scriptish_stringBundle("error.remoteVersionOlder"));
}
}
var source = req.responseText;
- this.script = Scriptish.config.parse(source, this.uri_);
+ this.script = Scriptish_config.parse(source, this.uri_, undefined, this._private);
- var file = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
var base = this.script.name.replace(/[^A-Z0-9_]/gi, "").toLowerCase();
file.append(base + ".user.js");
- file.createUnique(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0640);
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0640);
this.tempFiles_.push(file);
+ // sync save file
var converter = Instances.suc;
converter.charset = "UTF-8";
source = converter.ConvertFromUnicode(source);
@@ -109,46 +144,59 @@ ScriptDownloader.prototype.handleScriptDownloadComplete = function() {
this.script.setDownloadedFile(file);
+ // start async download of dependencies
timeout(this.fetchDependencies.bind(this));
switch (this.type) {
+ // show install dialog
case "install":
this._callback = function() {
this.showInstallDialog();
delete this._callback;
}
break;
+ // show script in tab with install banner
case "view":
this.showScriptView();
break;
}
-
- } catch (e) {
- Scriptish_alert(Scriptish_stringBundle("error.script.installing") + ": " + e);
+ }
+ catch (e) {
+ alert(Scriptish_stringBundle("error.script.installing") + ": " + e);
throw e;
}
}
+
ScriptDownloader.prototype.fetchDependencies = function() {
Scriptish_log("Fetching Dependencies");
- var deps = this.script.requires.concat(this.script.resources);
+ let { script } = this;
+ Scriptish_log(script.requires.join(',,'));
+
+ const deps = script.requires.
+ concat(script.resources).
+ concat(script.css);
+
// if this.script.icon._filename exists then the icon is a data scheme
- if (this.script.icon.hasDownloadURL())
- deps.push(this.script.icon);
- if (this.script.icon64.hasDownloadURL())
- deps.push(this.script.icon64);
+ if (script.icon.hasDownloadURL())
+ deps.push(script.icon);
+
+ if (script.icon64.hasDownloadURL())
+ deps.push(script.icon64);
for (let [, dep] in Iterator(deps)) {
if (this.checkDependencyURL(dep.urlToDownload)) {
this.depQueue_.push(dep);
- } else {
+ }
+ else {
let errMsg = Scriptish_stringBundle("error.dependency.local");
if (dep instanceof ScriptIcon) {
dep.reset();
Scriptish_logError(new Error(
Scriptish_stringBundle("error.dependency.loading") + ": " +
dep.urlToDownload + "\n" + errMsg));
- } else {
+ }
+ else {
this.errorInstallDependency(dep, errMsg);
return;
}
@@ -157,16 +205,15 @@ ScriptDownloader.prototype.fetchDependencies = function() {
this.downloadNextDependency();
}
ScriptDownloader.prototype.downloadNextDependency = function() {
+ // When the last dependency has been fetched..
if (!this.depQueue_.length) {
this.dependenciesLoaded_ = true;
- this._callback && this._callback();
+ this._callback && this._callback(); // show install dialog
this.finishInstall();
return;
}
- var tools = {};
var dep = this.depQueue_.pop();
- Cu.import("resource://scriptish/utils/Scriptish_getTempFile.js", tools);
try {
var persist = Instances.wbp;
persist.persistFlags =
@@ -178,20 +225,24 @@ ScriptDownloader.prototype.downloadNextDependency = function() {
if (this.secure) {
// make sure that the dependency's URI is a https url
if ("https" != sourceUri.scheme)
- return this.errorInstallDependency(dep, "Insecure dependency URI");
+ return this.errorInstallDependency(
+ dep, Scriptish_stringBundle("error.notSecure"));
}
var sourceChannel = Services.io.newChannelFromURI(sourceUri);
+ if (sourceChannel instanceof Ci.nsIHttpChannelInternal) {
+ sourceChannel.forceAllowThirdPartyCookie = true;
+ }
sourceChannel.notificationCallbacks = (this.secure)
? new BadCertHandler(!Scriptish_prefRoot.getValue("update.requireBuiltInCerts"))
: new NotificationCallbacks();
- var file = tools.Scriptish_getTempFile();
+ var file = Scriptish_getTempFile();
this.tempFiles_.push(file);
var progressListener = new PersistProgressListener(persist);
progressListener.onFinish =
- this.handleDependencyDownloadComplete.bind(this, dep, file, sourceChannel);
+ this.handleDependencyDownloadComplete.bind(this, dep, file);
persist.progressListener = progressListener;
persist.saveChannel(sourceChannel, file);
} catch (e) {
@@ -210,22 +261,30 @@ ScriptDownloader.prototype.handleDependencyDownloadComplete =
if (this.secure) {
// make sure that the final URI is a https url
- if ("https" != channel.URI.scheme)
- return this.errorInstallDependency(dep, "Insecure dependency URI");
+ if ("https" != channel.URI.scheme) {
+ return this.errorInstallDependency(
+ dep, Scriptish_stringBundle("error.notSecure"));
+ }
// make sure that the final URI's certificate is valid
try {
checkCert(channel, !Scriptish_prefRoot.getValue("update.requireBuiltInCerts"));
}
catch (e) {
- return this.errorInstallDependency(dep, "Invalid dependency SSL certificate");
+ return this.errorInstallDependency(
+ dep, Scriptish_stringBundle("error.invalidCert"));
}
}
let errMsgStart = Scriptish_stringBundle("error.dependency.loading") + ": " +
dep.urlToDownload + "\n";
if (httpChannel) {
- if (httpChannel.requestSucceeded) {
+ try {
+ var reqSucceeded = httpChannel.requestSucceeded;
+ } catch(e) {
+ var reqSucceeded = false;
+ }
+ if (reqSucceeded) {
if (this.updateScript) {
dep._script = this.script;
dep.updateScript = true;
@@ -242,24 +301,35 @@ ScriptDownloader.prototype.handleDependencyDownloadComplete =
dep.setDownloadedFile(file, channel.contentType, channel.contentCharset ? channel.contentCharset : null);
this.downloadNextDependency();
- } else {
+ }
+ else {
+ try {
+ var responseStatus = httpChannel.responseStatus + ": "
+ + httpChannel.responseStatusText;
+ }
+ catch(e) {
+ var responseStatus = Scriptish_stringBundle("nothing.timedOut");
+ }
let errMsg = Scriptish_stringBundle("error.dependency.serverReturned") + ": "
- + httpChannel.responseStatus + ": " + httpChannel.responseStatusText;
+ + responseStatus;
if (dep instanceof ScriptIcon) {
file.remove(false);
dep.reset();
Scriptish_logError(new Error(errMsgStart + errMsg));
this.downloadNextDependency();
- } else {
+ }
+ else {
this.errorInstallDependency(dep, errMsg);
}
}
- } else {
+ }
+ else {
dep.setDownloadedFile(file);
this.downloadNextDependency();
}
}
+
ScriptDownloader.prototype.checkDependencyURL = function(url) {
var scheme = Services.io.extractScheme(url);
@@ -276,37 +346,63 @@ ScriptDownloader.prototype.checkDependencyURL = function(url) {
}
}
ScriptDownloader.prototype.finishInstall = function() {
+ // if this install was for a manual script update..
if (this.updateScript) {
// Inject the script now that we have the new dependencies
this.script.useDelayedInjectors();
-
- // Save new values to config.xml
- this.script._config._save();
- } else if (this.installOnCompletion_) {
+ }
+ // if the user has clicked a 'install'/'upgrade' button (not related to EM)
+ else if (this.installOnCompletion_) {
this.installScript();
- } else if (this.scriptInstaller) {
+ }
+ // note: this.scriptInstaller exists for EM script updates
+ else if (this.scriptInstaller) {
this.scriptInstaller.changed("DownloadEnded");
}
}
ScriptDownloader.prototype.errorInstallDependency = function(dep, msg) {
- this.dependencyError = Scriptish_stringBundle("error.dependency.loading") + ": "
- + dep.urlToDownload + "\n" + msg;
+ this.dependencyError = Scriptish_stringBundle("error.dependency.loading")
+ + ": " + dep.urlToDownload + "\n" + msg;
+
Scriptish_log(this.dependencyError);
- if (this.scriptInstaller) return this.scriptInstaller.changed("DownloadFailed");
- if (this.installOnCompletion_) Scriptish_alert(this.dependencyError);
- this._callback && this._callback();
+
+ // note: scriptInstaller exists for script updates that come thru EM
+ if (this.scriptInstaller) {
+ return this.scriptInstaller.changed("DownloadFailed");
+ }
+
+ // if the user has already clicked a nonEM 'install'/'upgrade' button
+ if (this.installOnCompletion_) {
+ alert(this.dependencyError);
+ }
+
+ this._callback && this._callback(); // show install dialog
}
+
+// initially called when a user is ready to install or upgrade,
+// and again after dependencies are download if there are any that still need
+// to be fetched.
ScriptDownloader.prototype.installScript = function() {
+ // Was there a dependency error? if so, then alert the stored error message.
if (this.dependencyError) {
- Scriptish_alert(this.dependencyError, 0);
+ // use timeout to avoid race condition that causes install window to fail
+ // to be closed..
+ alert(this.dependencyError, null, 100);
return false;
- } else if (this.scriptInstaller && this.dependenciesLoaded_) {
+ }
+
+ // note: script installers exist for script updates
+ if (this.scriptInstaller && this.dependenciesLoaded_) {
this.scriptInstaller._script.replaceScriptWith(this.script);
this.scriptInstaller.changed("InstallEnded");
- } else if (this.dependenciesLoaded_) {
+ }
+ // for a normal install do this..
+ else if (this.dependenciesLoaded_) {
var script = this.script;
- Scriptish.config.install(script);
- } else {
+ Scriptish_config.install(script);
+ }
+ // if the dependencies are still being downloaded then wait for that.
+ else {
this.installOnCompletion_ = true;
}
return true;
@@ -316,6 +412,8 @@ ScriptDownloader.prototype.cleanupTempFiles = function() {
file.exists() && file.remove(false);
}
ScriptDownloader.prototype.showInstallDialog = function(aTimer) {
+ let self = this;
+
if (!aTimer)
return timeout(this.showInstallDialog.bind(this, 1));
@@ -325,7 +423,7 @@ ScriptDownloader.prototype.showInstallDialog = function(aTimer) {
}
ScriptDownloader.prototype.showScriptView = function() {
Services.wm.getMostRecentWindow("navigator:browser")
- .Scriptish_BrowserUI.showScriptView(this, this.script.previewURL);
+ .Scriptish_BrowserUI.showScriptView(this, this.uri_.spec);
}
@@ -363,6 +461,6 @@ PersistProgressListener.prototype.onStateChange =
function(aWebProgress, aRequest, aStateFlags, aStatus) {
if (this.persist.currentState == this.persist.PERSIST_STATE_FINISHED) {
Scriptish_log("Persister: Download complete " + aRequest.status);
- this.onFinish();
+ this.onFinish(aRequest);
}
};
diff --git a/extension/modules/script/scripticon.js b/extension/modules/script/scripticon.js
index e65e7dcd..cb2bb28e 100644
--- a/extension/modules/script/scripticon.js
+++ b/extension/modules/script/scripticon.js
@@ -1,9 +1,8 @@
var EXPORTED_SYMBOLS = ["ScriptIcon"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/logging.js");
-Components.utils.import("resource://scriptish/script/scriptdependency.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_getUriFromFile.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyImport(this, "resource://scriptish/script/scriptdependency.js", ["ScriptDependency"]);
+lazyUtil(this, "getUriFromFile");
+lazyUtil(this, "stringBundle");
function ScriptIcon() {
ScriptDependency.apply(this, arguments);
@@ -35,13 +34,17 @@ ScriptIcon.prototype.__defineSetter__("fileURL", function(aURL) {
});
ScriptIcon.prototype.setIcon = function(aVal, aURI) {
// aceept data uri schemes for image MIME types
- if (/^data:image\//i.test(aVal)) return this._dataURI = aVal;
+ if (/^data:image\//i.test(aVal))
+ return this._dataURI = aVal;
+
if (/^data:/i.test(aVal))
- throw new Error(Scriptish_stringBundle("error.icon.dataURL"));
+ throw Error(Scriptish_stringBundle("error.icon.dataURL"));
+
try {
this._downloadURL = NetUtil.newURI(aVal, null, aURI).spec;
- } catch (e) {
- throw new Error(Scriptish_stringBundle("error.icon.URL"));
+ }
+ catch (e) {
+ throw Error(Scriptish_stringBundle("error.icon.URL"));
}
}
ScriptIcon.prototype.isImage = function(aMIMEType) /^image\//i.test(aMIMEType);
diff --git a/extension/modules/script/scriptinstaller.js b/extension/modules/script/scriptinstaller.js
index 9f8898bd..52c7d8c2 100644
--- a/extension/modules/script/scriptinstaller.js
+++ b/extension/modules/script/scriptinstaller.js
@@ -1,6 +1,7 @@
var EXPORTED_SYMBOLS = ["ScriptInstall"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/logging.js");
+lazyImport(this, "resource://scriptish/config/configdownloader.js", ["Scriptish_configDownloader"]);
+lazyImport(this, "resource://gre/modules/AddonManager.jsm", ["AddonManager", "AddonManagerPrivate"]);
// Implements https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonInstall
function ScriptInstall(aScript) {
@@ -19,29 +20,32 @@ ScriptInstall.prototype = {
timeout(function() {
self.changed("InstallStarted");
self.scriptDownloader.installScript();
- }, 0)
+ });
break;
case "DownloadCancelled":
this.state = AddonManager.STATE_CANCELLED;
break;
case "DownloadFailed":
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+ delete this._script.updateAvailable;
break;
case "InstallStarted":
this.state = AddonManager.STATE_INSTALLING;
break;
case "InstallEnded":
this.state = AddonManager.STATE_INSTALLED;
+ delete this._script.updateAvailable;
break;
default:
return;
}
- AddonManagerPrivate.callAddonListeners("on"+aType, this);
+ AddonManagerPrivate.callAddonListeners("on"+aType, this, this._script);
if (!this._listeners.length) return;
var listeners = this._listeners;
for (var i = 0, listener; listener = listeners[i]; i++) {
- if (listener["on"+aType])
- listener["on"+aType](this, ("InstallEnded" == aType && this._script));
+ if (listener["on"+aType]) {
+ listener["on"+aType](this, this._script);
+ }
}
},
get name() this._script.name,
@@ -50,7 +54,7 @@ ScriptInstall.prototype = {
get icon64URL() this._script.icon64URL,
releaseNotesURI: null,
type: "userscript",
- state: AddonManager.STATE_DOWNLOADING,
+ state: AddonManager.STATE_AVAILABLE,
error: null,
sourceURI: null,
file: null,
@@ -61,11 +65,9 @@ ScriptInstall.prototype = {
get existingAddon() this._script,
addon: null,
install: function() {
- var tools = {};
- Components.utils.import("resource://scriptish/config/configdownloader.js", tools);
this.changed("DownloadStarted");
this.scriptDownloader =
- tools.Scriptish_configDownloader.startUpdateScript(
+ Scriptish_configDownloader.startUpdateScript(
this._script.cleanUpdateURL, this);
},
cancel: function() {
diff --git a/extension/modules/script/scriptresource.js b/extension/modules/script/scriptresource.js
index 273450b6..8c3a7e79 100644
--- a/extension/modules/script/scriptresource.js
+++ b/extension/modules/script/scriptresource.js
@@ -1,7 +1,7 @@
var EXPORTED_SYMBOLS = ["ScriptResource"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_getBinaryContents.js");
-Components.utils.import("resource://scriptish/script/scriptdependency.js");
+lazyImport(this, "resource://scriptish/script/scriptdependency.js", ["ScriptDependency"]);
+lazyUtil(this, "getBinaryContents");
function ScriptResource() {
ScriptDependency.apply(this, arguments);
@@ -11,7 +11,6 @@ ScriptResource.prototype = new ScriptDependency();
ScriptResource.prototype.constructor = ScriptResource;
ScriptResource.prototype.__defineGetter__("name", function() this._name);
ScriptResource.prototype.__defineGetter__("dataContent", function() {
- var win = Services.ass.hiddenDOMWindow;
var binaryContents = Scriptish_getBinaryContents(this._file);
var mimetype = this._mimetype;
@@ -19,5 +18,5 @@ ScriptResource.prototype.__defineGetter__("dataContent", function() {
mimetype += ";charset=" + this._charset;
return "data:" + mimetype + ";base64," +
- win.encodeURIComponent(win.btoa(binaryContents));
+ encodeURIComponent(btoa(binaryContents));
});
diff --git a/extension/modules/script/simple-script.js b/extension/modules/script/simple-script.js
new file mode 100644
index 00000000..2ad106aa
--- /dev/null
+++ b/extension/modules/script/simple-script.js
@@ -0,0 +1,141 @@
+var EXPORTED_SYMBOLS = ["SimpleScript"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/utils/PatternCollection.js", ["PatternCollection"]);
+lazyImport(this, "resource://scriptish/third-party/MatchPattern.js", ["MatchPattern"]);
+
+lazyUtil(this, "memoize");
+lazyUtil(this, "isGreasemonkeyable");
+
+const JSVersions = ['1.6', '1.7', '1.8', '1.8.1'];
+const maxJSVer = JSVersions[2];
+const runAtValues = ["document-start", "document-end", "document-idle", "document-complete", "window-load"];
+const defaultRunAt = runAtValues[1];
+
+function SimpleScript() {
+ this.name = null;
+ this.namespace = "";
+ this.enabled = true;
+ this.domains = [];
+ this._includes = new PatternCollection();
+ this._excludes = new PatternCollection();
+ this._matches = [];
+ this._user_includes = new PatternCollection();
+ this._user_excludes = new PatternCollection();
+ this._delay = null;
+ this.priority = 0;
+ this._requires = [];
+ this._resources = [];
+ this.noframes = false;
+ this._jsversion = null;
+ this["_run-at"] = null;
+}
+
+SimpleScript.prototype = {
+ needsUninstall: false,
+ get jsversion() this._jsversion || maxJSVer,
+ get runAt() this["_run-at"] || defaultRunAt,
+ get matches() this._matches.concat(),
+ addInclude: function(aPattern, aUserVal) {
+ this[aUserVal ? "_user_includes" : "_includes"].addPatterns(aPattern);
+ this.__all_includes = null;
+ },
+ addExclude: function(aPattern, aUserVal) {
+ this[aUserVal ? "_user_excludes" : "_excludes"].addPatterns(aPattern);
+ this.__all_excludes = null;
+ },
+ get _all_includes() {
+ if (!this.__all_includes) {
+ this.__all_includes = new PatternCollection();
+ this.__all_includes.addPatterns(this._includes.patterns);
+ this.__all_includes.addPatterns(this._user_includes.patterns);
+ }
+ return this.__all_includes;
+ },
+ get _all_excludes() {
+ if (!this.__all_excludes) {
+ this.__all_excludes = new PatternCollection();
+ this.__all_excludes.addPatterns(this._excludes.patterns);
+ this.__all_excludes.addPatterns(this._user_excludes.patterns);
+ }
+ return this.__all_excludes;
+ },
+ matchesDomain: function(aURL) {
+ try {
+ var host = NetUtil.newURI(aURL).host;
+ } catch (e) {
+ // If true, we're allowing a scheme that doesn't have a host.
+ // i.e. "about:scriptish"
+ return Scriptish_isGreasemonkeyable(aURL);
+ }
+
+ var i = this.domains.length - 1;
+ if (!~i) return true; // when there are no @domains, then allow the host
+ for (; ~i; i--) if (host == this.domains[i]) return true;
+ return false;
+ }
+};
+
+SimpleScript.prototype._make_matchesURL = function() {
+ if (this.includesDisabled) {
+ this.matchesURL = this._matchesURL_noincludes.bind(this);
+ }
+ else {
+ this.matchesURL = this._matchesURL_includes.bind(this);
+ }
+ this.matchesURL = Scriptish_memoize(this.matchesURL, 100);
+};
+SimpleScript.prototype.matchesURL = null;
+SimpleScript.prototype._matchesURL_noincludes = function(aURL) {
+ // check if the domain is ok
+ if (!this.matchesDomain(aURL)) return false;
+
+ return this._user_includes.test(aURL)
+ && !this._user_excludes.test(aURL);
+};
+SimpleScript.prototype._matchesURL_includes = function(aURL) {
+ // check if the domain is ok
+ if (!this.matchesDomain(aURL)) return false;
+
+ return (this._all_includes.test(aURL)
+ || this._matches.some(function(m) m.doMatch(aURL)))
+ && !this._all_excludes.test(aURL);
+};
+
+SimpleScript.prototype.update = function() {
+ this._make_matchesURL();
+};
+
+
+SimpleScript.loadFromJSON = function(aSkeleton) {
+ var script = new SimpleScript();
+
+ script._jsversion = aSkeleton.jsversion;
+ script["_run-at"] = aSkeleton["run-at"];
+ script.includesDisabled = aSkeleton.includesDisabled;
+ script.noframes = aSkeleton.noframes;
+ script.addInclude(aSkeleton.includes);
+ script.addExclude(aSkeleton.excludes);
+ // TODO: need to have the below updated when they are changed...
+ script.addInclude(aSkeleton.user_includes, true);
+ script.addExclude(aSkeleton.user_excludes, true);
+ aSkeleton.matches.forEach(function(i) script._matches.push(new MatchPattern(i)));
+ aSkeleton.requires.forEach(function(i) {
+ script._requires.push(i);
+ });
+ aSkeleton.resources.forEach(function(i) {
+ script._resources.push(i);
+ });
+
+ script.id = aSkeleton.id;
+ script.name = aSkeleton.name;
+ script.namespace = aSkeleton.namespace;
+ script.enabled = aSkeleton.enabled;
+ script.delay = aSkeleton.delay;
+ script.priority = aSkeleton.priority;
+
+ script.update();
+ return script;
+};
diff --git a/extension/modules/scriptish.js b/extension/modules/scriptish.js
index 76118880..e1092046 100644
--- a/extension/modules/scriptish.js
+++ b/extension/modules/scriptish.js
@@ -1,41 +1,59 @@
var EXPORTED_SYMBOLS = ["Scriptish"];
-Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/prefmanager.js");
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/config.js", ["Scriptish_config"]);
+
+function setStatus() (enabled = Scriptish_prefRoot.getValue("enabled", true));
+function notifyStatusChg(aVal) (
+ Scriptish.notify(null, "scriptish-enabled", {enabling: aVal}));
+
+var ignoreEnable = false, enabled = setStatus();
+Scriptish_prefRoot.watch("enabled", function() {
+ if (ignoreEnable) return ignoreEnable = false;
+ notifyStatusChg(setStatus());
+});
+
+let enableInstallDetection = true;
+function setEnableInstallDetection() {
+ enableInstallDetection = Scriptish_prefRoot.getValue("enableInstallDetection", true);
+}
+setEnableInstallDetection();
+Scriptish_prefRoot.watch("enableInstallDetection", setEnableInstallDetection);
+
+var global = this;
+
const Scriptish = {
- get config() Services.scriptish.config,
- get enabled() Scriptish_prefRoot.getValue("enabled", true),
- set enabled(aVal) Scriptish_prefRoot.setValue("enabled", !!aVal),
- openManager: function Scriptish_openManager() {
- var browserWin = Services.wm.getMostRecentWindow("navigator:browser");
- if (browserWin.BrowserOpenAddonsMgr)
- browserWin.BrowserOpenAddonsMgr("addons://list/userscript");
- else if (browserWin.toEM)
- browserWin.toEM("addons://list/userscript");
- },
- isGreasemonkeyable: function Scriptish_isGreasemonkeyable(aURL) {
- // if the url provide is not a valid url, then an error could be thrown
- try {
- var scheme = Services.io.extractScheme(aURL);
- } catch (e) {
- return false;
+ updateSecurely: Scriptish_prefRoot.getBoolValue("update.requireSecured"),
+ notify: function(aSubject, aTopic, aData) {
+ if (true === aData) {
+ aData = {saved: true};
}
-
- switch (scheme) {
- case "http":
- case "https":
- case "ftp":
- case "data":
- return true;
- case "about":
- // Always allow "about:blank".
- if (/^about:blank/.test(aURL)) return true;
- // Conditionally allow the rest of "about:".
- return Scriptish_prefRoot.getValue('aboutIsGreaseable');
- case "file":
- return Scriptish_prefRoot.getValue('fileIsGreaseable');
- case "unmht":
- return Scriptish_prefRoot.getValue('unmhtIsGreaseable');
+ else if (null !== aData) {
+ if (!aData) {
+ aData = {saved: false};
+ }
+ else if (!aData.saved) {
+ aData.saved = false;
+ }
}
- return false;
- }
+ if (aData && aSubject) aData.id = aSubject.id;
+ Services.obs.notifyObservers(null, aTopic, JSON.stringify(aData));
+ },
+ get enableInstallDetection() enableInstallDetection,
+ get enabled() enabled,
+ set enabled(aVal) {
+ ignoreEnable = true;
+ enabled = !!aVal;
+ notifyStatusChg(enabled);
+ Scriptish_prefRoot.setValue("enabled", enabled);
+ },
+ getMostRecentWindow: function() Services.wm.getMostRecentWindow("navigator:browser"),
+ getWindows: function() Services.wm.getEnumerator("navigator:browser")
}
+
+// Watch for the required secure updates pref being modified
+Scriptish_prefRoot.watch("update.requireSecured", function() {
+ Scriptish.updateSecurely = Scriptish_prefRoot.getValue("update.requireSecured");
+});
diff --git a/extension/modules/setup-page-mods.js b/extension/modules/setup-page-mods.js
new file mode 100644
index 00000000..e69de29b
diff --git a/extension/modules/third-party/MatchPattern.js b/extension/modules/third-party/MatchPattern.js
old mode 100755
new mode 100644
index d79ba1bc..db0b6708
--- a/extension/modules/third-party/MatchPattern.js
+++ b/extension/modules/third-party/MatchPattern.js
@@ -21,6 +21,7 @@
* David Dahl
* Drew Willcoxon
* Erik Vold
+ * Nils Maier
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -42,61 +43,70 @@ Cu.import("resource://scriptish/constants.js");
Cu.import("resource://scriptish/utils/Scriptish_convert2RegExp.js");
Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
-const validScheme = ['http', 'https', 'ftp', 'file'];
+const validSchemes = ['http', 'https', 'ftp', 'file'];
+const REG_HOST = /^(?:\*\.)?[^*\/]+$|^\*$|^$/;
-// matches *. or * or text of host
-const validateHost = /^(\*|\*\.[^/*]+|[^/*]+)$/;
+function MatchPattern(pattern) {
+ this.pattern = pattern;
-const validatePath = /^(\*|\/.*)$/;
+ // special case "
+ if (pattern == "") {
+ this.all = true;
+ this.scheme = "all_urls";
+ return;
+ }
-// create and return a match pattern obj validate match pattern!
-function MatchPattern(pattern){
- this.pattern = pattern;
+ // special case wild scheme
+ if (pattern[0] == "*") {
+ this.wildScheme = true;
+ // use http, because we need to ensure we get a host
+ pattern = "http" + pattern.slice(1);
+ }
try {
var uri = NetUtil.newURI(pattern);
- } catch (e) {
- throw new Error(Scriptish_stringBundle("error.pattern.parsing") + ": " + e);
+ }
+ catch (e) {
+ throw Error(Scriptish_stringBundle("error.pattern.parsing") + ": " + e);
}
- var scheme = uri.scheme;
+ var scheme = this.wildScheme ? "all" : uri.scheme;
var host = uri.host;
var path = uri.path;
- if (scheme === 'file') {
- if (!validatePath.test(path)) {
- throw new Error(Scriptish_stringBundle("error.matchPattern.rules.file"));
- }
+ if (scheme != "all" && validSchemes.indexOf(scheme) == -1) {
+ throw Error(Scriptish_stringBundle("error.matchPattern.rules"));
}
- else {
- if (validScheme.indexOf(scheme) < 0 || !validateHost.test(host) ||
- !validatePath.test(path)) {
- throw new Error(Scriptish_stringBundle("error.matchPattern.rules"));
- }
-
- if (host == "*" && uri.path == "/") path = host;
+ if (!REG_HOST.test(host)) {
+ throw Error(Scriptish_stringBundle("error.matchPattern.rules"));
+ }
+ if (path[0] !== "/") {
+ throw Error(Scriptish_stringBundle("error.matchPattern.rules"));
}
- var regexs = {};
- regexs.scheme = scheme;
- regexs.host = (scheme !== 'file') ? Scriptish_convert2RegExp(host) : null;
- regexs.path = Scriptish_convert2RegExp(path, true);
-
- this.regexs = regexs;
- return this;
+ this.scheme = scheme;
+ if (host) {
+ this.hostExpr = Scriptish_convert2RegExp(host.replace(/^\*\./, "*"));
+ }
+ else {
+ // if omitted, then it means "", an alias for localhost
+ this.hostExpr = /^$/;
+ }
+ this.pathExpr = Scriptish_convert2RegExp(path, false, true);
}
MatchPattern.prototype.doMatch = function (uriSpec) {
var matchURI = NetUtil.newURI(uriSpec);
- var regexs = this.regexs;
- if (regexs.host === null){
- return (regexs.scheme === matchURI.scheme &&
- regexs.path.test(matchURI.path));
- } else {
- return (regexs.scheme === matchURI.scheme &&
- regexs.path.test(matchURI.path) &&
- regexs.host.test(matchURI.host));
+ if (validSchemes.indexOf(matchURI.scheme) == -1) {
+ return false;
+ }
+ if (this.all) {
+ return true;
+ }
+ if (!this.wildScheme && this.scheme != matchURI.scheme) {
+ return false;
}
+ return this.hostExpr.test(matchURI.host) && this.pathExpr.test(matchURI.path);
};
diff --git a/extension/modules/third-party/Scriptish_getBrowserForContentWindow.js b/extension/modules/third-party/Scriptish_getBrowserForContentWindow.js
new file mode 100644
index 00000000..7cb89d52
--- /dev/null
+++ b/extension/modules/third-party/Scriptish_getBrowserForContentWindow.js
@@ -0,0 +1,57 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is DevTools (HeadsUpDisplay) Console Code
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Dahl (original author)
+ * Rob Campbell
+ * Johnathan Nightingale
+ * Patrick Walton
+ * Julian Viereck
+ * Mihai é÷ucan
+ * Greg Parris
+ * Erik Vold
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var EXPORTED_SYMBOLS = ["Scriptish_getBrowserForContentWindow"];
+Components.utils.import("resource://scriptish/constants.js");
+
+function Scriptish_getBrowserForContentWindow(aContentWindow) {
+ return aContentWindow
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow)
+ .QueryInterface(Ci.nsIDOMChromeWindow);
+}
diff --git a/extension/modules/third-party/Scriptish_openFolder.js b/extension/modules/third-party/Scriptish_openFolder.js
deleted file mode 100644
index d11b848f..00000000
--- a/extension/modules/third-party/Scriptish_openFolder.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/**** BEGIN LICENSE BLOCK *****
-Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
-The contents of this file are subject to the Mozilla Public License Version
-1.1 (the "License"); you may not use this file except in compliance with
-the License. You may obtain a copy of the License at
-http://www.mozilla.org/MPL/
-
-Software distributed under the License is distributed on an "AS IS" basis,
-WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-for the specific language governing rights and limitations under the
-License.
-
-The Original Code is Mozilla.org Code.
-
-The Initial Developer of the Original Code is
-Netscape Communications Corporation.
-Portions created by the Initial Developer are Copyright (C) 2001
-the Initial Developer. All Rights Reserved.
-
-Contributor(s):
- Blake Ross (Original Author)
- Ben Goodger (v2.0)
- Dan Mosedale
- Fredrik Holmqvist
- Josh Aas
- Shawn Wilsher (v3.0)
- Edward Lee
-
- Anthony Lieuallen
- Mike Medley
- Erik Vold
-
-Alternatively, the contents of this file may be used under the terms of
-either the GNU General Public License Version 2 or later (the "GPL"), or
-the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-in which case the provisions of the GPL or the LGPL are applicable instead
-of those above. If you wish to allow use of your version of this file only
-under the terms of either the GPL or the LGPL, and not to allow others to
-use your version of this file under the terms of the MPL, indicate your
-decision by deleting the provisions above and replace them with the notice
-and other provisions required by the GPL or the LGPL. If you do not delete
-the provisions above, a recipient may use your version of this file under
-the terms of any one of the MPL, the GPL or the LGPL.
-
-***** END LICENSE BLOCK ****/
-var EXPORTED_SYMBOLS = ["Scriptish_openFolder"];
-Components.utils.import("resource://scriptish/constants.js");
-
-function Scriptish_openFolder(aFile) {
- try {
- // Show the directory containing the file and select the file.
- aFile.reveal();
- } catch (e) {
- // Either the file doesn't exist or reveal is not implemented
- var fParent = aFile.parent;
-
- try {
- // Lauch the parent directory if the file doesn't exist.
- if (fParent.exists()) fParent.launch();
- } catch (e) {
- // If launch also fails let the OS handler try to open the parent.
- Services.eps.loadUrl(Services.io.newFileURI(fParent));
- }
- }
-}
diff --git a/extension/modules/third-party/Timer.js b/extension/modules/third-party/Timer.js
deleted file mode 100644
index f45c534e..00000000
--- a/extension/modules/third-party/Timer.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Jetpack.
- *
- * The Initial Developer of the Original Code is Mozilla.
- * Portions created by the Initial Developer are Copyright (C) 2007
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Atul Varma
- * Drew Willcoxon
- * Erik Vold
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-var EXPORTED_SYMBOLS = ["Timer"];
-Components.utils.import("resource://scriptish/constants.js");
-
-const Timer = function() {
- var timers = new Timers();
-
- this.setTimeout = function(aCallback, aDelay) {
- return timers.addTimer(
- false, Ci.nsITimer.TYPE_ONE_SHOT, aCallback, TimeoutCallback, aDelay,
- Array.slice(arguments, 2));
- }
- this.clearTimeout = function(aTimerID) timers.removeTimer(aTimerID);
-
- this.setInterval = function(aCallback, aDelay) {
- return timers.addTimer(
- true, Ci.nsITimer.TYPE_REPEATING_SLACK, aCallback, IntervalCallback,
- aDelay, Array.slice(arguments, 2));
- }
- this.clearInterval = function(aTimerID) timers.removeTimer(aTimerID);
-}
-
-
-const Timers = function() {
- this.cache = {};
- this.nextTimerID = 0;
-}
-Timers.prototype = {
- getTimer: function(aTimerID) {
- return this.cache[aTimerID];
- },
- addTimer: function(aInterval, aType, aCallback, aCbType, aDelay, aParams) {
- var timerID = this.nextTimerID++;
- var timer = this.cache[timerID] = Instances.timer;
- var removeFunc = false;
-
- if (!aInterval) {
- var self = this;
- removeFunc = function() self.removeTimer(timerID);
- }
-
- timer.initWithCallback(
- new aCbType(aCallback, aParams, removeFunc), aDelay, aType);
- return timerID;
- },
- removeTimer: function(aTimerID) {
- var timer = this.cache[aTimerID];
- if (!timer) return;
- timer.cancel();
- delete this.cache[this.nextTimerID];
- }
-}
-
-
-function TimerCallback(aCallback, aParams) {
- this._callback = aCallback;
- this._params = aParams;
-};
-TimerCallback.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
-};
-
-function TimeoutCallback(aCallback, aParams, aRemoveFunc) {
- TimerCallback.apply(this, arguments);
- this._remove = aRemoveFunc;
-};
-TimeoutCallback.prototype = new TimerCallback();
-TimeoutCallback.prototype.notify = function notifyOnTimeout(timer) {
- this._remove();
- this._callback.apply(null, this._params);
-};
-
-function IntervalCallback(aCallback, aParams) {
- TimerCallback.apply(this, arguments)
-};
-IntervalCallback.prototype = new TimerCallback();
-IntervalCallback.prototype.notify = function notifyOnInterval() {
- this._callback.apply(null, this._params);
-};
diff --git a/extension/modules/third-party/regexpmerger.js b/extension/modules/third-party/regexpmerger.js
new file mode 100644
index 00000000..0c3def0e
--- /dev/null
+++ b/extension/modules/third-party/regexpmerger.js
@@ -0,0 +1,390 @@
+/* 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";
+
+const REG_TAINTED = /\\[ux]?\d/;
+const REG_TAINTED_ESCAPES = /\\\\/g;
+
+/**
+ * Filter function - Split a group of patterns into processable and
+ * tainted ones. The |this| context must be set up to refer to an
+ * array that will receive the tainted patterns.
+ * @param {String} r A pattern
+ * @returns {Boolean} Filter pattern
+ */
+function tainted_filter(r) {
+ // No negative lookbehind in js :p
+ if (REG_TAINTED.test(r.replace(REG_TAINTED_ESCAPES, ""))) {
+ this.push(r);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Filter function - Create an unique array
+ * @usage arr.filter(unique_filter, Object.create(null));
+ */
+function unique_filter(e) !((e in this) || (this[e] = null));
+
+/**
+ * Return a good prefix, with no bracket mismatches
+ * @param {String} Calculate the prefix from
+ * @return {String} Calculated safe prefix without bracket mismatches
+ */
+function killInvalidBrackets(string) {
+ let c = 0; // num of unclosed (
+ let C = 0; // num of unclosed [
+ let good = -1; // last good position
+
+ for (let i = 0, e = string.length; i < e; ++i) {
+ let ch = string[i];
+
+ if (ch == "\\") {
+ // step over escaping
+ ++i;
+ continue;
+ }
+
+ // ()
+ if (ch == '(') {
+ if (!C) {
+ // not in a character class []
+ if (!c) {
+ // not open yet
+ good = i - 1;
+ }
+ ++c;
+ }
+ continue;
+ }
+ if (ch == ')') {
+ if (!C) {
+ // not in a character class
+ --c;
+ if (c < 0) {
+ // cannot be valid and negative at the same time
+ // At this point the regex would be bad
+ break;
+ }
+ if (!c && !C) {
+ // all closed
+ good = i;
+ }
+ }
+ continue;
+ }
+
+ // []
+ if (ch == '[') {
+ if (!C && !c) {
+ // last good (nothing open)
+ good = i - 1;
+ }
+ ++C;
+ }
+ if (ch == ']') {
+ --C;
+ if (C < 0) {
+ // cannot be valid and negative at the same time
+ break;
+ }
+ if (!C && !c) {
+ // all closed now
+ good = i;
+ }
+ }
+ if (ch == "{") {
+ if (good == -1 || good != i - 1) {
+ good = i - 2;
+ }
+ else if (good != -1 && string[good] == ")" || string[good] == "]") {
+ // we don't really have a good position now :p
+ good = -1;
+ for (i = 0; i < e; ++i) {
+ ch = string[i];
+ if (ch == "\\") {
+ ++i;
+ }
+ else if (ch == "(" || ch == "[") {
+ break;
+ }
+ ++good;
+ }
+ }
+ else {
+ good -= 1;
+ }
+ // force not ok
+ c = 1;
+ break;
+ }
+ }
+
+ if (c == 0 && C == 0) {
+ // all closed, use whole string
+ return string;
+ }
+
+ if (good >= 0) {
+ // something is bad, but we got a good position
+ return string.substring(0, good + 1);
+ }
+
+ // whole string is invalid
+ return "";
+}
+
+/**
+ * Splits a pattern into individual alternates, if any
+ * @param {String} pattern Pattern to process
+ * @param {Array} rv Result array patterns will be pushed to
+ */
+function splitAlternates(pattern, rv) {
+ if (!pattern) {
+ rv.push(pattern);
+ return;
+ }
+
+ let c = 0; // num of unclosed (
+ let C = 0; // num of unclosed [
+ let cur = ""; // current alternate
+ for (let i = 0, e = pattern.length; i < e; ++i) {
+ let char = pattern[i];
+
+ if (char == "\\") {
+ cur += char + pattern[++i];
+ }
+ else if (char == "(") {
+ if (!C) ++c;
+ cur += char;
+ }
+ else if (char == ")") {
+ if (!C) --c;
+ cur += char;
+ }
+ else if (char == "[") {
+ ++C;
+ cur += char;
+ }
+ else if (char == "]") {
+ --C;
+ cur += char;
+ }
+ else {
+ if (char == "|" && !c && !C) {
+ rv.push(cur);
+ cur = "";
+ }
+ else {
+ cur += char;
+ }
+ }
+ }
+ rv.push(cur);
+}
+
+/**
+ * Sanitizes (truncates) a prefix, so that it does not end with an escape
+ * character
+ * @param {String} prefix Prefix to sanitize
+ * @returns {String} Sanitized Prefix
+ */
+function sanitizePrefixTail(prefix) {
+ const pl = prefix.length;
+ if (!pl) {
+ return "";
+ }
+ if (pl > 1 && prefix[pl-1] == "\\" && prefix[pl-2] != "\\" ) {
+ return prefix.substr(0, pl-1);
+ }
+ else if (pl == 1 && prefix == "\\") {
+ return "";
+ }
+ return prefix;
+}
+
+/**
+ * Recursively determine the the largest group with a common prefix
+ * The group is guaranteed to contain at least 3 items
+ *
+ * @param {Array} patterns Patterns to process. Must be sorted.
+ * @param {int} low Optional. Low bound. Default = 0
+ * @param {int} high Optional. High bound. Default = patterns.length
+ * @param {int} level Optional. Recursion level. Default = length
+ */
+function largestPrefixGroup(patterns, low, high, level) {
+ level = level || 0;
+ low = low || 0;
+ high = high || patterns.length;
+
+ // split patterns in heading char and tails
+ let heads = patterns.map(function(p) p.charAt(0));
+ let tails = patterns.map(function(p) p.substring(1));
+
+ let besti = -1; // best starting match
+ let beste = 0; // best ending match
+ let bestc = 0; // num of matches
+
+ for (let i = low; i < high - 1; ++i) {
+ let allgood = true;
+
+ for (let e = i + 1; e < high; ++e) {
+ if (heads[i] == heads[e]) {
+ continue;
+ }
+
+ // mismatched!
+ let c = e - i;
+ if (bestc < c) {
+ bestc = c;
+ beste = e;
+ besti = i;
+ }
+ allgood = false;
+ break;
+ }
+
+ if (allgood) {
+ let c = high - i;
+ if (bestc < c) {
+ bestc = c;
+ besti = i;
+ beste = high;
+ }
+ }
+ }
+
+ if (bestc < Math.min(4, Math.max(2, patterns.length))) {
+ // at least 3 items in the group are required
+ return [0,0,0];
+ }
+
+ let prefix = heads[besti];
+
+ if (tails.some(function(p) p.length == 0)) {
+ return [besti, beste, prefix];
+ }
+
+ let [nlow, nhigh, np] = largestPrefixGroup(tails, besti, beste, level + 1);
+ if (nhigh) {
+ prefix += np;
+ if (!level) {
+ // root level needs to check for bracket mismatches
+ // this might cause the group to get smaller than it has to be
+ // Consumers should/will account for this
+ prefix = killInvalidBrackets(prefix);
+ }
+ return [nlow, nhigh, prefix];
+ }
+
+ return [besti, beste, prefix];
+}
+
+/**
+ * Merge prefix group with set of patterns according to bounds and prefix
+ *
+ * @param {Array} patterns Set of patterns
+ * @param {int} low Lower bound
+ * @param {int} high Higher bound
+ * @param {String} prefix Prefix of the group
+ * @return {Array} mutated & reduced patterns array where the patterns
+ * specified by the low & high params are merged.
+ */
+function mergePatterns(patterns, low, high, prefix) {
+ const pl = prefix.length;
+
+ // splice the patterns to be merged, chop off their common prefix and join
+ let tails = patterns.splice(low, high - low).map(function(p) p.substring(pl));
+
+ // if there is an empty tail, then we can omit the whole group
+ let newpattern = "";
+ if (tails.indexOf("") == -1) {
+ newpattern = tails.join("|");
+ }
+
+ if (prefix && newpattern) {
+ newpattern = prefix + "(?:" + newpattern + ")";
+ }
+ else if (prefix) {
+ newpattern = prefix;
+ }
+
+ // Add the merged pattern
+ patterns.push(newpattern);
+
+ // need to return sorted as largestPrefixGroup relies on sorting
+ return patterns.sort();
+}
+
+function merge_finish_map(e) "(?:" + e + ")";
+
+function merge_finish(patterns, tainted) {
+ patterns = patterns.concat(tainted);
+ if (patterns.length < 2) {
+ return patterns[0];
+ }
+ return patterns.map(merge_finish_map).join("|");
+}
+
+/**
+ * Merge patterns with optimizations (prefixes)
+ * @param {Array} patterns Patterns to merge
+ * @returns {String} Resulting merged and optimized pattern
+ */
+function merge(patterns) {
+ if (patterns.length < 2) {
+ return patterns[0];
+ }
+
+ // Copy patterns and make unique
+ patterns = patterns.filter(unique_filter, Object.create(null));
+ if (patterns.length < 2) {
+ return patterns[0];
+ }
+
+ // Remove tainted patterns for now
+ let tainted = [];
+ patterns = patterns.filter(tainted_filter, tainted);
+
+ // split patterns into pieces by top-level alternates
+ let newpatterns = [];
+ for (let [,p] in Iterator(patterns)) {
+ splitAlternates(p, newpatterns);
+ }
+ patterns = newpatterns.filter(unique_filter, Object.create(null));
+ if (patterns.length < 2) {
+ return merge_finish(patterns, tainted);
+ }
+
+ // Good to go
+ patterns.sort();
+
+ for (;;) {
+ let [i, e, prefix] = largestPrefixGroup(patterns);
+ if (!e) {
+ // no common prefix found in (remaining) patterns
+ break;
+ }
+ const sprefix = sanitizePrefixTail(prefix);
+ patterns = mergePatterns(patterns, i, e, sprefix);
+ }
+
+ let len = patterns.length;
+ if (len == 1) {
+ // already merged into a single pattern
+ return merge_finish(patterns, tainted);
+ }
+
+ // not yet a single pattern (i.e. not all patterns shared a common prefix)
+ // merge without a prefix to get single pattern
+ return merge_finish(mergePatterns(patterns, 0, len, ""), tainted);
+}
+
+if ("exports" in this) {
+ exports.merge = merge;
+}
+else {
+ this.EXPORTED_SYMBOLS = ["merge"];
+}
diff --git a/extension/modules/utils/PatternCollection.js b/extension/modules/utils/PatternCollection.js
new file mode 100644
index 00000000..d8dee116
--- /dev/null
+++ b/extension/modules/utils/PatternCollection.js
@@ -0,0 +1,82 @@
+"use strict";
+const EXPORTED_SYMBOLS = ["PatternCollection"];
+
+Components.utils.import("resource://scriptish/utils/Scriptish_convert2RegExp.js");
+Components.utils.import("resource://scriptish/utils/Scriptish_getTLDURL.js");
+Components.utils.import("resource://scriptish/utils/Scriptish_mergeRegExps.js");
+
+const FAKE_REGEXP = {test: function() false};
+
+function merge(regs, flags) {
+ if (!regs.length) {
+ // No patterns -> always test |false|
+ // Do not merge, or we'll create an empty expression
+ // that will always test |true|
+ return FAKE_REGEXP;
+ }
+ return Scriptish_mergeRegExps(regs, flags);
+}
+
+function PatternCollection() {
+ this._patterns = [];
+ this._regs = [];
+ this._regsTLD = [];
+ this._regsSensitives = [];
+}
+PatternCollection.prototype = {
+ _hasTLD: false,
+ addPattern: function(pattern) {
+ if (typeof(pattern) != "string") {
+ return;
+ }
+ this._patterns.push(pattern);
+ var r = Scriptish_convert2RegExp(pattern);
+ if (r.isTLD) {
+ this._regsTLD.push(r);
+ this._hasTLD = true;
+ } else {
+ if (r.ignoreCase)
+ this._regs.push(r);
+ else
+ this._regsSensitives.push(r);
+ }
+
+ this._merged = this._mergedTLD = null;
+ },
+ addPatterns: function(patterns) {
+ if (!patterns) {
+ return;
+ }
+ if (!patterns.forEach) {
+ this.addPattern(patterns);
+ return;
+ }
+ for (var [,p] in Iterator(patterns)) {
+ this.addPattern(p);
+ }
+ },
+ clear: function() {
+ this._patterns.length = 0;
+ this._regs.length = this._regsSensitives = this._regsTLD.length = 0;
+ delete this._hasTLD;
+ this._merged = this._mergedTLD = null;
+ },
+ get patterns() this._patterns.concat(),
+ get merged() {
+ if (!this._merged)
+ this._merged = merge(this._regs, "i");
+ return this._merged;
+ },
+ get mergedSensitives() {
+ if (!this._mergedSensitives)
+ this._mergedSensitives = merge(this._regsSensitives);
+ return this._mergedSensitives;
+ },
+ get mergedTLD() {
+ if (!this._mergedTLD)
+ this._mergedTLD = merge(this._regsTLD, "i");
+ return this._mergedTLD;
+ },
+ test: function(url) this.merged.test(url) || this.mergedSensitives.test(url)
+ || (this._hasTLD && this.mergedTLD.test(Scriptish_getTLDURL(url)))
+};
diff --git a/extension/modules/utils/Scriptish_ExtendedStringBundle.js b/extension/modules/utils/Scriptish_ExtendedStringBundle.js
index acbb2748..3225dc58 100644
--- a/extension/modules/utils/Scriptish_ExtendedStringBundle.js
+++ b/extension/modules/utils/Scriptish_ExtendedStringBundle.js
@@ -1,5 +1,5 @@
var EXPORTED_SYMBOLS = ["Scriptish_ExtendedStringBundle"];
-Components.utils.import("resource://scriptish/logging.js");
+
function Scriptish_ExtendedStringBundle(aBase) {
this.basebundle = aBase;
this.strings = {};
diff --git a/extension/modules/utils/Scriptish_alert.js b/extension/modules/utils/Scriptish_alert.js
deleted file mode 100644
index 99310909..00000000
--- a/extension/modules/utils/Scriptish_alert.js
+++ /dev/null
@@ -1,8 +0,0 @@
-var EXPORTED_SYMBOLS = ["Scriptish_alert"];
-Components.utils.import("resource://scriptish/constants.js");
-
-function Scriptish_alert(aMsg, aWait) {
- if (typeof aWait == "number")
- return timeout(function() Scriptish_alert(aMsg), aWait);
- Services.prompt.alert(null, "Scriptish", aMsg+"");
-}
diff --git a/extension/modules/utils/Scriptish_convert2RegExp.js b/extension/modules/utils/Scriptish_convert2RegExp.js
index ea3e2a56..8ec43f21 100644
--- a/extension/modules/utils/Scriptish_convert2RegExp.js
+++ b/extension/modules/utils/Scriptish_convert2RegExp.js
@@ -1,49 +1,23 @@
var EXPORTED_SYMBOLS = ["Scriptish_convert2RegExp"];
-// Mighty TLD Check
-const tldChk = new RegExp("^(\\^(?:[^/]*)(?://)?(?:[^/]*))(\\\\\\.tld)((?:/.*)?)$");
+const RE_REGEXP = /^\/(.*)\/(i)?$/;
+const RE_ESCAPE = /[{}()\[\]\\^$.?]/g;
+const RE_WILD = /\*+/g;
+const RE_TLD = /^\^[^\/]*(?:\/\/)?[^\/]*\\\.tld(?:\/.*)?\$$/;
-// Converts simple pattern notation to a regular expression.
-// thanks AdBlock! http://www.mozdev.org/source/browse/adblock/adblock/
-function Scriptish_convert2RegExp(aPattern, aNoTLD) {
- var s = aPattern+"";
- var res = "^";
+function Scriptish_convert2RegExp(aPattern, aNoTLD, forceString) {
+ var s = aPattern.toString().trim(), m;
- var regExpChk = /^\/(.*)\/(i)?\n?$/.exec(s);
- if (regExpChk) return new RegExp(regExpChk[1], regExpChk[2]);
-
- for (var i = 0 ; i < s.length; i++) {
- switch(s[i]) {
- case "*":
- res += ".*";
- break;
- case ".":
- case "?":
- case "^":
- case "$":
- case "+":
- case "{":
- case "}":
- case "[":
- case "]":
- case "|":
- case "(":
- case ")":
- case "\\":
- res += "\\" + s[i];
- break;
- case " ":
- case "\n":
- break;
- default:
- res += s[i];
- break;
- }
+ // Already a regexp?
+ if (!forceString && (m = s.match(RE_REGEXP))) {
+ return new RegExp(m[1], m[2]);
}
- var regExp = new RegExp(res+"$", "i");
- if (!aNoTLD && tldChk.test(res))
- regExp.isTLD = true;
-
+ var res = "^" + s
+ .replace(RE_ESCAPE, "\\$&")
+ .replace(RE_WILD, ".*")
+ + "$";
+ var regExp = new RegExp(res, "i");
+ regExp.isTLD = !aNoTLD && RE_TLD.test(res);
return regExp;
}
diff --git a/extension/modules/utils/Scriptish_createUserScriptSource.js b/extension/modules/utils/Scriptish_createUserScriptSource.js
index 9ef8fccd..1685034c 100644
--- a/extension/modules/utils/Scriptish_createUserScriptSource.js
+++ b/extension/modules/utils/Scriptish_createUserScriptSource.js
@@ -1,43 +1,61 @@
var EXPORTED_SYMBOLS = ["Scriptish_createUserScriptSource"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyUtil(this, "stringBundle");
-function Scriptish_createUserScriptSource(aHeader) {
+function Scriptish_createUserScriptSource(aHeader, aContent) {
var script = ["// ==UserScript=="];
- var tmpAry, val, i;
- if (aHeader.id)
- script.push("// @id "+aHeader.id);
- else
- throw new Error(Scriptish_stringBundle("newscript.noID"));
+ function push(k, v) {
+
+ // Arrays
+ if (v.forEach) {
+ for each (let vv in v) {
+ push(k, vv);
+ }
+ return;
+ }
+
+ let pk = k.toString();
+ while (pk.length < 14) {
+ pk += " ";
+ }
+
+ // booleans
+ if (typeof v == "boolean") {
+ if (v) {
+ script.push("// @" + k);
+ }
+ return;
+ }
+
+ // XPCOM URIs
+ if (v instanceof Ci.nsIURI) {
+ v = v.spec;
+ }
+
+ // The rest
+ script.push("// @" + pk + " " + v.toString());
+ }
+
+ if (!aHeader.id || typeof aHeader.id != "string") {
+ throw Error(Scriptish_stringBundle("newscript.noID"));
+ }
+ // push the id first, always
+ push("id", aHeader.id);
+ delete aHeader.id;
+
+ for (let [k,v] in Iterator(aHeader)) {
+ push(k, v);
+ }
- if (aHeader.name)
- script.push("// @name "+aHeader.name);
- else
- throw new Error(Scriptish_stringBundle("newscript.noName"));
-
- if (aHeader.namespace)
- script.push("// @namespace "+aHeader.namespace);
-
- if (aHeader.description)
- script.push("// @description "+aHeader.description);
-
- if (aHeader.noframes
- || (typeof aHeader.noframes == "string" && "" == aHeader.noframes))
- script.push("// @noframes");
-
- if (tmpAry = aHeader.matches)
- for (i = 0; val = tmpAry[i++];)
- script.push("// @match "+val);
+ script.push("// ==/UserScript==");
+ script.push("");
- if (tmpAry = aHeader.includes)
- for (i = 0; val = tmpAry[i++];)
- script.push("// @include "+val);
+ if (aContent) {
+ script.push(aContent.toString());
+ script.push("");
+ }
- if (tmpAry = aHeader.excludes)
- for (i = 0; val = tmpAry[i++];)
- script.push("// @exclude "+val);
- script.push("// ==/UserScript==");
return script.join(Services.appinfo.OS == "WINNT" ? "\r\n" : "\n");
}
diff --git a/extension/modules/utils/Scriptish_cryptoHash.js b/extension/modules/utils/Scriptish_cryptoHash.js
index 8bd358d4..dd983c03 100644
--- a/extension/modules/utils/Scriptish_cryptoHash.js
+++ b/extension/modules/utils/Scriptish_cryptoHash.js
@@ -1,8 +1,8 @@
var EXPORTED_SYMBOLS = ["Scriptish_cryptoHash"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyUtil(this, "stringBundle");
-// this tells updateFromStream to read the entire string
+// this tells updateFromStream to read the entire string
const PR_UINT32_MAX = 0xffffffff;
function Scriptish_cryptoHash(aString, aAlg, aCharset) {
@@ -14,14 +14,16 @@ function Scriptish_cryptoHash(aString, aAlg, aCharset) {
try {
ch.initWithString(alg);
- } catch (e) {
- throw new Error(Scriptish_stringBundle("error.hash.algorithm"));
+ }
+ catch (e) {
+ throw Error(Scriptish_stringBundle("error.hash.algorithm"));
}
try {
unicodeConverter.charset = charset;
- } catch(e) {
- throw new Error(Scriptish_stringBundle("error.charset"));
+ }
+ catch(e) {
+ throw Error(Scriptish_stringBundle("error.charset"));
}
if (str)
diff --git a/extension/modules/utils/Scriptish_evalInSandbox.js b/extension/modules/utils/Scriptish_evalInSandbox.js
new file mode 100644
index 00000000..6f75cd56
--- /dev/null
+++ b/extension/modules/utils/Scriptish_evalInSandbox.js
@@ -0,0 +1,81 @@
+var EXPORTED_SYMBOLS = [ "Scriptish_evalInSandbox" ];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log", "Scriptish_logError", "Scriptish_logScriptError"]);
+lazyUtil(this, "stringBundle");
+
+const { Style } = jetpack("sdk/stylesheet/style");
+const { attach, detach } = jetpack("sdk/content/mod");
+
+const fileURLPrefix = "chrome://scriptish/content/scriptish.js -> ";
+jetpack('scriptish/security/api-check-filenames').add(Components.stack.filename);
+
+function Scriptish_evalInSandbox(aScript, aSandbox, aWindow, options) {
+ const jsVer = aScript.jsversion;
+ const fileURL = aScript.fileURL;
+ const id = aScript.id;
+ const scriptText = aScript.textContent + "\n";
+
+ // add script @css
+ aScript.css.forEach(function(aScriptCSS) {
+ attach(Style({ source: aScriptCSS.textContent }), aWindow);
+ });
+
+ // eval script @requires
+ try {
+ for (let [, req] in Iterator(aScript.requires)) {
+ var rfileURL = req.fileURL;
+ try {
+ Cu.evalInSandbox(
+ req.textContent + "\n",
+ aSandbox,
+ jsVer,
+ fileURLPrefix + rfileURL,
+ 1);
+ }
+ catch (e) {
+ Scriptish_logScriptError(e, aWindow, rfileURL, id);
+ }
+ }
+ }
+ catch (e) {
+ return Scriptish_logError(e, 0, fileURL, e.lineNumber);
+ }
+
+ // eval script
+ try {
+ try {
+ Cu.evalInSandbox(
+ scriptText,
+ aSandbox,
+ jsVer,
+ fileURLPrefix + fileURL,
+ 1);
+ }
+ catch (e if (e.message == "return not in function"
+ || /\(NS_ERROR_OUT_OF_MEMORY\) \[nsIXPCComponents_Utils.evalInSandbox\]/.test(e.message))) {
+ // catch errors when return is not in a function or when a window global
+ // is being overwritten (which throws NS_ERROR_OUT_OF_MEMORY..)
+ var sw = Instances.se;
+ sw.init(
+ Scriptish_stringBundle("warning.returnfrommain"),
+ fileURL,
+ "",
+ e.lineNumber || 0,
+ e.columnNumber || 0,
+ sw.warningFlag,
+ "scriptish userscript warnings");
+ Scriptish_logScriptError(sw, aWindow, fileURL, id);
+ Cu.evalInSandbox(
+ "(function(){" + scriptText + "})()",
+ aSandbox,
+ jsVer,
+ fileURLPrefix + fileURL,
+ 1);
+ }
+ }
+ catch (e) {
+ Scriptish_logScriptError(e, aWindow, fileURL, id);
+ }
+}
diff --git a/extension/modules/utils/Scriptish_findError.js b/extension/modules/utils/Scriptish_findError.js
deleted file mode 100644
index 5fff1c66..00000000
--- a/extension/modules/utils/Scriptish_findError.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var EXPORTED_SYMBOLS = ["Scriptish_findError"];
-
-function Scriptish_findError(aScript, aLineNum) {
- var start = 0;
- var end = 1;
- var len = aScript.offsets.length;
-
- for (var i = 0; i < len; i++) {
- end = aScript.offsets[i];
- if (aLineNum < end) {
- return {
- uri: aScript.requires[i].fileURL,
- lineNumber: (aLineNum - start)
- };
- }
- start = end;
- }
-
- return {
- uri: aScript.fileURL,
- lineNumber: (aLineNum - end)
- };
-}
diff --git a/extension/modules/utils/Scriptish_getBinaryContents.js b/extension/modules/utils/Scriptish_getBinaryContents.js
index 150395cc..8afa0270 100644
--- a/extension/modules/utils/Scriptish_getBinaryContents.js
+++ b/extension/modules/utils/Scriptish_getBinaryContents.js
@@ -1,6 +1,6 @@
var EXPORTED_SYMBOLS = ["Scriptish_getBinaryContents"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_getUriFromFile.js");
+lazyUtil(this, "getUriFromFile");
function Scriptish_getBinaryContents(aFile) {
var channel = Services.io.newChannelFromURI(Scriptish_getUriFromFile(aFile));
diff --git a/extension/modules/utils/Scriptish_getBrowserForContentWindow.js b/extension/modules/utils/Scriptish_getBrowserForContentWindow.js
deleted file mode 100644
index d4b3c1fb..00000000
--- a/extension/modules/utils/Scriptish_getBrowserForContentWindow.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var EXPORTED_SYMBOLS = ["Scriptish_getBrowserForContentWindow"];
-Components.utils.import("resource://scriptish/constants.js");
-function Scriptish_getBrowserForContentWindow(aWin) {
- // enumerate through browser windows
- let browserEnumerator = Services.wm.getEnumerator("navigator:browser");
- while (browserEnumerator.hasMoreElements()) {
- let browserWin = browserEnumerator.getNext();
- let tabBrowser = browserWin.getBrowser();
- for (let [, tab] in Iterator(tabBrowser.tabs)) {
- if (!tab) continue;
- let win = tab.linkedBrowser.contentWindow;
- if (aWin === win || aWin.top === win) return browserWin;
- }
- }
- return null;
-}
diff --git a/extension/modules/utils/Scriptish_getContents.js b/extension/modules/utils/Scriptish_getContents.js
index 7a108dc2..bec7e59c 100644
--- a/extension/modules/utils/Scriptish_getContents.js
+++ b/extension/modules/utils/Scriptish_getContents.js
@@ -1,32 +1,73 @@
var EXPORTED_SYMBOLS = ["Scriptish_getContents"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/logging.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_getUriFromFile.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_logError"]);
+lazyUtil(this, "getUriFromFile");
+lazyUtil(this, "stringBundle");
-function Scriptish_getContents(file, charset) {
- if (!charset) charset = "UTF-8";
- var scriptableStream = Services.sis;
- var unicodeConverter = Instances.suc;
- unicodeConverter.charset = charset;
+try {
+ // only available when the HUD console is shipped
+ // that excludes Seamonkey at the moment
+ Components.utils.import("resource:///modules/NetworkHelper.jsm");
+}
+catch (ex) {
+ // Minimal replacement
+ // XXX: Might want to keep this up-to-date?!
+ this.NetworkHelper = {
+ readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset) {
+ let text = null;
+ try {
+ text = NetUtil.readInputStreamToString(aStream, aStream.available());
+ if (!aCharset) {
+ return text;
+ }
+ let conv = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+ conv.charset = aCharset;
+ return conv.ConvertToUnicode(text);
+ }
+ catch (ex) {
+ return text;
+ }
+ }
+ };
+}
- var channel = Services.io.newChannelFromURI(Scriptish_getUriFromFile(file));
- try {
- var input = channel.open();
- } catch (e) {
- Scriptish_logError(
- new Error(Scriptish_stringBundle("error.openingFile") + ": " + file.path));
- return "";
- }
+function Scriptish_getContents(aFile, aCharset, aCallback) {
+ if (!aCharset) aCharset = "UTF-8";
+
+ if (aCallback) {
+ NetUtil.asyncFetch(aFile, function(aInputStream, aStatusCode) {
+ if (!Components.isSuccessCode(aStatusCode)) {
+ Scriptish_logError(
+ new Error(Scriptish_stringBundle("error.openingFile") + ": " + aFile.path));
+ return aCallback("");
+ }
+
+ aCallback(NetworkHelper.readAndConvertFromStream(aInputStream, aCharset));
+ });
+ } else {
+ var scriptableStream = Services.sis;
+ var unicodeConverter = Instances.suc;
+ unicodeConverter.charset = aCharset;
+
+ var channel = Services.io.newChannelFromURI(Scriptish_getUriFromFile(aFile));
+ try {
+ var input = channel.open();
+ } catch (e) {
+ Scriptish_logError(
+ new Error(Scriptish_stringBundle("error.openingFile") + ": " + aFile.path));
+ return "";
+ }
- scriptableStream.init(input);
- var str = scriptableStream.read(input.available());
- scriptableStream.close();
- input.close();
+ scriptableStream.init(input);
+ var str = scriptableStream.read(input.available());
+ scriptableStream.close();
+ input.close();
- try {
- return unicodeConverter.ConvertToUnicode(str);
- } catch (e) {
- return str;
+ try {
+ return unicodeConverter.ConvertToUnicode(str);
+ } catch (e) {
+ return str;
+ }
}
}
diff --git a/extension/modules/utils/Scriptish_getEditor.js b/extension/modules/utils/Scriptish_getEditor.js
index bbb3e88d..165de110 100644
--- a/extension/modules/utils/Scriptish_getEditor.js
+++ b/extension/modules/utils/Scriptish_getEditor.js
@@ -2,10 +2,10 @@ var EXPORTED_SYMBOLS = ["Scriptish_getEditor"];
const Cu = Components.utils;
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/prefmanager.js");
-Cu.import("resource://scriptish/logging.js");
-Cu.import("resource://scriptish/utils/Scriptish_alert.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+const { alert } = jetpack('scriptish/alert');
+lazyUtil(this, "stringBundle");
const Scriptish_getEditor = function(parentWindow, change) {
var editorPath = Scriptish_prefRoot.getValue("editor");
@@ -13,12 +13,20 @@ const Scriptish_getEditor = function(parentWindow, change) {
if (!change && editorPath) {
Scriptish_log("Found saved editor preference: " + editorPath);
+ // Check if Scratchpad
+ if ("Scratchpad" == editorPath)
+ return editorPath;
+
var editor = Instances.lf;
editor.followLinks = true;
- editor.initWithPath(editorPath);
+ try {
+ editor.initWithPath(editorPath);
+ } catch (e) {
+ editor = null;
+ }
// make sure the editor preference is still valid
- if (editor.exists() && editor.isExecutable()) {
+ if (editor && editor.exists() && editor.isExecutable()) {
return editor;
} else {
Scriptish_log("Editor preference either does not exist or is not executable");
@@ -29,30 +37,61 @@ const Scriptish_getEditor = function(parentWindow, change) {
// Ask the user to choose a new editor. Sometimes users get confused and
// pick a non-executable file, so we set this up in a loop so that if they do
// that we can give them an error and try again.
+ var hasScratchpad =
+ !!Services.wm.getMostRecentWindow("navigator:browser").Scratchpad;
+ try {
+ hasScratchpad = hasScratchpad
+ && Services.prefs.getBoolPref("devtools.scratchpad.enabled");
+ } catch(e) {}
+
+ var sp = Services.prompt;
+ var flags = sp.BUTTON_POS_0 * sp.BUTTON_TITLE_IS_STRING
+ + sp.BUTTON_POS_1 * sp.BUTTON_TITLE_IS_STRING;
+
while (true) {
Scriptish_log("Asking user to choose editor...");
+
+ // If available, ask if the user wants to use Scratchpad.
+ // Note: confirmEx always returns 1 if prompt is closed w/ the close button,
+ // so we need to keep the negative answer at button index 1.
+ if (hasScratchpad) {
+ var answer = sp.confirmEx(
+ null,
+ Scriptish_stringBundle("editor.useScratchpad"),
+ Scriptish_stringBundle("editor.useScratchpad"),
+ flags,
+ Scriptish_stringBundle("editor.useScratchpad.yes"),
+ Scriptish_stringBundle("editor.useScratchpad.no"),
+ "",
+ null,
+ {value:false});
+
+ // The user answered Yes. Set Scratchpad as the editor.
+ if (0 === answer) {
+ return Scriptish_prefRoot.setValue("editor", "Scratchpad");
+ }
+ }
+
var nsIFilePicker = Ci.nsIFilePicker;
- var filePicker = Instances.fp;
- filePicker.init(
+ var fp = Instances.fp;
+ fp.init(
parentWindow,
Scriptish_stringBundle("editor.prompt"),
nsIFilePicker.modeOpen);
- filePicker.appendFilters(nsIFilePicker.filterApplication);
- filePicker.appendFilters(nsIFilePicker.filterAll);
+ fp.appendFilters(nsIFilePicker.filterApplication);
+ fp.appendFilters(nsIFilePicker.filterAll);
- if (filePicker.show() != nsIFilePicker.returnOK) {
- // The user canceled, return null.
- Scriptish_log("User canceled file picker dialog");
+ if (fp.show() != nsIFilePicker.returnOK)
return null;
- }
- Scriptish_log("User selected: " + filePicker.file.path);
+ Scriptish_log("User selected: " + fp.file.path);
- if (filePicker.file.exists() && filePicker.file.isExecutable()) {
- Scriptish_prefRoot.setValue("editor", filePicker.file.path);
- return filePicker.file;
- } else {
- Scriptish_alert(Scriptish_stringBundle("editor.pleasePickExecutable"));
+ if (fp.file.exists() && fp.file.isExecutable()) {
+ Scriptish_prefRoot.setValue("editor", fp.file.path);
+ return fp.file;
+ }
+ else {
+ alert(Scriptish_stringBundle("editor.pleasePickExecutable"));
}
}
}
diff --git a/extension/modules/utils/Scriptish_getFirebugConsole.js b/extension/modules/utils/Scriptish_getFirebugConsole.js
index 943c8d6e..6307f3eb 100644
--- a/extension/modules/utils/Scriptish_getFirebugConsole.js
+++ b/extension/modules/utils/Scriptish_getFirebugConsole.js
@@ -1,3 +1,4 @@
+"use strict";
var EXPORTED_SYMBOLS = ["Scriptish_getFirebugConsole"];
function Scriptish_getFirebugConsole(win, chromeWin) {
diff --git a/extension/modules/utils/Scriptish_getProfileFile.js b/extension/modules/utils/Scriptish_getProfileFile.js
index c1074cb4..5c1880e8 100644
--- a/extension/modules/utils/Scriptish_getProfileFile.js
+++ b/extension/modules/utils/Scriptish_getProfileFile.js
@@ -1,8 +1,9 @@
+"use strict";
var EXPORTED_SYMBOLS = ["Scriptish_getProfileFile"];
Components.utils.import("resource://scriptish/constants.js");
const Scriptish_getProfileFile = function(aFilename) {
- var file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
+ var file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append(aFilename);
return file;
}
diff --git a/extension/modules/utils/Scriptish_getScriptHeader.js b/extension/modules/utils/Scriptish_getScriptHeader.js
new file mode 100644
index 00000000..8c59b51c
--- /dev/null
+++ b/extension/modules/utils/Scriptish_getScriptHeader.js
@@ -0,0 +1,48 @@
+"use strict";
+var EXPORTED_SYMBOLS = ["Scriptish_getScriptHeader"];
+Components.utils.import("resource://scriptish/constants.js");
+lazyUtil(this, "getContents");
+lazyUtil(this, "parser");
+
+function Scriptish_getScriptHeader(aScript, aKey, aLocalVal) {
+ if (aLocalVal) {
+ let key = aKey ? aKey.toLowerCase().trim() : "";
+
+ switch (key) {
+ case "id":
+ case "name":
+ case "namespace":
+ case "creator":
+ case "author":
+ case "description":
+ case "version":
+ case "jsversion":
+ case "delay":
+ case "noframes":
+ return aScript[key];
+ case "homepage":
+ case "homepageurl":
+ return aScript.homepageURL;
+ case "updateurl":
+ return aScript.updateURL;
+ case "contributor":
+ case "include":
+ case "exclude":
+ case "screenshot":
+ return aScript[key + "s"];
+ case "match":
+ return aScript[key + "es"];
+ case "grant":
+ return Object.keys(aScript.grant);
+ }
+ }
+
+ // TODO: cache headers and clear cache when the script is modified..
+ // TODO: should not use privates here
+ let headers = Scriptish_parser(Scriptish_getContents(aScript._tempFile || aScript._file));
+ if (aKey) {
+ return headers[aKey]
+ }
+
+ return headers;
+}
diff --git a/extension/modules/utils/Scriptish_getTLDURL.js b/extension/modules/utils/Scriptish_getTLDURL.js
index bfe1a56e..2e6cfc93 100644
--- a/extension/modules/utils/Scriptish_getTLDURL.js
+++ b/extension/modules/utils/Scriptish_getTLDURL.js
@@ -1,16 +1,13 @@
var EXPORTED_SYMBOLS = ["Scriptish_getTLDURL"];
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/utils/Scriptish_memoize.js");
+lazyUtil(this, "memoize");
const Scriptish_getTLDURL = Scriptish_memoize(function(aURL) {
- let tldURL = aURL;
try {
let uri = NetUtil.newURI(aURL);
- let host = uri.host;
- let tld = Services.tld.getPublicSuffixFromHost(host);
- uri.host = uri.host.replace(new RegExp(tld + "$"), "tld");
- tldURL = uri.spec;
+ uri.host = uri.host.slice(0, -Services.tld.getPublicSuffix(uri).length) + "tld";
+ return uri.spec;
} catch (e) {}
- return tldURL;
-});
+ return aURL;
+}, 200);
diff --git a/extension/modules/utils/Scriptish_getTempFile.js b/extension/modules/utils/Scriptish_getTempFile.js
index a483111b..e13690a8 100644
--- a/extension/modules/utils/Scriptish_getTempFile.js
+++ b/extension/modules/utils/Scriptish_getTempFile.js
@@ -2,8 +2,8 @@ var EXPORTED_SYMBOLS = ["Scriptish_getTempFile"];
Components.utils.import("resource://scriptish/constants.js");
function Scriptish_getTempFile() {
- var file = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
file.append("scriptish-temp");
- file.createUnique(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0640);
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0640);
return file;
}
diff --git a/extension/modules/utils/Scriptish_getWindowIDs.js b/extension/modules/utils/Scriptish_getWindowIDs.js
deleted file mode 100644
index ce4566b4..00000000
--- a/extension/modules/utils/Scriptish_getWindowIDs.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var EXPORTED_SYMBOLS = ["Scriptish_getWindowIDs"];
-Components.utils.import("resource://scriptish/constants.js");
-
-function Scriptish_getWindowIDs(aWin) {
- let utils = aWin.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- return {
- innerID: utils.currentInnerWindowID,
- outerID: utils.outerWindowID
- };
-}
diff --git a/extension/modules/utils/Scriptish_getWriteStream.js b/extension/modules/utils/Scriptish_getWriteStream.js
index 09e716c5..0fae0121 100644
--- a/extension/modules/utils/Scriptish_getWriteStream.js
+++ b/extension/modules/utils/Scriptish_getWriteStream.js
@@ -1,8 +1,11 @@
var EXPORTED_SYMBOLS = ["Scriptish_getWriteStream"];
Components.utils.import("resource://scriptish/constants.js");
-const Scriptish_getWriteStream = function(aFile) {
- var stream = Instances.fos;
- stream.init(aFile, 0x02 | 0x08 | 0x20, 420, -1);
- return stream;
+const Scriptish_getWriteStream = function(aFile, details) {
+ var fos;
+ details = details || {};
+ if (details.safe) fos = Instances.sfos;
+ else fos = Instances.fos;
+ fos.init(aFile, 0x02 | 0x08 | 0x20, 420, (details.defer) ? fos.DEFER_OPEN : -1);
+ return fos;
}
diff --git a/extension/modules/utils/Scriptish_injectScripts.js b/extension/modules/utils/Scriptish_injectScripts.js
new file mode 100644
index 00000000..59a4baca
--- /dev/null
+++ b/extension/modules/utils/Scriptish_injectScripts.js
@@ -0,0 +1,130 @@
+var EXPORTED_SYMBOLS = [ "Scriptish_injectScripts" ];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+Cu.import("resource://scriptish/api/GM_sandboxScripts.js");
+
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log", "Scriptish_logError"]);
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/api.js", ["GM_API"]);
+lazyImport(this, "resource://scriptish/api/GM_console.js", ["GM_console", "GM_getConsoleFor"]);
+lazyImport(this, "resource://scriptish/api/GM_ScriptLogger.js", ["GM_ScriptLogger"]);
+
+lazyImport(this, "resource://scriptish/third-party/Scriptish_getBrowserForContentWindow.js", ["Scriptish_getBrowserForContentWindow"]);
+
+lazyUtil(this, "evalInSandbox");
+lazyUtil(this, "windowUnloader");
+
+jetpack('scriptish/security/api-check-filenames').add(Components.stack.filename);
+
+const gTimer = jetpack('sdk/timers');
+const { getInnerId } = jetpack('sdk/window/utils');
+
+const {nsIDOMXPathResult: XPATH_RESULT} = Ci;
+
+let useGrants = Scriptish_prefRoot.getValue("enableScriptGrant");
+Scriptish_prefRoot.watch("enableScriptGrant", _ => {
+ useGrants = Scriptish_prefRoot.getValue("enableScriptGrant");
+});
+
+function Scriptish_injectScripts(options) {
+ const { scripts, url, safeWin } = options;
+ const chromeWin = Scriptish_getBrowserForContentWindow(safeWin).wrappedJSObject;
+ if (!chromeWin || !chromeWin.Scriptish_BrowserUI) return;
+
+ if (0 >= scripts.length) return;
+
+ let unsafeContentWin = safeWin.wrappedJSObject;
+ let winID = getInnerId(safeWin);
+
+ let delays = [];
+
+ // window destroyed handler
+ Scriptish_windowUnloader(function() {
+ // destroy a possible inject @delay
+ for (let [, id] in Iterator(delays)) gTimer.clearTimeout(id);
+ delays.length = 0;
+ }, winID);
+
+ for (let i = 0, e = scripts.length; i < e; ++i) {
+ // Do not "optimize" |script| out of the loop block and into the loop
+ // declaration!
+ // Need to keep a block scoped reference to |script| around so that GM_log
+ // and the delay code (and probably other consumer work).
+ let script = scripts[i];
+
+ let sandbox = new Cu.Sandbox(safeWin, {
+ sandboxName: script.fileURL,
+ sandboxPrototype: safeWin,
+ wantXrays: true
+ });
+
+ // hack XPathResult since that is so commonly used
+ sandbox.XPathResult = XPATH_RESULT;
+
+ Cu.evalInSandbox(GM_SandboxScripts, sandbox, "latest", "@initializer", 1);
+
+ // add GM_* API to sandbox
+ Scriptish_log('granting: ' + Object.keys(script.grant).join(', '))
+ {
+ let GM_api = new GM_API(extend(options, {
+ script: script,
+ url: url,
+ winID: winID,
+ safeWin: safeWin,
+ unsafeWin: unsafeContentWin,
+ chromeWin: chromeWin,
+ sandbox: sandbox
+ }));
+
+ for (let funcName in GM_api) {
+ if (!useGrants || script.grant[funcName]) {
+ sandbox[funcName] = GM_api[funcName];
+ }
+ else {
+ delete GM_api[funcName];
+ }
+ }
+ }
+
+ lazy(sandbox, "console", function() {
+ return GM_console(sandbox, script, safeWin, chromeWin);
+ });
+
+ if (!useGrants || script.grant['GM_log']) {
+ lazy(sandbox, "GM_log", function() {
+ const _console = GM_getConsoleFor(safeWisafeWin, chromeWin);
+ if (Scriptish_prefRoot.getValue("logToErrorConsole")) {
+ let logger = new GM_ScriptLogger(script);
+ return function() {
+ const args = Array.slice(arguments);
+ logger.log(args.join(" "));
+ _console.log.apply(_console, args);
+ }
+ }
+ return _console.log.bind(_console);
+ });
+ }
+
+ if (script.grant['unsafeWindow']) {
+ sandbox.unsafeWindow = unsafeContentWin;
+ }
+
+ let delay = script.delay;
+ if (delay || delay === 0) {
+ // don't use window's setTimeout, b/c then window could clearTimeout
+ delays.push(gTimer.setTimeout(function() {
+ Scriptish_evalInSandbox(script, sandbox, safeWin, options);
+ }, delay));
+ }
+ else {
+ Scriptish_evalInSandbox(script, sandbox, safeWin, options);
+ }
+
+ // window destroyed handler
+ Scriptish_windowUnloader(function() {
+ // try to nuke the sandbox (FF 17+ see bug 769273)
+ Cu.nukeSandbox(sandbox);
+ }, winID);
+ }
+}
diff --git a/extension/modules/utils/Scriptish_installUri.js b/extension/modules/utils/Scriptish_installUri.js
index 6ff254ef..a3c2550e 100644
--- a/extension/modules/utils/Scriptish_installUri.js
+++ b/extension/modules/utils/Scriptish_installUri.js
@@ -1,8 +1,15 @@
var EXPORTED_SYMBOLS = ["Scriptish_installUri"];
+
Components.utils.import("resource://scriptish/constants.js");
-Components.utils.import("resource://scriptish/config/configdownloader.js");
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyImport(this, "resource://scriptish/config/configdownloader.js", ["Scriptish_configDownloader"]);
+
+let { isPrivate } = jetpack('sdk/private-browsing');
function Scriptish_installUri(aURI, aWin) {
// docs for nsicontentpolicy say we're not supposed to block, so short timer.
- timeout(function() Scriptish_configDownloader.startInstall(aURI, aWin));
+ timeout(function() {
+ aURI = (typeof aURI == "string") ? NetUtil.newURI(aURI) : aURI;
+ Scriptish_configDownloader.startInstall(aURI, (aWin && isPrivate(aWin)));
+ });
}
diff --git a/extension/modules/utils/Scriptish_isGreasemonkeyable.js b/extension/modules/utils/Scriptish_isGreasemonkeyable.js
new file mode 100644
index 00000000..75bfb35a
--- /dev/null
+++ b/extension/modules/utils/Scriptish_isGreasemonkeyable.js
@@ -0,0 +1,39 @@
+var EXPORTED_SYMBOLS = ["Scriptish_isGreasemonkeyable"];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+
+function Scriptish_isGreasemonkeyable(aURL) {
+ if (!aURL)
+ return false;
+
+ // if the url provide is not a valid url, then an error could be thrown
+ try {
+ var scheme = Services.io.extractScheme(aURL);
+ if (!scheme) {
+ return false;
+ }
+ }
+ catch (e) {
+ return false;
+ }
+
+ switch (scheme) {
+ case "http":
+ case "https":
+ return Scriptish_prefRoot.getBoolValue("enabledSchemes.http", true);
+
+ case "ftp":
+ case "data":
+ return Scriptish_prefRoot.getBoolValue("enabledSchemes." + scheme, true);
+
+ case "about":
+ // Always allow "about:blank".
+ if (/^about:blank(?:[#?].*)?$/.test(aURL))
+ return true;
+ // no break
+ default:
+ return Scriptish_prefRoot.getBoolValue("enabledSchemes." + scheme, false);
+ }
+};
diff --git a/extension/modules/utils/Scriptish_isScriptRunnable.js b/extension/modules/utils/Scriptish_isScriptRunnable.js
new file mode 100644
index 00000000..1eedecb2
--- /dev/null
+++ b/extension/modules/utils/Scriptish_isScriptRunnable.js
@@ -0,0 +1,9 @@
+var EXPORTED_SYMBOLS = ["Scriptish_isScriptRunnable"];
+
+function Scriptish_isScriptRunnable(script, url, topWin) {
+ return !(!topWin && script.noframes)
+ && !script.delayInjection
+ && script.enabled
+ && !script.needsUninstall
+ && script.matchesURL(url);
+}
diff --git a/extension/modules/utils/Scriptish_isURLExcluded.js b/extension/modules/utils/Scriptish_isURLExcluded.js
new file mode 100644
index 00000000..ccbbb870
--- /dev/null
+++ b/extension/modules/utils/Scriptish_isURLExcluded.js
@@ -0,0 +1,21 @@
+var EXPORTED_SYMBOLS = [
+ "Scriptish_isURLExcluded",
+ "Scriptish_addExcludes",
+ "Scriptish_setExcludes",
+ "Scriptish_getExcludes"
+];
+
+Components.utils.import("resource://scriptish/utils/PatternCollection.js");
+
+var excludes = new PatternCollection();
+
+function Scriptish_isURLExcluded(url) excludes.test(url);
+
+function Scriptish_addExcludes(aExcludes) excludes.addPatterns(aExcludes);
+
+function Scriptish_setExcludes(aExcludes) {
+ excludes.clear();
+ excludes.addPatterns(aExcludes);
+};
+
+function Scriptish_getExcludes() excludes.patterns;
diff --git a/extension/modules/utils/Scriptish_localizeDOM.js b/extension/modules/utils/Scriptish_localizeDOM.js
new file mode 100644
index 00000000..ccad7f61
--- /dev/null
+++ b/extension/modules/utils/Scriptish_localizeDOM.js
@@ -0,0 +1,49 @@
+"use strict";
+
+const EXPORTED_SYMBOLS = ["Scriptish_localizeSubtree", "Scriptish_localizeOnLoad"];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+lazyUtil(this, "stringBundle");
+
+function Scriptish_localizeOnLoad(window) {
+ window.addEventListener("DOMContentLoaded", function localize() {
+ window.removeEventListener("DOMContentLoaded", localize, false);
+
+ Scriptish_localizeSubtree(window.document);
+ }, false);
+}
+
+function Scriptish_localizeSubtree(aNode) {
+ let document = aNode.ownerDocument || aNode;
+ let nodes = aNode.querySelectorAll("*[localize]");
+
+ for (let i = 0, e = nodes.length, n; i < e; ++i) {
+ n = nodes[i];
+ let localized = n.getAttribute("localize").split(",");
+ for each (let l in localized) {
+ try {
+ if (l == "#text") {
+ let s = Scriptish_stringBundle(n.textContent);
+ n.textContent = s;
+ continue;
+ }
+ let s = Scriptish_stringBundle(n.getAttribute(l));
+ n.setAttribute(l, s);
+
+ // also localize pref-panes
+ if (/^pane-/.test(n.id)) {
+ n = document.documentElement._selector.querySelector("*[pane=\"" + n.id + "\"]");
+ if (n) {
+ n.setAttribute(l, s);
+ }
+ }
+ }
+ catch (ex) {
+ Scriptish_log("Failed to set localized attribute; l=" + l);
+ }
+ }
+ }
+
+}
diff --git a/extension/modules/utils/Scriptish_memoize.js b/extension/modules/utils/Scriptish_memoize.js
index 2a965a59..16afcd9e 100644
--- a/extension/modules/utils/Scriptish_memoize.js
+++ b/extension/modules/utils/Scriptish_memoize.js
@@ -1,30 +1,105 @@
+"use strict";
var EXPORTED_SYMBOLS = ["Scriptish_memoize"];
-// Decorate a function with a memoization wrapper, with a limited-size cache
-// to reduce peak memory utilization. Simple usage:
-//
-// function foo(arg1, arg2) { /* complex operation */ }
-// foo = Scriptish_memoize(foo);
-//
-// The memoized function may have any number of arguments, but they must be
-// be serializable, and uniquely. It's safest to use this only on functions
-// that accept primitives.
-const Scriptish_memoize = function(func, limit) {
+/**
+ * Decorate a function with a memoization wrapper, with a limited-size cache
+ * to reduce peak memory utilization.
+ *
+ * The memoized function may have any number of arguments, but they must be
+ * be serializable. It's safest to use this only on functions that accept
+ * primitives.
+ *
+ * A memoized function is not thread-safe, but so is JS, nor re-entrant-safe!
+ *
+ * @usage var foo = Scriptish_memoize(function foo(arg1, arg2) { ... complex operation ... });
+ * @param {Function} func The function to be memoized
+ * @param {Number} limit Optional. Cache size (default: 3000)
+ * @param {Number} num_args Options. Number of arguments the function expects (default: func.length)
+ * @return {Function} Memoized function
+ */
+function Scriptish_memoize(func, limit, num_args) {
limit = limit || 3000;
- var cache = {__proto__: null};
+ num_args = num_args || func.length;
+
+ var cache = Object.create(null);
var keylist = [];
+ var args = [];
+ var key, result;
+
+ switch (num_args) {
+ case 0:
+ throw Error("memoize does not support functions without arguments");
+ case 1:
+ return function memoize_one_arg(a) {
+ key = a.toString();
+
+ if (key in cache)
+ return cache[key];
+
+ result = func.call(null, a);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+ case 2:
+ return function memoize_two_args(a, b) {
+ args[0] = a; args[1] = b;
+ key = JSON.stringify(args);
+ args.length = 0;
+
+ if (key in cache)
+ return cache[key];
+
+ var result = func.call(null, a, b);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+ case 3:
+ return function memoize_three_args(a, b, c) {
+ args[0] = a; args[1] = b; args[2] = c;
+ key = JSON.stringify(args);
+ args.length = 0;
+
+ if (key in cache)
+ return cache[key];
+
+ var result = func.call(null, a, b, c);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
- return function(a) {
- var args = Array.prototype.slice.call(arguments);
- var key = uneval(args);
- if (key in cache) return cache[key];
+ case 4:
+ return function memoize_four_args(a, b, c, d) {
+ args[0] = a; args[1] = b; args[2] = c; args[3] = d;
+ key = JSON.stringify(args);
+ args.length = 0;
- var result = func.apply(null, args);
+ if (key in cache)
+ return cache[key];
- cache[key] = result;
+ var result = func.call(null, a, b, c, d);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
- if (keylist.push(key) > limit) delete cache[keylist.shift()];
+ default:
+ return function() {
+ var key = JSON.stringify(arguments);
+ if (key in cache)
+ return cache[key];
- return result;
+ var result = func.apply(null, arguments);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
}
}
diff --git a/extension/modules/utils/Scriptish_mergeRegExps.js b/extension/modules/utils/Scriptish_mergeRegExps.js
new file mode 100644
index 00000000..655ee0e2
--- /dev/null
+++ b/extension/modules/utils/Scriptish_mergeRegExps.js
@@ -0,0 +1,24 @@
+"use strict";
+const EXPORTED_SYMBOLS = ["Scriptish_mergeRegExpStrings", "Scriptish_mergeRegExps"];
+Components.utils.import("resource://scriptish/prefmanager.js");
+
+function merge_map(e) "(?:" + e + ")";
+
+if (Scriptish_prefRoot.getValue("optimizingRegexpMerge")) {
+ Components.utils.import("resource://scriptish/third-party/regexpmerger.js");
+}
+else {
+ this.merge = function merge_naive(strs) {
+ if (strs.length < 2) {
+ return strs[0];
+ }
+ return strs.map(merge_map).join("|");
+ }
+}
+
+
+function Scriptish_mergeRegExpStrings(strs, flags) (
+ new RegExp(merge(strs), flags || ""))
+
+function Scriptish_mergeRegExps(regs, flags) (
+ Scriptish_mergeRegExpStrings([r.source for ([,r] in Iterator(regs))], flags))
diff --git a/extension/modules/utils/Scriptish_notification.js b/extension/modules/utils/Scriptish_notification.js
index 5cd94c65..ce8a652c 100644
--- a/extension/modules/utils/Scriptish_notification.js
+++ b/extension/modules/utils/Scriptish_notification.js
@@ -1,24 +1,24 @@
var EXPORTED_SYMBOLS = ["Scriptish_notification"];
+
Components.utils.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+
+const { notify } = jetpack("sdk/notifications");
+
+const DO_NOTHING = function(){};
+
function Scriptish_notification(aMsg, aTitle, aIconURL, aCallback) {
- timeout(function() {
- if (aCallback) var callback = new Observer(aCallback);
+ if (!Scriptish_prefRoot.getValue("enabledNotifications.sliding"))
+ return Scriptish_log(aMsg);
- // if Growl is not installed or disabled on OSX, then this will error
- try {
- Services.as.showAlertNotification(
- aIconURL || "chrome://scriptish/skin/icon_medium.png",
- aTitle || "Scriptish", aMsg+"", !!callback, "", callback || null);
- } catch (e) {}
- }, 0);
+ timeout(function() {
+ notify({
+ title: aTitle || "Scriptish",
+ text: aMsg + "",
+ iconURL: aIconURL || "chrome://scriptish/skin/scriptish32.png",
+ onClick: aCallback || DO_NOTHING
+ });
+ });
};
-
-function Observer(aCallback) {
- this._callback = aCallback;
-}
-Observer.prototype.observe = function() {
- if ("alertclickcallback" != arguments[1]) return;
- let self = this;
- timeout(function() { self._callback.call(null); }, 0);
-}
diff --git a/extension/modules/utils/Scriptish_openInEditor.js b/extension/modules/utils/Scriptish_openInEditor.js
index 38bbd619..b793bed1 100644
--- a/extension/modules/utils/Scriptish_openInEditor.js
+++ b/extension/modules/utils/Scriptish_openInEditor.js
@@ -1,12 +1,14 @@
+"use strict";
+
var EXPORTED_SYMBOLS = ["Scriptish_openInEditor"];
const Cu = Components.utils;
Cu.import("resource://scriptish/constants.js");
-Cu.import("resource://scriptish/prefmanager.js");
-Cu.import("resource://scriptish/utils/Scriptish_getEditor.js");
-Cu.import("resource://scriptish/utils/Scriptish_launchApplicationWithDoc.js");
-Cu.import("resource://scriptish/utils/Scriptish_alert.js");
-Cu.import("resource://scriptish/utils/Scriptish_stringBundle.js");
+const { alert } = jetpack("scriptish/alert");
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyUtil(this, "getEditor");
+lazyUtil(this, "launchApplicationWithDoc");
+lazyUtil(this, "stringBundle");
function Scriptish_openInEditor(script, parentWindow) {
var file = script.editFile;
@@ -15,12 +17,34 @@ function Scriptish_openInEditor(script, parentWindow) {
if (!editor) return;
try {
- Scriptish_launchApplicationWithDoc(editor, file);
- } catch (e) {
- // Something may be wrong with the editor the user selected. Remove so that
- // next time they can pick a different one.
- Scriptish_alert(Scriptish_stringBundle("editor.couldNotLaunch") + "\n" + e);
+ if ("Scratchpad" == editor)
+ openScriptInScratchpad(parentWindow, file);
+ else
+ Scriptish_launchApplicationWithDoc(editor, file);
+ }
+ catch (e) {
+ // Something may be wrong with the editor the user selected. Remove it.
+ alert(Scriptish_stringBundle("editor.couldNotLaunch") + "\n" + e);
Scriptish_prefRoot.remove("editor");
throw e;
}
}
+
+function openScriptInScratchpad(parentWindow, file) {
+ let spWin = (parentWindow.Scratchpad
+ || Services.wm.getMostRecentWindow("navigator:browser").Scratchpad)
+ .openScratchpad();
+
+ spWin.addEventListener("load", function spWinLoaded() {
+ spWin.removeEventListener("load", spWinLoaded, false);
+
+ let Scratchpad = spWin.Scratchpad;
+ Scratchpad.setFilename(file.path);
+ Scratchpad.addObserver({
+ onReady: function() {
+ Scratchpad.removeObserver(this);
+ Scratchpad.importFromFile.call(Scratchpad, file);
+ }
+ });
+ }, false);
+}
diff --git a/extension/modules/utils/Scriptish_openInTab.js b/extension/modules/utils/Scriptish_openInTab.js
new file mode 100644
index 00000000..17682ad5
--- /dev/null
+++ b/extension/modules/utils/Scriptish_openInTab.js
@@ -0,0 +1,49 @@
+var EXPORTED_SYMBOLS = ["Scriptish_openInTab"];
+
+const Cu = Components.utils;
+Cu.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+
+const prefs = jetpack('sdk/preferences/service');
+
+function Scriptish_openInTab(aURL, aLoadInBackground, aReuse, aChromeWin) {
+ aChromeWin = aChromeWin || Scriptish.getMostRecentWindow();
+ aLoadInBackground = aLoadInBackground || prefs.get('browser.tabs.loadInBackground', true);
+
+ // Try to reuse an existing tab
+ if (aReuse) {
+ let browserEnumerator = Services.wm.getEnumerator("navigator:browser");
+
+ while (browserEnumerator.hasMoreElements()) {
+ let browserWin = browserEnumerator.getNext();
+ let tabBrowser = browserWin.gBrowser;
+ let i = tabBrowser.browsers.length - 1;
+
+ for (; ~i; i--) {
+ let browser = tabBrowser.getBrowserAtIndex(i);
+ // TODO: check rel=canonical too
+ if (aURL === browser.currentURI.spec) {
+ if (!aLoadInBackground) {
+ tabBrowser.selectedTab = tabBrowser.tabContainer.childNodes[i];
+ browserWin.focus();
+ }
+ return getWindowForBrowser(browser);
+ }
+ }
+ }
+ }
+
+ // Opening a new tab
+ var browser = aChromeWin.gBrowser;
+ var selectedTab = browser.selectedTab;
+ var newTab = browser.loadOneTab(aURL, {"inBackground": !!aLoadInBackground});
+ var afterCurrent = prefs.get('browser.tabs.insertRelatedAfterCurrent', true);
+
+ if (afterCurrent)
+ browser.moveTabTo(newTab, selectedTab._tPos + 1);
+
+ return getWindowForBrowser(browser.getBrowserForTab(newTab));
+}
+
+function getWindowForBrowser(browser) browser.docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
diff --git a/extension/modules/utils/Scriptish_openManager.js b/extension/modules/utils/Scriptish_openManager.js
new file mode 100644
index 00000000..98bf9504
--- /dev/null
+++ b/extension/modules/utils/Scriptish_openManager.js
@@ -0,0 +1,19 @@
+var EXPORTED_SYMBOLS = ["Scriptish_openManager"];
+Components.utils.import("resource://scriptish/constants.js");
+
+function Scriptish_openManager(aURL, aID) {
+ aURL = aURL || "addons://list/userscript";
+ if (aID) {
+ aURL = 'addons://detail/' + encodeURIComponent(aID) + '/preferences';
+ }
+
+ let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+ // Fx
+ if (browserWin.BrowserOpenAddonsMgr) {
+ browserWin.BrowserOpenAddonsMgr(aURL);
+ }
+ // Sm
+ else if (browserWin.toEM) {
+ browserWin.toEM(aURL);
+ }
+}
diff --git a/extension/modules/utils/Scriptish_parser.js b/extension/modules/utils/Scriptish_parser.js
new file mode 100644
index 00000000..34ea14f1
--- /dev/null
+++ b/extension/modules/utils/Scriptish_parser.js
@@ -0,0 +1,32 @@
+"use strict";
+var EXPORTED_SYMBOLS = ["Scriptish_parser"];
+
+function Scriptish_parser(aSource) {
+ var headers = {};
+ var foundMeta = false;
+ var line;
+
+ // do not 'optimize' by reusing this reg exp! it should not be reused!
+ var metaRegExp = /\/\/[ \t]*(?:==(\/?UserScript)==|\@(\S+)(?:[ \t]+([^\r\f\n]+))?)/g;
+
+ // read one line at a time looking for start meta delimiter or EOF
+ while (line = metaRegExp.exec(aSource)) {
+ if (line[1]) {
+ if ("userscript" == line[1].toLowerCase()) {
+ foundMeta = true; // start
+ continue;
+ } else {
+ break; // done
+ }
+ }
+ if (!foundMeta) continue;
+
+ var header = line[2].toLowerCase();
+ var value = line[3];
+
+ if (!headers[header]) headers[header] = [value];
+ else headers[header].push(value);
+ }
+
+ return headers;
+}
diff --git a/extension/modules/utils/Scriptish_popupNotification.js b/extension/modules/utils/Scriptish_popupNotification.js
new file mode 100644
index 00000000..cc53ab4e
--- /dev/null
+++ b/extension/modules/utils/Scriptish_popupNotification.js
@@ -0,0 +1,38 @@
+var EXPORTED_SYMBOLS = ["Scriptish_popupNotification"];
+Components.utils.import("resource://gre/modules/PopupNotifications.jsm");
+Components.utils.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/scriptish.js", ["Scriptish"]);
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log"]);
+
+function Scriptish_popupNotification(details) {
+ if (!Scriptish_prefRoot.getValue("enabledNotifications.popup"))
+ return Scriptish_log(details.message);
+
+ let secondaryActions = details.secondaryActions || [];
+
+ var win = Scriptish.getMostRecentWindow();
+ if (win && win.PopupNotifications) {
+ timeout(function() {
+ win.PopupNotifications.show(
+ win.gBrowser.selectedBrowser,
+ details.id,
+ details.message,
+ "scriptish-notification-icon",
+ {
+ label: details.mainAction.label,
+ accessKey: details.mainAction.accessKey,
+ callback: function() {
+ details.mainAction.callback();
+ }
+ },
+ secondaryActions,
+ details.options
+ );
+ });
+
+ return true;
+ }
+
+ return false;
+};
diff --git a/extension/modules/utils/Scriptish_stringBundle.js b/extension/modules/utils/Scriptish_stringBundle.js
index 2f4c0f1f..d6e371b3 100644
--- a/extension/modules/utils/Scriptish_stringBundle.js
+++ b/extension/modules/utils/Scriptish_stringBundle.js
@@ -12,7 +12,7 @@ const engBundle = Services.strings.createBundle((function(){
return tmp.join("/")
})());
-XPCOMUtils.defineLazyGetter(this, "Scriptish_getPref", function() {
+lazy(this, "Scriptish_getPref", function() {
let tools = {};
Components.utils.import("resource://scriptish/prefmanager.js", tools);
return function(aVal) tools.Scriptish_prefRoot.getValue(aVal);
diff --git a/extension/modules/utils/Scriptish_updateChk.js b/extension/modules/utils/Scriptish_updateChk.js
new file mode 100644
index 00000000..20d77648
--- /dev/null
+++ b/extension/modules/utils/Scriptish_updateChk.js
@@ -0,0 +1,8 @@
+var EXPORTED_SYMBOLS = [];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+timeout(function() {
+ Services.scriptloader
+ .loadSubScript("chrome://scriptish/content/js/updatecheck.js");
+}, 750);
diff --git a/extension/modules/utils/Scriptish_updateModifiedScripts.js b/extension/modules/utils/Scriptish_updateModifiedScripts.js
new file mode 100644
index 00000000..1095c262
--- /dev/null
+++ b/extension/modules/utils/Scriptish_updateModifiedScripts.js
@@ -0,0 +1,77 @@
+var EXPORTED_SYMBOLS = ["Scriptish_updateModifiedScripts"];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+lazyImport(this, "resource://scriptish/prefmanager.js", ["Scriptish_prefRoot"]);
+lazyImport(this, "resource://scriptish/config.js", ["Scriptish_config"]);
+
+lazyUtil(this, "injectScripts");
+lazyUtil(this, "isScriptRunnable");
+
+const docRdyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"];
+
+function Scriptish_updateModifiedScripts(href, safeWin, shouldNotRun) {
+ if (!Scriptish_prefRoot.getValue("enableScriptRefreshing"))
+ return;
+
+ Scriptish_config.updateModifiedScripts(function(script) {
+ if (shouldNotRun()
+ || !Scriptish_isScriptRunnable(script, href, (safeWin === safeWin.top)))
+ return;
+
+ let rdyStateIdx = docRdyStates.indexOf(safeWin.document.readyState);
+ function inject() {
+ if (shouldNotRun())
+ return;
+
+ Scriptish_injectScripts({
+ scripts: [script],
+ url: href,
+ safeWin: safeWin
+ });
+ }
+
+ switch (script.runAt) {
+ case "document-end":
+ if (2 > rdyStateIdx) {
+ safeWin.addEventListener("DOMContentLoaded", function listener() {
+ safeWin.removeEventListener("DOMContentLoaded", listener, true);
+ inject();
+ }, true);
+ return;
+ }
+ break;
+ case "document-idle":
+ if (2 > rdyStateIdx) {
+ safeWin.addEventListener("DOMContentLoaded", function listener() {
+ safeWin.removeEventListener("DOMContentLoaded", listener, true);
+ timeout(inject);
+ }, true);
+ return;
+ }
+ break;
+ case "document-complete":
+ if (4 > rdyStateIdx) {
+ safeWin.document.addEventListener("readystatechange", function listener() {
+ if ("complete" != safeWin.document.readyState)
+ return;
+ safeWin.document.removeEventListener("readystatechange", listener, true);
+ inject();
+ }, true);
+ return;
+ }
+ break;
+ case "window-load":
+ if (4 > rdyStateIdx) {
+ safeWin.addEventListener("load", function listener() {
+ safeWin.removeEventListener("load", listener, true);
+ inject();
+ }, true);
+ return;
+ }
+ break;
+ }
+
+ inject();
+ });
+}
diff --git a/extension/modules/utils/Scriptish_windowEventTracker.js b/extension/modules/utils/Scriptish_windowEventTracker.js
new file mode 100644
index 00000000..3b5bb402
--- /dev/null
+++ b/extension/modules/utils/Scriptish_windowEventTracker.js
@@ -0,0 +1,52 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["Scriptish_windowEventTracker"];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+lazyUtil(this, "windowUnloader");
+
+const { getInnerId } = jetpack('sdk/window/utils');
+
+const events = ["DOMContentLoaded", "load"];
+
+const trackers = Object.create(null);
+
+function Scriptish_windowEventTracker(aWin) {
+ return;
+ const winID = getInnerId(aWin);
+ if (winID in trackers)
+ return trackers[winID];
+
+ trackers[winID] = "start";
+
+ aWin.addEventListener("DOMContentLoaded", function onDOMContentLoaded() {
+ aWin.removeEventListener("DOMContentLoaded", onDOMContentLoaded, true);
+
+ // if the tracker event gte to this one has occurred then ignore
+ if (~events.indexOf(trackers[winID]))
+ return;
+
+ trackers[winID] = "DOMContentLoaded";
+ }, true);
+
+ aWin.document.addEventListener("readystatechange", function readyStateListener() {
+ if ("complete" != aWin.document.readyState)
+ return;
+ aWin.document.removeEventListener("readystatechange", readyStateListener, true);
+
+ trackers[winID] = "readystate@complete";
+ }, true);
+
+ aWin.addEventListener("load", function onLoad() {
+ aWin.removeEventListener("load", onLoad, true);
+
+ trackers[winID] = "load";
+ }, true);
+
+ Scriptish_windowUnloader(function() {
+ delete trackers[winID];
+ }, winID);
+
+ return trackers[winID];
+};
diff --git a/extension/modules/utils/Scriptish_windowUnloader.js b/extension/modules/utils/Scriptish_windowUnloader.js
new file mode 100644
index 00000000..154516f4
--- /dev/null
+++ b/extension/modules/utils/Scriptish_windowUnloader.js
@@ -0,0 +1,32 @@
+var EXPORTED_SYMBOLS = ["Scriptish_windowUnloader"];
+Components.utils.import("resource://scriptish/constants.js");
+lazyImport(this, "resource://scriptish/logging.js", ["Scriptish_log", "Scriptish_logError"]);
+
+const winUnloaders = {};
+
+const observer = {
+ observe: function(aSubject, aTopic, aData) {
+ var innerID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ var unloaders = winUnloaders[innerID];
+ if (!unloaders) return;
+
+ for (var i = unloaders.length - 1; ~i; i--) {
+ try {
+ unloaders[i]();
+ }
+ catch(e) {
+ Scriptish_logError(e);
+ }
+ }
+
+ winUnloaders[innerID] = null;
+ }
+};
+Services.obs.addObserver(observer, "inner-window-destroyed", false);
+
+const Scriptish_windowUnloader = function(aUnloader, aInnerID) {
+ if (!winUnloaders[aInnerID])
+ winUnloaders[aInnerID] = [];
+
+ winUnloaders[aInnerID].push(aUnloader);
+};
diff --git a/extension/modules/utils/q.js b/extension/modules/utils/q.js
new file mode 100644
index 00000000..01bee7b8
--- /dev/null
+++ b/extension/modules/utils/q.js
@@ -0,0 +1,13 @@
+var EXPORTED_SYMBOLS = ["Q"];
+
+Components.utils.import("resource://scriptish/constants.js");
+
+let Q = {};
+
+Q.defer = jetpack('sdk/core/promise').defer;
+
+Q.chain = function chain() {
+ var funcs = Array.prototype.slice.call(arguments);
+ if (!funcs.length) return true;
+ return funcs.shift()().then(function() Q.chain.apply(null, funcs));
+}
diff --git a/extension/skin/aboutScriptish.css b/extension/skin/aboutScriptish.css
new file mode 100644
index 00000000..405cd632
--- /dev/null
+++ b/extension/skin/aboutScriptish.css
@@ -0,0 +1,93 @@
+body {
+ background-color: -moz-Dialog;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+h1, h2, h3 {
+ margin: 0;
+ padding: 0;
+ margin-bottom: 1ex;
+ font-family: "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;
+}
+
+section { margin-bottom: 3em; }
+section > p {
+ max-width: 80ex;
+}
+
+header { margin-bottom: 1.5em; }
+header.primary {
+ padding-bottom: 4px;
+ border-bottom: 2px solid #bada55;
+ font-size: x-large;
+}
+header.primary img {
+ margin-bottom: -5px;
+ padding-right: 6px;
+}
+header.primary h1 { display:inline; }
+header.secondary h2 { border-bottom: 1px dotted #ccc; }
+
+ul { margin-top: 5px; padding-left: 15px; }
+li { margin-bottom: 0.2em; }
+
+p { margin: 0; padding: 0; margin-bottom: 0.5em; }
+
+a { color: #00f; text-decoration: none; }
+a:hover { color: #09f; }
+
+article {
+ color: black;
+ background-color: white;
+
+ margin: 2em auto 2em;
+ padding: 3em;
+ max-width: 110ex;
+ background-color: #fff;
+ border: 1px solid ThreeDShadow;
+ border-radius: 15px;
+}
+
+#main table {
+ width: 100%;
+ padding: 0;
+ margin: 0;
+}
+#main td {
+ vertical-align: top;
+ margin: 1ex;
+}
+
+#contlist {
+ -moz-column-count: 2;
+}
+
+@media all and (min-width: 1100px) {
+ #contlist {
+ -moz-column-count: 3;
+ }
+}
+
+.homepage {
+ color: black;
+ border-bottom: 1px dotted lightgray;
+}
+
+#test {
+ display: none;
+ padding: 0;
+}
+#test > section {
+ margin: 0;
+}
+
+#test header { margin-bottom: 0; border: none; }
+
+#qunit-testresult {
+ max-width: 100% !important;
+}
+
+#qunit-tests li {
+ margin: 0 !important;
+}
+
diff --git a/extension/skin/addonstab.css b/extension/skin/addonstab.css
index 0877d287..0f1acf8c 100644
--- a/extension/skin/addonstab.css
+++ b/extension/skin/addonstab.css
@@ -1,6 +1,27 @@
-#category-userscripts > .category-icon{
+#category-userscript > .category-icon {
list-style-image: url("chrome://scriptish/skin/third-party/uso_medium.png");
}
.addon[type="userscript"] .control-container {
-moz-binding: url("chrome://scriptish/content/addonstab.xml#control-container");
}
+
+page.scriptish #addon-list-empty, #scriptish-list-empty {
+ display: none;
+}
+
+page.scriptish #scriptish-detail-contrib-description {
+ font-style: italic;
+ margin-bottom: 1em;
+ color: #373D48; /* color not set in gnomestripe... */
+}
+page.scriptish #scriptish-detail-contrib-description, #detail-contrib-description {
+ display: inline;
+}
+page.scriptish #detail-contrib-description, #scriptish-detail-contrib-description {
+ display: none;
+}
+
+/* Hide the 'restart now' link for user scripts. */
+page.scriptish #addon-list button[command=cmd_restartApp] {
+ display: none;
+}
diff --git a/extension/skin/browser-fennec.css b/extension/skin/browser-fennec.css
new file mode 100644
index 00000000..c1bc6d43
--- /dev/null
+++ b/extension/skin/browser-fennec.css
@@ -0,0 +1,7 @@
+#tool-userscripts {
+ list-style-image: url("chrome://scriptish/skin/scriptish32.png");
+}
+
+richlistitem[typeName="userscript"] {
+ -moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-local")
+}
diff --git a/extension/skin/browser.css b/extension/skin/browser.css
index 3841ec06..96f25cf5 100644
--- a/extension/skin/browser.css
+++ b/extension/skin/browser.css
@@ -1,20 +1,51 @@
#scriptish-button {
- -moz-box-orient: horizontal;
- list-style-image: url("chrome://scriptish/skin/icon_24.png");
- -moz-image-region: rect(0, 24px, 24px, 0);
+ -moz-box-orient: horizontal;
+ -moz-appearance: none;
+ padding: 0;
+ margin: 0;
}
-toolbar[iconsize="small"] #scriptish-button {
- list-style-image: url("chrome://scriptish/skin/icon_16.png");
- -moz-image-region: rect(0, 16px, 16px, 0);
+toolbar #scriptish-button {
+ -moz-binding: url("chrome://scriptish/content/third-party/firebug/startButton.xml#scriptish-button");
}
-#scriptish-button[scriptish-disabled] {
- list-style-image: url("chrome://scriptish/skin/icon_24_disabled.png");
+/* Also set a default icon for the "outer" #scriptish-button.
+ It will be disabled while the binding is still loading.
+ See GH-108 */
+#scriptish-button,
+#scriptish-button-inner {
+ -moz-box-orient: horizontal;
+ list-style-image: url("chrome://scriptish/skin/scriptish24.png");
+}
+toolbar[iconsize="small"] #scriptish-button,
+toolbar[iconsize="small"] #scriptish-button-inner {
+ list-style-image: url("chrome://scriptish/skin/scriptish16.png");
+}
+toolbarpaletteitem[place="palette"] > #scriptish-button,
+#scriptish-button[cui-areatype="menu-panel"],
+#scriptish-button[cui-areatype="menu-panel"] > #scriptish-button-inner {
+ list-style-image: url("chrome://scriptish/skin/scriptish32.png");
}
-toolbar[iconsize="small"] #scriptish-button[scriptish-disabled] {
- list-style-image: url("chrome://scriptish/skin/icon_16_disabled.png");
+#scriptish-button-inner[scriptish-disabled] {
+ list-style-image: url("chrome://scriptish/skin/scriptish24_disabled.png");
+}
+toolbar[iconsize="small"] #scriptish-button-inner[scriptish-disabled] {
+ list-style-image: url("chrome://scriptish/skin/scriptish16_disabled.png");
+}
+#scriptish-button[cui-areatype="menu-panel"][scriptish-disabled],
+#scriptish-button[cui-areatype="menu-panel"] > #scriptish-button-inner[scriptish-disabled] {
+ list-style-image: url("chrome://scriptish/skin/scriptish32_disabled.png");
}
-#gm-status {width: 16px;}
+#scriptish-notification-icon {
+ list-style-image: url("chrome://scriptish/skin/scriptish16.png");
+}
+
+.popup-notification-icon[popupid="scriptish-install-popup-notification"] {
+ list-style-image: url("chrome://scriptish/skin/scriptish64.png");
+}
+
+#notification-popup-box[anchorid="scriptish-notification-icon"] > #scriptish-notification-icon {
+ display: -moz-box;
+}
diff --git a/extension/skin/icon_16.png b/extension/skin/icon_16.png
deleted file mode 100644
index a5e74c06..00000000
Binary files a/extension/skin/icon_16.png and /dev/null differ
diff --git a/extension/skin/icon_16_disabled.png b/extension/skin/icon_16_disabled.png
deleted file mode 100644
index 8910c829..00000000
Binary files a/extension/skin/icon_16_disabled.png and /dev/null differ
diff --git a/extension/skin/icon_24.png b/extension/skin/icon_24.png
deleted file mode 100644
index ca4abb28..00000000
Binary files a/extension/skin/icon_24.png and /dev/null differ
diff --git a/extension/skin/icon_24_disabled.png b/extension/skin/icon_24_disabled.png
deleted file mode 100644
index a888e328..00000000
Binary files a/extension/skin/icon_24_disabled.png and /dev/null differ
diff --git a/extension/skin/icon_32_disabled.png b/extension/skin/icon_32_disabled.png
deleted file mode 100644
index 0f29e287..00000000
Binary files a/extension/skin/icon_32_disabled.png and /dev/null differ
diff --git a/extension/skin/icon_medium.png b/extension/skin/icon_medium.png
deleted file mode 100644
index 4afc8c28..00000000
Binary files a/extension/skin/icon_medium.png and /dev/null differ
diff --git a/extension/skin/install.css b/extension/skin/install.css
index 27c9da08..5557fb6e 100644
--- a/extension/skin/install.css
+++ b/extension/skin/install.css
@@ -12,7 +12,7 @@
-moz-appearance:textfield;
margin:2px 4px;
}
-#itemBox {
+#itemBox{
padding-top: 1ex;
overflow:auto;
}
@@ -21,21 +21,37 @@
background:InfoBackground;
color: InfoText;
border-bottom:1px black dotted;
- padding:0.5em;
+ padding: 0.5em;
+ padding-right: 1.5em;
}
-#scriptIcon {
- max-width: 48px;
- max-height: 48px;
+#scriptIconBox {
+ width: 64px;
+ height: 64px;
+ margin-top: 1ex;
margin-right: 1ex;
}
-#scriptDescription{margin:0;}
+#scriptIcon {
+ min-width: 24px;
+ min-height: 24px;
+ max-width: 64px;
+ max-height: 64px;
+ image-rendering: optimizeQuality;
+ image-rendering: -moz-crisp-edges;
+}
-#scriptDescription strong{font-size: 1.25em;}
+#scriptTitle {
+ font-weight: bold;
+ font-size: 1.1em;
+ margin-bottom: 1ex;
+}
-#showSource-box {
+#showSource-box{
margin: 1ex;
+ -moz-box-flex: 1;
+ -moz-box-pack: end;
+ -moz-box-align: end;
}
#itemBox vbox:not(#script-desc-vbox){
@@ -43,9 +59,13 @@
display:none;
}
-#excludes-desc, #includes-desc, #matches-desc{font-style:italic;}
+#excludes-desc, #includes-desc, #matches-desc {
+ font-style:italic;
+}
-#itemBox vbox.display:not(#script-desc-vbox){display:inherit;}
+#itemBox vbox.display:not(#script-desc-vbox) {
+ display:inherit;
+}
html|ul {
margin-top: 0;
diff --git a/extension/skin/newscript.css b/extension/skin/newscript.css
new file mode 100644
index 00000000..a4b4be2d
--- /dev/null
+++ b/extension/skin/newscript.css
@@ -0,0 +1,21 @@
+@import url(chrome://global/skin/);
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+row {
+ /* some more white space, so that the invalid borders don't overlap */
+ margin-bottom: 2px;
+ margin-bottom: 4px;
+}
+
+row > label {
+ margin-right: 1em;
+}
+
+textbox {
+ min-width: 35em;
+}
+
+*[required][invalid="true"] {
+ box-shadow: 0pt 0pt 1.5px 1px red;
+}
diff --git a/extension/skin/options.css b/extension/skin/options.css
new file mode 100644
index 00000000..8ec872b3
--- /dev/null
+++ b/extension/skin/options.css
@@ -0,0 +1,35 @@
+@import "chrome://global/skin/";
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+radio[pane] {
+ list-style-image: url(options.png);
+}
+radio[pane="pane-main"] {
+ -moz-image-region: rect(0px, 32px, 32px, 0px);
+}
+radio[pane="pane-ui"] {
+ -moz-image-region: rect(0px, 128px, 32px, 96px);
+}
+radio[pane="pane-excludes"] {
+ -moz-image-region: rect(0px, 64px, 32px, 32px);
+}
+radio[pane="pane-sync"] {
+ list-style-image: url(chrome://browser/skin/sync-32.png);
+}
+radio[pane="pane-advanced"] {
+ -moz-image-region: rect(0px, 96px, 32px, 64px);
+}
+
+caption,
+description {
+ font-weight: bold;
+}
+
+.remark {
+ font-style: italic;
+ font-size: x-small;
+}
+
+tabpanel {
+ -moz-box-orient: vertical;
+}
diff --git a/extension/skin/options.png b/extension/skin/options.png
new file mode 100644
index 00000000..526b9728
Binary files /dev/null and b/extension/skin/options.png differ
diff --git a/extension/skin/scriptish16.png b/extension/skin/scriptish16.png
new file mode 100755
index 00000000..09d5b78c
Binary files /dev/null and b/extension/skin/scriptish16.png differ
diff --git a/extension/skin/scriptish16_disabled.png b/extension/skin/scriptish16_disabled.png
new file mode 100755
index 00000000..1362b0c4
Binary files /dev/null and b/extension/skin/scriptish16_disabled.png differ
diff --git a/extension/skin/scriptish24.png b/extension/skin/scriptish24.png
new file mode 100755
index 00000000..ac9378bc
Binary files /dev/null and b/extension/skin/scriptish24.png differ
diff --git a/extension/skin/scriptish24_disabled.png b/extension/skin/scriptish24_disabled.png
new file mode 100755
index 00000000..2d036fbe
Binary files /dev/null and b/extension/skin/scriptish24_disabled.png differ
diff --git a/extension/skin/scriptish32.png b/extension/skin/scriptish32.png
new file mode 100644
index 00000000..afeabf75
Binary files /dev/null and b/extension/skin/scriptish32.png differ
diff --git a/extension/skin/scriptish32_disabled.png b/extension/skin/scriptish32_disabled.png
new file mode 100644
index 00000000..61ce4ad6
Binary files /dev/null and b/extension/skin/scriptish32_disabled.png differ
diff --git a/extension/skin/scriptish64.png b/extension/skin/scriptish64.png
new file mode 100644
index 00000000..5b6b4cb8
Binary files /dev/null and b/extension/skin/scriptish64.png differ
diff --git a/extension/skin/third-party/alert.css b/extension/skin/third-party/alert.css
new file mode 100644
index 00000000..b5b1c464
--- /dev/null
+++ b/extension/skin/third-party/alert.css
@@ -0,0 +1,99 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Scott MacGregor
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* ===== alert.css =====================================================
+ == Styles specific to the alerts dialog.
+ ======================================================================= */
+
+@import url("chrome://global/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.alertBox {
+ border: 2px solid #7B969C;
+ background-color: -moz-Dialog;
+ min-height: 50px;
+}
+
+.alertBox[orient="horizontal"] > .alertImageBox {
+ -moz-margin-start: 4px;
+ -moz-margin-end: 6px;
+ min-height: 46px;
+}
+
+.alertBox[orient="vertical"] > .alertImageBox {
+ margin-top: 6px;
+ margin-bottom: 4px;
+ min-width: 46px;
+}
+
+.alertTitle {
+ font-weight: bold;
+}
+
+.alertText {
+ -moz-margin-end: 6px;
+}
+
+#alertNotification[clickable="true"] {
+ cursor: pointer;
+}
+
+label {
+ cursor: inherit;
+}
+
+.alertText[clickable="true"] {
+ color: -moz-nativehyperlinktext;
+ text-decoration: underline;
+}
+
+.alertText[clickable="true"]:hover:active {
+ color: #424F63;
+}
+
+.alertBox[orient="horizontal"] > .alertTextBox {
+ -moz-padding-end: 10px;
+ padding-top: 5px;
+}
+
+.alertBox[orient="vertical"] > .alertTextBox {
+ -moz-padding-start: 5px;
+ -moz-padding-end: 5px;
+ margin-bottom: 8px;
+ -moz-box-align: center; /* also hard-coded in alert.js, see bug 311557 */
+}
diff --git a/sync-locales.js b/sync-locales.js
new file mode 100644
index 00000000..da32291e
--- /dev/null
+++ b/sync-locales.js
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+
+var path = require("path");
+var glob = require("glob").glob;
+var globSync = require("glob").globSync;
+var fs = require("fs");
+
+
+function Properties(file) {
+ this.file = file;
+ this.items = {};
+ var self = this;
+
+ // store the keys/values
+ var data = fs.readFileSync(file, "utf-8");
+ var list = data.match(/[^\n]*(\n|$)/g);
+
+ for (var i = list.length - 1; ~i; i--) {
+ var m = list[i].match(/(^[^#=]*)=([^\n]*)(\n|$)/, "");
+ if (!m) continue;
+ self.items[m[1]] = m[2];
+ }
+}
+
+Properties.prototype.merge = function(rhs) {
+ // remove the obsolete keys
+ for (var key in this.items)
+ if (!rhs.items[key])
+ delete this.items[key];
+
+ // add new keys
+ for (var key in rhs.items)
+ if (!this.items[key])
+ this.items[key] = "";
+};
+
+Properties.prototype.save = function() {
+ var keys = [], newStr = [], self = this;
+ for (var key in this.items) {
+ keys.push(key);
+ }
+
+ keys.sort(function(a, b) {
+ return (a.toLowerCase() > b.toLowerCase()) ? 1 : -1;
+ });
+ for (var i = 0, e = keys.length; i < e; i++) {
+ newStr.push(keys[i] + "=" + this.items[keys[i]]);
+ }
+ newStr = newStr.join("\n") + "\n";
+ fs.writeFile(self.file, newStr, "utf-8", function(e) {
+ if (e) throw e;
+ });
+};
+
+function process_properties(base, files) {
+ base = new Properties(base);
+
+ for (var i = files.length - 1, file; ~i; i--) {
+ file = files[i];
+ if (file == base.file)
+ continue
+ console.log(file);
+ file = new Properties(file);
+ file.merge(base);
+ file.save();
+ }
+}
+
+glob("extension/locale/en-US/*.properties", function(e, m) {
+ if (e) throw e;
+ m.forEach(function(f) {
+ var fn = f.match(/[^\\\/\.]*.properties$/)[0];
+ var files = globSync("extension/locale/*/" + fn);
+ process_properties(f, files);
+ });
+})
+
diff --git a/sync-locales.py b/sync-locales.py
new file mode 100644
index 00000000..803f1dd9
--- /dev/null
+++ b/sync-locales.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# Nils Maier has dedicated this work to the public domain by waiving all of his
+# rights to the work worldwide under copyright law, including all related
+# and neighboring rights, to the extent allowed by law.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+import os, sys, re
+from glob import glob
+from codecs import open as copen
+
+class Properties(object):
+ def __init__(self, file):
+ self.file = file
+ self._items = {}
+ with copen(self.file, "rb", encoding="utf-8") as fp:
+ for line in fp:
+ line = line.strip()
+ if line.startswith("#"):
+ continue
+ key, value = line.split("=", 1)
+ self._items[key] = value
+
+ def merge(self, rhs):
+ # remove obsolete
+ for k in self._items.keys():
+ if not k in rhs._items:
+ del self._items[key]
+
+ # add new keys
+ for k in rhs._items.keys():
+ if not k in self._items:
+ self._items[k] = ""
+
+ def save(self):
+ with copen(self.file, "wb", encoding="utf-8") as op:
+ for k in sorted(self._items.keys(), key=unicode.lower):
+ op.write("%s=%s\n" % (k, self._items[k]))
+
+def process_properties(base, files):
+ base = Properties(base)
+ base.save()
+ for file in files:
+ if file == base.file:
+ continue
+ print file
+ file = Properties(file)
+ file.merge(base)
+ file.save()
+
+
+if __name__ == "__main__":
+ for f in glob("extension/locale/en-US/*.properties"):
+ fn = os.path.basename(f)
+ process_properties(f, glob("extension/locale/*/" + fn))
diff --git a/test.js b/test.js
new file mode 100644
index 00000000..aec1d32a
--- /dev/null
+++ b/test.js
@@ -0,0 +1,45 @@
+#!/usr/bin/env node
+
+var exec = require('child_process').exec;
+
+var debug = ("debug" == process.argv[2]);
+
+exec("./build.sh test", function() {
+ console.log("Running mozmill tests...");
+ if (debug) {
+ exec("mozmill -t tests/mozmill-tests -a scriptish-test.xpi --show-all", doAsycTests.bind(null, 0));
+ } else {
+ exec("mozmill -t tests/mozmill-tests -a scriptish-test.xpi", doAsycTests.bind(null, 0));
+ }
+});
+
+function dumpResults(e, out) {
+ if (out) console.log(out);
+}
+
+function doAsycTests(step, e, out) {
+ dumpResults(e, out);
+ step = step || 0;
+ switch (step) {
+ case 4:
+ console.log("Running mozmill-restart tests...");
+ if (debug) {
+ exec("mozmill-restart -t tests/mozmill-tests/tests/restartTests -a scriptish-test.xpi --show-all", dumpResults);
+ } else {
+ exec("mozmill-restart -t tests/mozmill-tests/tests/restartTests -a scriptish-test.xpi", dumpResults);
+ }
+ return;
+ case 0:
+ console.log("Starting restart tests in 10 secs... (Press ctrl+z to cancel)");
+ return setTimeout(doAsycTests.bind(null, ++step), 7000);
+ case 1:
+ console.log("Starting restart tests in 3 secs...");
+ return setTimeout(doAsycTests.bind(null, ++step), 1000);
+ case 2:
+ console.log("Starting restart tests in 2 secs...");
+ return setTimeout(doAsycTests.bind(null, ++step), 1000);
+ case 3:
+ console.log("Starting restart tests in 1 secs...");
+ return setTimeout(doAsycTests.bind(null, ++step), 1000);
+ }
+}
diff --git a/tests/mozmill-tests/tests/restartTests/testOpenInTab/test1.js b/tests/mozmill-tests/tests/restartTests/testOpenInTab/test1.js
new file mode 100644
index 00000000..a4f1a65e
--- /dev/null
+++ b/tests/mozmill-tests/tests/restartTests/testOpenInTab/test1.js
@@ -0,0 +1,109 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://scriptish/utils/Scriptish_openInTab.js");
+
+var tabs = require("../../../../../../mozmill-tests/lib/tabs");
+
+var URL = "about:scriptish";
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+var testOpenInTabDefaults = function() {
+ var win = controller.window;
+
+ tabs.closeAllTabs(controller);
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+
+ // Open about:scriptish and verify the page loaded
+ var window = Scriptish_openInTab(URL);
+ let tabBrowser = win.gBrowser;
+
+ controller.waitFor(function() {
+ return controller.tabs.activeTab.defaultView.location.href == URL;
+ }, "wait for about:scriptish", 500, 100);
+
+ window.close();
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+}
+
+var testOpenInTabArgLoadInBackground = function() {
+ var win = controller.window;
+
+ tabs.closeAllTabs(controller);
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+
+ // Open about:scriptish and verify the page loaded
+ var window = Scriptish_openInTab(URL, true);
+ let tabBrowser = win.gBrowser;
+
+ controller.waitFor(function() {
+ return tabs.getTabsWithURL(URL).length == 1;
+ }, "wait for about:scriptish", 500, 100);
+
+ controller.assert(function() controller.tabs.activeTab.defaultView.location.href != URL);
+
+ window.close();
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+}
+
+var testOpenInTabArgReuse = function() {
+ var win = controller.window;
+
+ tabs.closeAllTabs(controller);
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+
+ // Open about:scriptish and verify the page loaded
+ var window = Scriptish_openInTab(URL, false, true);
+ let tabBrowser = win.gBrowser;
+
+ // resuse should open a new tab when there is not one already, and focus on it
+ controller.waitFor(function() {
+ return controller.tabs.activeTab.defaultView.location.href == URL;
+ }, "wait for about:scriptish", 500, 100);
+
+ var window2 = Scriptish_openInTab(URL, false, true);
+ // resuse should not open a new tab when there is one already, and focus on it
+ controller.waitFor(function() {
+ var win = controller.tabs.activeTab.defaultView;
+ return window2 === win && window2 === window;
+ }, "wait for about:scriptish again w/ reuse = true", 500, 100);
+
+ window.close();
+ controller.assert(function() (tabs.getTabsWithURL(URL).length == 0));
+}
diff --git a/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishOpenedFR.js b/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishOpenedFR.js
new file mode 100644
index 00000000..30545d1b
--- /dev/null
+++ b/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishOpenedFR.js
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var tabs = require("../../../../../mozmill-tests/lib/tabs");
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+// Test that the about:scriptish tab is opened on first run
+var testAboutScriptishOpened = function() {
+ var results = tabs.getTabsWithURL("about:scriptish");
+
+ controller.assert(function() {
+ return results.length === 1;
+ }, "Only one about:scriptish tab was opened");
+}
diff --git a/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishTests.js b/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishTests.js
new file mode 100644
index 00000000..588b57fb
--- /dev/null
+++ b/tests/mozmill-tests/tests/testAboutScriptish/testAboutScriptishTests.js
@@ -0,0 +1,65 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+var testFailed = false;
+
+var testAboutScriptishTestsRan = function() {
+ // Go to about:scriptish?test and verify the page loaded
+ controller.open("about:scriptish?test");
+ controller.waitForPageLoad();
+ controller.assertNode(
+ new elementslib.ID(controller.tabs.activeTab, "test"));
+ var config = controller.tabs.activeTab.defaultView.QUnit.config;
+
+ controller.waitFor(function() {
+ if (!config.blocking
+ && ("END" == config.currentModule)
+ && ("END" == config.previousModule)) {
+ testFailed = (config.stats.bad > 0);
+ return true;
+ } else {
+ return false;
+ }
+ }, null, 1500);
+}
+
+var testAboutScriptishTestFailed = function() {
+ controller.assert(function() !testFailed);
+}
diff --git a/tests/mozmill-tests/tests/testGeneral/testTimeout.js b/tests/mozmill-tests/tests/testGeneral/testTimeout.js
new file mode 100644
index 00000000..5ed497f4
--- /dev/null
+++ b/tests/mozmill-tests/tests/testGeneral/testTimeout.js
@@ -0,0 +1,80 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://scriptish/constants.js");
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+//Test that timeout works
+var testTimeoutExists = function() {
+ controller.assert(
+ function() { return !!timeout; },
+ "Test timeout exists",
+ 50);
+}
+
+// Test that timeout works
+var testTimeoutDefault = function() {
+ var success = false;
+ timeout(function() {
+ success = true;
+ });
+ controller.assert(function() !success);
+
+ controller.waitFor(
+ function() { return success; },
+ "Test timeout works with default",
+ 250, 10);
+}
+
+//Test that timeout works with a param
+var testTimeoutWithParam = function() {
+ var success = false;
+ timeout(function() (success = true), 50);
+
+ controller.assert(function() !success);
+ controller.sleep(30);
+ controller.assert(function() !success);
+ controller.sleep(30);
+ controller.assert(function() success);
+
+ controller.waitFor(
+ function() success,
+ "Test timeout works with a param",
+ 250, 10);
+}
diff --git a/tests/mozmill-tests/tests/testToolbarbutton/testTBB.js b/tests/mozmill-tests/tests/testToolbarbutton/testTBB.js
new file mode 100644
index 00000000..9fd56062
--- /dev/null
+++ b/tests/mozmill-tests/tests/testToolbarbutton/testTBB.js
@@ -0,0 +1,61 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://scriptish/scriptish.js");
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+// Test that the element with ID 'scriptish-button' can be found.
+var testToolbarButton = function() {
+ var status = Scriptish.enabled;
+ var tbb = (new elementslib.ID(controller.window.document, "scriptish-button"));
+ var tbbm = controller.window.document.getElementById("scriptish-tb-popup");
+ controller.click(tbb);
+ controller.assert(function() (!status == Scriptish.enabled), "Scriptish is disabled");
+ controller.assert(function() (tbbm.state == "closed"), "Menu is closed");
+ controller.click(tbb);
+ controller.assert(function() (status == Scriptish.enabled), "Scriptish is enabled");
+ controller.assert(function() (tbbm.state == "closed"), "Menu is closed");
+ // below this line is WIP.
+ controller.rightClick(tbb);
+ controller.assert(function() (status == Scriptish.enabled), "Scriptish is still enabled");
+ /*controller.waitFor(function() {
+ return (tbbm.state == "open" || tbbm.state == "showing");
+ }, "Menu is open", 500, 2);*/
+};
+
diff --git a/tests/mozmill-tests/tests/testToolbarbutton/testTBBExists.js b/tests/mozmill-tests/tests/testToolbarbutton/testTBBExists.js
new file mode 100644
index 00000000..94af51d4
--- /dev/null
+++ b/tests/mozmill-tests/tests/testToolbarbutton/testTBBExists.js
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scriptish mozmill test suite.
+ *
+ * The Initial Developer of the Original Code is Erik Vold.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Erik Vold (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var setupModule = function(module) {
+ module.controller = mozmill.getBrowserController();
+}
+
+// Test that the element with ID 'scriptish-button' can be found.
+var testToolbarButtonExists = function() {
+ controller.waitFor(function() {
+ return (new elementslib.ID(controller.window.document, "scriptish-button")).exists();
+ }, "Scriptish toolbar button should be found", 2000);
+
+ controller.waitFor(function() {
+ return (new elementslib.ID(controller.window.document, "scriptish-tb-popup")).exists();
+ }, "Scriptish toolbar button should be found", 2000);
+}
diff --git a/update.rdf b/update.rdf
deleted file mode 100644
index 42a27332..00000000
--- a/update.rdf
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
- 0.1
-
-
- {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
- 4.0b5pre
- 4.0.*
- http://github.com/downloads/erikvold/scriptish/scriptish-0.1.xpi
- sha1:4c580dd3bfa177fcf731bbbc73a4b88433dc5fba
-
-
-
-
- {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}
- 2.1a3
- 2.1.*
- http://github.com/downloads/erikvold/scriptish/scriptish-0.1.xpi
- sha1:4c580dd3bfa177fcf731bbbc73a4b88433dc5fba
-
-
-
-
-
-
-
- MIGTMA0GCSqGSIb3DQEBDQUAA4GBACgVZadk1YLJR3dDyc7XO1512F/vGhd2oWoDXHnxunbHIPlKDamfAyavR78ZQBEVJNqL+xmJfUAB9DnFbON3kLm8pdI/FgnGaR/hXDhrDU/9eeKvWv9oq5ifUBLWmweRB4rSPOeoEEACco76/sdWoWwxodPISUUmdJDf9LZqKWvB
-
-