diff --git a/Makefile b/Makefile
index 3f03164..9d57ac5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,32 @@
BUILDDIR := build/
-NAME := ipvfoo
-VERSION := $(shell cat src/manifest.json | \
- sed -n 's/^ *"version": *"\([0-9.]\+\)".*/\1/p' | \
- head -n1)
+NAME := ipvfoo
+MANIFEST := src/manifest.json
+MANIFEST_F := src/manifest/firefox-manifest.json
+MANIFEST_C := src/manifest/chrome-manifest.json
+VERSION_F := $(shell cat ${MANIFEST_F} | \
+ sed -n 's/^ *"version": *"\([0-9.]\+\)".*/\1/p' | \
+ head -n1)
+VERSION_C := $(shell cat ${MANIFEST_C} | \
+ sed -n 's/^ *"version": *"\([0-9.]\+\)".*/\1/p' | \
+ head -n1)
all: prepare firefox chrome
prepare:
+ @diff ${MANIFEST} ${MANIFEST_F} >/dev/null || \
+ diff ${MANIFEST} ${MANIFEST_C} >/dev/null || \
+ (echo "${MANIFEST} is not a copy of ${MANIFEST_F} or ${MANIFEST_C}; aborting."; exit 1)
mkdir -p build
firefox: prepare
- rm -f ${BUILDDIR}${NAME}-${VERSION}.xpi
- cd src && zip -9r ../${BUILDDIR}${NAME}-${VERSION}.xpi *
+ rm -f ${BUILDDIR}${NAME}-${VERSION_F}.xpi
+ cp -f ${MANIFEST_F} ${MANIFEST}
+ zip -9j ${BUILDDIR}${NAME}-${VERSION_F}.xpi -j src/*
chrome: prepare
- rm -f ${BUILDDIR}${NAME}-${VERSION}.zip
- zip -9r ${BUILDDIR}${NAME}-${VERSION}.zip src
+ rm -f ${BUILDDIR}${NAME}-${VERSION_C}.zip
+ cp -f ${MANIFEST_C} ${MANIFEST}
+ zip -9j ${BUILDDIR}${NAME}-${VERSION_C}.zip -j src/*
clean:
rm -rf ${BUILDDIR}
diff --git a/README.md b/README.md
index d47c87b..ebe750f 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,49 @@
-IPvFoo is a Chrome extension that adds an icon to your location bar, indicating whether the current page was fetched using IPv4 or IPv6. When you click the icon, a pop-up appears, listing the IP address for each domain that served the page elements.
+**IPvFoo** is a Chrome/Firefox extension that adds an icon to indicate whether the current page was fetched using IPv4 or IPv6. When you click the icon, a pop-up appears, listing the IP address for each domain that served the page elements.
-Everything is captured privately using the webRequest API (new in Chrome 17), without creating any additional network traffic.
+Everything is captured privately using the webRequest API, without creating any additional network traffic.
-#### Install it from the Chrome Web Store:
-https://chrome.google.com/webstore/detail/ecanpcehffngcegjmadlcijfolapggal
+## Screenshots
+
-#### Screenshot:
-
+
-#### Firefox Support:
-IPvFoo now [runs on Firefox](https://addons.mozilla.org/en-US/firefox/addon/ipvfoo-pmarks/), but there are [a few bugs](https://github.com/pmarks-net/ipvfoo/issues/32) to work out.
+## Add to Chrome
+https://chrome.google.com/webstore/detail/ipvfoo/ecanpcehffngcegjmadlcijfolapggal
+
+
+
+
+
+## Add to Firefox
+https://addons.mozilla.org/addon/ipvfoo/
+
+
+
+
+
+## Add to Edge
+https://microsoftedge.microsoft.com/addons/detail/ipvfoo/dphnkggpaicipkljebciobedeiaiofod
+*(You can also run the Chrome version on Edge, as they are identical.)*
+
+## Safari?
+
+IPvFoo cannot be [ported to Safari](https://github.com/pmarks-net/ipvfoo/issues/39) because the `webRequest` API does not report IP addresses. In theory, a Safari extension could do its own DNS lookups over HTTPS, but such behavior is beyond the scope of IPvFoo.
+
+## Running IPvFoo unpacked from git
+
+IPvFoo shares a common codebase for Chrome and Firefox, but `manifest.json` is browser specific.
+
+Firefox shows this error when running the Chrome version:
+
+> There was an error during the temporary add-on installation.
+> background.service_worker is currently disabled. Add background.scripts.
+
+Chrome shows this error when running the Firefox version:
+
+> 'background.scripts' requires manifest version of 2 or lower.
+> 'page_action' requires manifest version of 2 or lower.
+
+The `use_*_manifest.sh.bat` scripts in the [manifest](src/manifest/) directory may be used to switch between versions.
+
+
+Donate: https://liberapay.com/pmarks
diff --git a/misc/privacy_policy.txt b/misc/privacy_policy.txt
index 534a9fe..fcb7734 100644
--- a/misc/privacy_policy.txt
+++ b/misc/privacy_policy.txt
@@ -1,8 +1,15 @@
Privacy Policy
-IPvFoo monitors all of your web traffic, in order to present a table of
-connection information. This information is stored in local RAM, and is
-not transmitted over the network.
+IPvFoo has full access to your web traffic, as this is necessary to present
+the table of connection information. All information is kept in RAM on your
+computer, and never transmitted over the network.
-If you use the "Look up on bgp.he.net" feature, the selected domain name
-or IP address will be sent to Hurricane Electric via URL request.
+Technically there is one exception: if you use the "Look up on bgp.he.net"
+feature, your browser will navigate to a URL managed by Hurricane Electric,
+containing the selected domain name or IP address.
+
+
+My other Chrome extensions do not use personal information at all:
+- Incognito Proxy
+- Endless Whisper
+-
Line Wrapper
diff --git a/misc/screenshot_edge_toolbar_1280x800.png b/misc/screenshot_edge_toolbar_1280x800.png
new file mode 100644
index 0000000..f2de3dd
Binary files /dev/null and b/misc/screenshot_edge_toolbar_1280x800.png differ
diff --git a/misc/screenshot-firefox1.png b/misc/screenshot_firefox.png
similarity index 100%
rename from misc/screenshot-firefox1.png
rename to misc/screenshot_firefox.png
diff --git a/misc/screenshot_firefox_android.png b/misc/screenshot_firefox_android.png
new file mode 100644
index 0000000..013af3a
Binary files /dev/null and b/misc/screenshot_firefox_android.png differ
diff --git a/misc/screenshot_firefox_permission.png b/misc/screenshot_firefox_permission.png
new file mode 100644
index 0000000..d842736
Binary files /dev/null and b/misc/screenshot_firefox_permission.png differ
diff --git a/misc/screenshot_options.png b/misc/screenshot_options.png
new file mode 100644
index 0000000..a748a5d
Binary files /dev/null and b/misc/screenshot_options.png differ
diff --git a/misc/screenshot_webstore_1280x800.png b/misc/screenshot_webstore_1280x800.png
deleted file mode 100644
index 252b0ec..0000000
Binary files a/misc/screenshot_webstore_1280x800.png and /dev/null differ
diff --git a/misc/screenshot_webstore_1_1280x800.png b/misc/screenshot_webstore_1_1280x800.png
new file mode 100755
index 0000000..c6e2737
Binary files /dev/null and b/misc/screenshot_webstore_1_1280x800.png differ
diff --git a/misc/screenshot_webstore_1_640x400.png b/misc/screenshot_webstore_1_640x400.png
new file mode 100644
index 0000000..a8fad0f
Binary files /dev/null and b/misc/screenshot_webstore_1_640x400.png differ
diff --git a/misc/screenshot_webstore_2_1280x800.png b/misc/screenshot_webstore_2_1280x800.png
new file mode 100755
index 0000000..a5e8810
Binary files /dev/null and b/misc/screenshot_webstore_2_1280x800.png differ
diff --git a/misc/screenshot_webstore_640x400.png b/misc/screenshot_webstore_640x400.png
deleted file mode 100644
index e434239..0000000
Binary files a/misc/screenshot_webstore_640x400.png and /dev/null differ
diff --git a/src/1x1_808080.png b/src/1x1_808080.png
new file mode 100644
index 0000000..c918a96
Binary files /dev/null and b/src/1x1_808080.png differ
diff --git a/src/background.js b/src/background.js
index 2ceca40..607f194 100644
--- a/src/background.js
+++ b/src/background.js
@@ -35,133 +35,50 @@ Popup updates begin sooner, in wR.onBeforeRequest(main_frame), because the
user can demand a popup before any IP addresses are available.
*/
-// Returns an Object with no default properties.
-function newMap() {
- return Object.create(null);
-}
-
-// requestId -> {tabInfo, domain}
-const requestMap = newMap();
-
-// tabId -> TabInfo
-const tabMap = newMap();
-
-// Images from spritesXX.png: [x, y, w, h]
-const spriteBig = {
- "4": {16: [1, 1, 9, 14],
- 32: [1, 1, 21, 28]},
- "6": {16: [11, 1, 9, 14],
- 32: [23, 1, 21, 28]},
- "?": {16: [21, 1, 9, 14],
- 32: [45, 1, 21, 28]},
-};
-const spriteSmall = {
- "4": {16: [31, 1, 6, 6],
- 32: [67, 1, 10, 10]},
- "6": {16: [31, 8, 6, 6],
- 32: [67, 12, 10, 10]},
-};
+"use strict";
-// Destination coordinates: [x, y]
-const targetBig = {
- 16: [0, 1],
- 32: [0, 2],
-};
-const targetSmall1 = {
- 16: [10, 1],
- 32: [22, 2],
-};
-const targetSmall2 = {
- 16: [10, 8],
- 32: [22, 14],
-};
+if (chrome.runtime.getManifest().background.service_worker) {
+ // This only runs on Chrome.
+ // Firefox uses manifest.json/background/scripts instead.
+ importScripts("iputil.js", "common.js");
+}
// Possible states for an instance of TabInfo.
// We begin at BIRTH, and only ever move forward, not backward.
-const TAB_BIRTH = 0; // Waiting for TabTracker onConnect
-const TAB_ALIVE = 1; // Waiting for TabTracker onDisconnect
-const TAB_DELETED = 2; // Dead.
+const TAB_BIRTH = 0; // Waiting for makeAlive() or remove()
+const TAB_ALIVE = 1; // Waiting for remove()
+const TAB_DEAD = 2;
// RequestFilter for webRequest events.
const FILTER_ALL_URLS = { urls: [""] };
-// Whitelist IP address and domain name characters.
-const IP_CHARS = /^[0-9A-Fa-f:.]+$/;
+// Distinguish IP address and domain name characters.
+// Note that IP6_CHARS must not match "beef.de"
+const IP4_CHARS = /^[0-9.]+$/;
+const IP6_CHARS = /^[0-9A-Fa-f]*:[0-9A-Fa-f:.]*$/;
const DNS_CHARS = /^[0-9A-Za-z._-]+$/;
-// Load spriteXX.png of a particular size.
-// Executing this inline ensures that the images load before
-// firing the onload handler.
-function loadSpriteImg(size) {
- const s = document.createElement("img");
- s.src = "sprites" + size + ".png";
- return s;
-}
-const spriteImg = {
- 16: loadSpriteImg(16),
- 32: loadSpriteImg(32),
-};
+const SECONDS = 1000; // to milliseconds
-// Get a