diff --git a/background.js b/background.js
new file mode 100644
index 0000000000000000000000000000000000000000..7403e34e0ad7680088e342cc0357be33f1d1ffa2
--- /dev/null
+++ b/background.js
@@ -0,0 +1,50 @@
+var unsafelinks = {
+
+    unsafelink: function(messageHeader) {
+        // Request the full message contents so we can do text replacement.
+        browser.messages.getFull(messageHeader.id).then(unsafelinks.replaceInAllMessageParts);
+    },
+
+    replaceInAllMessageParts(messageOrPart) {
+        // Iterate over each "part" of the message (i.e. text, html, etc.)
+        for (partId in messageOrPart.parts) {
+            part = messageOrPart.parts[partId];
+            // Handle this part, if it has a body.
+            if ('body' in part) {
+                unsafelinks.replace(part);
+            }
+            // Recursively handle sub-parts.
+            if ('parts' in part) {
+                unsafelinks.replaceInAllMessageParts(part);
+            }
+        }
+    },
+
+    replace: function(messagePart) {
+        // Replace each safelinks URL in the message body with the original URL.
+        messagePart.body = part.body.replace(unsafelinks.urlRegex, unsafelinks.replacer);
+        // TODO: This doesn't actually update the view!
+    },
+
+    // Regular expression matching a safelinks-encoded URL.
+    urlRegex: /https?:\/\/(?:.+?\.)?safelinks\.protection\.outlook\.com\/([A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=%]*)/gi,
+
+    replacer: function(url, queryString) {
+        // Extract the "url" parameter from the URL, if it exists.
+        var params = new URLSearchParams(queryString);
+        if (params.has('url')) {
+            return params.get('url');
+        } else {
+            return url;
+        }
+    },
+
+    reloadTab: function(tabId) {
+        browser.mailTabs.update(tabId, {});
+    }
+};
+
+browser.messageDisplay.onMessageDisplayed.addListener((tabId, message) => {
+    unsafelinks.unsafelink(message);
+    unsafelinks.reloadTab(tabId);
+});
diff --git a/chrome.manifest b/chrome.manifest
deleted file mode 100644
index 5b813cbf038d9f8a2f39a9cd0472f0a7ee3d6a61..0000000000000000000000000000000000000000
--- a/chrome.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-content     unsafelinks    chrome/content/
-overlay chrome://messenger/content/messenger.xul chrome://unsafelinks/content/unsafelinks.xul
\ No newline at end of file
diff --git a/chrome/content/unsafelinks.js b/chrome/content/unsafelinks.js
deleted file mode 100644
index b0fd8dc74364891e63824c41314700f9de18e7ab..0000000000000000000000000000000000000000
--- a/chrome/content/unsafelinks.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Listen for the "window loaded" event and initialize.
-window.addEventListener("load", function load(event) {
-    window.removeEventListener("load", load, false);
-    unsafelinks.init();
-}, false);
-
-var unsafelinks = {
-    init: function() {
-        // Listen for the "message loaded" event, if this is a message window.
-        var messagepane = document.getElementById("messagepane");
-        if (messagepane) {
-            messagepane.addEventListener("load", unsafelinks.onMessageLoad, true);
-        }
-    },
-
-    onMessageLoad: function(event) {
-        // Replace each safelinks URL in the message body with the original URL.
-        var body = event.originalTarget.body;
-        body.innerHTML = body.innerHTML.replace(unsafelinks.urlRegex, unsafelinks.replacer);
-    },
-
-    // Regular expression matching a safelinks-encoded URL.
-    urlRegex: /https?:\/\/(?:.+?\.)?safelinks\.protection\.outlook\.com\/([A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=%]*)/gi,
-
-    replacer: function(url, queryString){
-        // Extract the "url" parameter from the URL, if it exists.
-        var params = new URLSearchParams(queryString);
-        if (params.has('url')) {
-            return params.get('url');
-        } else {
-            return url;
-        }
-    },
-};
diff --git a/chrome/content/unsafelinks.xul b/chrome/content/unsafelinks.xul
deleted file mode 100644
index b0d6e60c388f20324cefe68af6845332fd90d604..0000000000000000000000000000000000000000
--- a/chrome/content/unsafelinks.xul
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0"?>
-<overlay id="unsafelinks" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-<script type="application/javascript" src="chrome://unsafelinks/content/unsafelinks.js"/>
-</overlay>
diff --git a/icon.png b/icon.png
index a54dd9498002d8d1ed7bf5abe171c167a21b7787..e790a17eb4ceb7b07ceb405f9953d39a5031322c 100644
Binary files a/icon.png and b/icon.png differ
diff --git a/icon64.png b/icon64.png
deleted file mode 100644
index e790a17eb4ceb7b07ceb405f9953d39a5031322c..0000000000000000000000000000000000000000
Binary files a/icon64.png and /dev/null differ
diff --git a/install.rdf b/install.rdf
deleted file mode 100644
index e8ff0c803a17a08637134a85f1462e69dad207af..0000000000000000000000000000000000000000
--- a/install.rdf
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0"?>
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>unsafelinks@ant.sr</em:id>
-    <em:name>Un-Safelinks</em:name>
-    <em:description>Replace URLs which have been rewritten by Office 365 Advanced Threat Protection with the original URL.</em:description>
-    <em:version>1.2</em:version>
-	<em:homepageURL>https://ant.sr/unsafelinks</em:homepageURL>
-    <em:targetApplication>
-      <Description>
-        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
-        <em:minVersion>45.8.0</em:minVersion>
-        <em:maxVersion>60.*</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-  </Description>
-</RDF>
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..e7dde0ab70dd17ea3745e2033dcd243ee38ba63d
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,25 @@
+{
+    "manifest_version": 2,
+    "name": "Un-Safelinks",
+    "description": "Replace URLs which have been rewritten by Office 365 Advanced Threat Protection with the original URL.",
+    "version": "1.3",
+    "author": "Anton Sarukhanov",
+    "author": "Anton Sarukhanov",
+    "applications": {
+        "gecko": {
+            "id": "unsafelinks@ant.sr",
+            "strict_min_version": "66.0"
+        }
+    },
+    "permissions": [
+        "messagesRead"
+    ],
+    "background": {
+        "scripts": [
+            "background.js"
+        ]
+    },
+    "icons": {
+        "64": "icon.png"
+    }
+}