diff --git a/background/background.js b/background/background.js
index 6d6ea071..77034880 100644
--- a/background/background.js
+++ b/background/background.js
@@ -1,7 +1,7 @@
 /* global download prefs openURL FIREFOX CHROME
   URLS ignoreChromeError usercssHelper
   styleManager msg navigatorUtil workerUtil contentScripts sync
-  findExistingTab createTab activateTab isTabReplaceable getActiveTab
+  findExistingTab activateTab isTabReplaceable getActiveTab
   tabManager */
 
 'use strict';
@@ -336,7 +336,7 @@ function openManage({options = false, search} = {}) {
         if (isTabReplaceable(tab, url)) {
           return activateTab(tab, {url});
         }
-        return createTab({url});
+        return browser.tabs.create({url});
       });
     });
 }
diff --git a/background/content-scripts.js b/background/content-scripts.js
index 4be73294..dfd744f2 100644
--- a/background/content-scripts.js
+++ b/background/content-scripts.js
@@ -1,4 +1,4 @@
-/* global msg queryTabs ignoreChromeError URLS */
+/* global msg ignoreChromeError URLS */
 /* exported contentScripts */
 'use strict';
 
@@ -55,7 +55,7 @@ const contentScripts = (() => {
   }
 
   function injectToAllTabs() {
-    return queryTabs({}).then(tabs => {
+    return browser.tabs.query({}).then(tabs => {
       for (const tab of tabs) {
         // skip unloaded/discarded/chrome tabs
         if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue;
diff --git a/background/db-chrome-storage.js b/background/db-chrome-storage.js
index 46cfadb1..01e38262 100644
--- a/background/db-chrome-storage.js
+++ b/background/db-chrome-storage.js
@@ -1,46 +1,29 @@
-/* global promisify */
+/* global chromeLocal */
 /* exported createChromeStorageDB */
 'use strict';
 
 function createChromeStorageDB() {
-  const get = promisify(chrome.storage.local.get.bind(chrome.storage.local));
-  const set = promisify(chrome.storage.local.set.bind(chrome.storage.local));
-  const remove = promisify(chrome.storage.local.remove.bind(chrome.storage.local));
-
   let INC;
 
   const PREFIX = 'style-';
   const METHODS = {
     // FIXME: we don't use this method at all. Should we remove this?
-    get: id => get(PREFIX + id)
-      .then(result => result[PREFIX + id]),
-    put: obj => Promise.resolve()
-      .then(() => {
-        if (!obj.id) {
-          return prepareInc()
-            .then(() => {
-              // FIXME: should we clone the object?
-              obj.id = INC++;
-            });
-        }
-      })
-      .then(() => set({[PREFIX + obj.id]: obj}))
-      .then(() => obj.id),
+    get: id => chromeLocal.getValue(PREFIX + id),
+    put: obj =>
+      // FIXME: should we clone the object?
+      Promise.resolve(!obj.id && prepareInc().then(() => Object.assign(obj, {id: INC++})))
+        .then(() => chromeLocal.setValue(PREFIX + obj.id, obj))
+        .then(() => obj.id),
     putMany: items => prepareInc()
-      .then(() => {
-        for (const item of items) {
-          if (!item.id) {
-            item.id = INC++;
-          }
-        }
-        return set(items.reduce((obj, curr) => {
-          obj[PREFIX + curr.id] = curr;
-          return obj;
-        }, {}));
-      })
+      .then(() =>
+        chromeLocal.set(items.reduce((data, item) => {
+          if (!item.id) item.id = INC++;
+          data[PREFIX + item.id] = item;
+          return data;
+        }, {})))
       .then(() => items.map(i => i.id)),
-    delete: id => remove(PREFIX + id),
-    getAll: () => get(null)
+    delete: id => chromeLocal.remove(PREFIX + id),
+    getAll: () => chromeLocal.get()
       .then(result => {
         const output = [];
         for (const key in result) {
@@ -69,7 +52,7 @@ function createChromeStorageDB() {
 
   function prepareInc() {
     if (INC) return Promise.resolve();
-    return get(null).then(result => {
+    return chromeLocal.get().then(result => {
       INC = 1;
       for (const key in result) {
         if (key.startsWith(PREFIX)) {
diff --git a/background/navigator-util.js b/background/navigator-util.js
index ab08dffa..67fdc1e7 100644
--- a/background/navigator-util.js
+++ b/background/navigator-util.js
@@ -1,4 +1,4 @@
-/* global promisify CHROME URLS */
+/* global CHROME URLS */
 /* exported navigatorUtil */
 'use strict';
 
@@ -6,7 +6,6 @@ const navigatorUtil = (() => {
   const handler = {
     urlChange: null
   };
-  const tabGet = promisify(chrome.tabs.get.bind(chrome.tabs));
   return extendNative({onUrlChange});
 
   function onUrlChange(fn) {
@@ -48,7 +47,7 @@ const navigatorUtil = (() => {
     ) {
       return Promise.resolve();
     }
-    return tabGet(data.tabId)
+    return browser.tabs.get(data.tabId)
       .then(tab => {
         if (tab.url === 'chrome://newtab/') {
           data.url = tab.url;
diff --git a/background/token-manager.js b/background/token-manager.js
index 0b0fd3e0..8be21874 100644
--- a/background/token-manager.js
+++ b/background/token-manager.js
@@ -1,9 +1,11 @@
-/* global chromeLocal promisify FIREFOX */
+/* global chromeLocal promisifyChrome FIREFOX */
 /* exported tokenManager */
 'use strict';
 
 const tokenManager = (() => {
-  const launchWebAuthFlow = promisify(chrome.identity.launchWebAuthFlow.bind(chrome.identity));
+  promisifyChrome({
+    identity: ['launchWebAuthFlow'],
+  });
   const AUTH = {
     dropbox: {
       flow: 'token',
@@ -158,7 +160,7 @@ const tokenManager = (() => {
       Object.assign(query, provider.authQuery);
     }
     const url = `${provider.authURL}?${stringifyQuery(query)}`;
-    return launchWebAuthFlow({
+    return browser.identity.launchWebAuthFlow({
       url,
       interactive
     })
diff --git a/background/usercss-helper.js b/background/usercss-helper.js
index ea083ae9..3f6081f6 100644
--- a/background/usercss-helper.js
+++ b/background/usercss-helper.js
@@ -1,8 +1,7 @@
-/* global API_METHODS usercss styleManager deepCopy openURL download URLS getTab */
-/* exports usercssHelper */
+/* global API_METHODS usercss styleManager deepCopy openURL download URLS */
+/* exported usercssHelper */
 'use strict';
 
-// eslint-disable-next-line no-unused-vars
 const usercssHelper = (() => {
   const installCodeCache = {};
   const clearInstallCode = url => delete installCodeCache[url];
@@ -46,7 +45,7 @@ const usercssHelper = (() => {
     openInstallerPage(tabId, url, {code, inTab} = {}) {
       const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
       if (inTab) {
-        getTab(tabId).then(tab =>
+        browser.tabs.get(tabId).then(tab =>
           openURL({
             url: `${newUrl}&tabId=${tabId}`,
             active: tab.active,
diff --git a/edit.html b/edit.html
index 72a2c45c..12614c99 100644
--- a/edit.html
+++ b/edit.html
@@ -64,7 +64,6 @@
     
 
     
-    
     
     
     
diff --git a/edit/edit.js b/edit/edit.js
index 4c5d098d..7d3b49d4 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -1,5 +1,5 @@
 /* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
-  createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch
+  createSourceEditor sessionStorageHash getOwnTab FIREFOX API tryCatch
   closeCurrentTab messageBox debounce workerUtil
   initBeautifyButton ignoreChromeError
   moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
@@ -226,7 +226,7 @@ function preinit() {
   }).observe(document, {subtree: true, childList: true});
 
   if (chrome.windows) {
-    queryTabs({currentWindow: true}).then(tabs => {
+    browser.tabs.query({currentWindow: true}).then(tabs => {
       const windowId = tabs[0].windowId;
       if (prefs.get('openEditInWindow')) {
         if (
diff --git a/edit/global-search.js b/edit/global-search.js
index 349b8ac1..ad46a4cc 100644
--- a/edit/global-search.js
+++ b/edit/global-search.js
@@ -1,4 +1,4 @@
-/* global CodeMirror focusAccessibility colorMimicry editor
+/* global CodeMirror focusAccessibility colorMimicry editor chromeLocal
   onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */
 'use strict';
 
@@ -915,7 +915,7 @@ onDOMready().then(() => {
 
 
   function readStorage() {
-    chrome.storage.local.get('editor', ({editor = {}}) => {
+    chromeLocal.getValue('editor').then((editor = {}) => {
       state.find = editor.find || '';
       state.replace = editor.replace || '';
       state.icase = editor.icase || state.icase;
@@ -924,14 +924,12 @@ onDOMready().then(() => {
 
 
   function writeStorage() {
-    chrome.storage.local.get('editor', ({editor}) =>
-      chrome.storage.local.set({
-        editor: Object.assign(editor || {}, {
-          find: state.find,
-          replace: state.replace,
-          icase: state.icase,
-        })
-      }));
+    chromeLocal.getValue('editor').then((editor = {}) =>
+      chromeLocal.setValue('editor', Object.assign(editor, {
+        find: state.find,
+        replace: state.replace,
+        icase: state.icase,
+      })));
   }
 
 
diff --git a/edit/regexp-tester.js b/edit/regexp-tester.js
index 590714d3..8f001102 100644
--- a/edit/regexp-tester.js
+++ b/edit/regexp-tester.js
@@ -1,4 +1,4 @@
-/* global showHelp $ $create tryRegExp queryTabs URLS t template openURL */
+/* global showHelp $ $create tryRegExp URLS t template openURL */
 /* exported regExpTester */
 'use strict';
 
@@ -66,7 +66,7 @@ const regExpTester = (() => {
       return rxData;
     });
     const getMatchInfo = m => m && {text: m[0], pos: m.index};
-    queryTabs({}).then(tabs => {
+    browser.tabs.query({}).then(tabs => {
       const supported = tabs.map(tab => tab.url)
         .filter(url => URLS.supported(url));
       const unique = [...new Set(supported).values()];
diff --git a/install-usercss.html b/install-usercss.html
index d168a346..9083ad31 100644
--- a/install-usercss.html
+++ b/install-usercss.html
@@ -10,7 +10,6 @@
   
 
   
-  
   
   
   
diff --git a/install-usercss/install-usercss.js b/install-usercss/install-usercss.js
index c4417571..82bb8b73 100644
--- a/install-usercss/install-usercss.js
+++ b/install-usercss/install-usercss.js
@@ -394,13 +394,9 @@
         }
       });
       port.onDisconnect.addListener(() => {
-        chrome.tabs.get(tabId, tab => {
-          if (chrome.runtime.lastError) {
-            closeCurrentTab();
-          } else if (tab.url === initialUrl) {
-            location.reload();
-          }
-        });
+        browser.tabs.get(tabId)
+          .then(tab => tab.url === initialUrl && location.reload())
+          .catch(closeCurrentTab);
       });
       return ({timer = true} = {}) => new Promise((resolve, reject) => {
         const id = performance.now();
diff --git a/js/messaging.js b/js/messaging.js
index 82f009a5..642b20dc 100644
--- a/js/messaging.js
+++ b/js/messaging.js
@@ -1,7 +1,7 @@
 /* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError
   getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
   closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
-/* global promisify */
+/* global promisifyChrome */
 'use strict';
 
 const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(\d+)|$/)[1]);
@@ -93,33 +93,20 @@ if (IS_BG) {
 // Object.defineProperty(window, 'localStorage', {value: {}});
 // Object.defineProperty(window, 'sessionStorage', {value: {}});
 
-const createTab = promisify(chrome.tabs.create.bind(chrome.tabs));
-const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs));
-const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs));
-const moveTabs = promisify(chrome.tabs.move.bind(chrome.tabs));
-
-// Android doesn't have chrome.windows
-const updateWindow = chrome.windows && promisify(chrome.windows.update.bind(chrome.windows));
-const createWindow = chrome.windows && promisify(chrome.windows.create.bind(chrome.windows));
+promisifyChrome({
+  tabs: ['create', 'get', 'getCurrent', 'move', 'query', 'update'],
+  windows: ['create', 'update'], // Android doesn't have chrome.windows
+});
 // FF57+ supports openerTabId, but not in Android
 // (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
 const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
 
-function getTab(id) {
-  return new Promise(resolve =>
-    chrome.tabs.get(id, tab =>
-      !chrome.runtime.lastError && resolve(tab)));
-}
-
-
 function getOwnTab() {
-  return new Promise(resolve =>
-    chrome.tabs.getCurrent(tab => resolve(tab)));
+  return browser.tabs.getCurrent();
 }
 
-
 function getActiveTab() {
-  return queryTabs({currentWindow: true, active: true})
+  return browser.tabs.query({currentWindow: true, active: true})
     .then(tabs => tabs[0]);
 }
 
@@ -140,7 +127,7 @@ function urlToMatchPattern(url, ignoreSearch) {
 
 function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
   url = new URL(url);
-  return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
+  return browser.tabs.query({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
     // FIXME: is tab.url always normalized?
     .then(tabs => tabs.find(matchTab));
 
@@ -191,8 +178,8 @@ function openURL({
         url: url !== tab.url && url.includes('#') ? url : undefined,
       });
     }
-    if (newWindow && createWindow) {
-      return createWindow(Object.assign({url}, windowPosition))
+    if (newWindow && browser.windows) {
+      return browser.windows.create(Object.assign({url}, windowPosition))
         .then(wnd => wnd.tabs[0]);
     }
     return getActiveTab().then((activeTab = {url: ''}) =>
@@ -205,7 +192,7 @@ function openURL({
     if (id != null && !openerTab.incognito && openerTabIdSupported) {
       options.openerTabId = id;
     }
-    return createTab(options);
+    return browser.tabs.create(options);
   }
 }
 
@@ -232,9 +219,9 @@ function activateTab(tab, {url, index, openerTabId} = {}) {
     options.openerTabId = openerTabId;
   }
   return Promise.all([
-    updateTab(tab.id, options),
-    updateWindow && updateWindow(tab.windowId, {focused: true}),
-    index != null && moveTabs(tab.id, {index})
+    browser.tabs.update(tab.id, options),
+    browser.windows && browser.windows.update(tab.windowId, {focused: true}),
+    index != null && browser.tabs.move(tab.id, {index})
   ])
     .then(() => tab);
 }
diff --git a/js/msg.js b/js/msg.js
index f7e6bafd..d52438e4 100644
--- a/js/msg.js
+++ b/js/msg.js
@@ -1,12 +1,12 @@
-/* global promisify deepCopy */
+/* global promisifyChrome deepCopy */
 // deepCopy is only used if the script is executed in extension pages.
 'use strict';
 
 self.msg = self.INJECTED === 1 ? self.msg : (() => {
-  const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime));
-  const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
-  const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs));
-
+  promisifyChrome({
+    runtime: ['sendMessage'],
+    tabs: ['sendMessage', 'query'],
+  });
   const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
   if (isBg) {
     window._msg = {
@@ -49,19 +49,21 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
       }
     }
     if (chrome.runtime.getBackgroundPage) {
-      return promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))()
-        .catch(() => null);
+      promisifyChrome({
+        runtime: ['getBackgroundPage'],
+      });
+      return browser.runtime.getBackgroundPage().catch(() => null);
     }
     return Promise.resolve(null);
   }
 
   function send(data, target = 'extension') {
     const message = {data, target};
-    return runtimeSend(message).then(unwrapData);
+    return browser.runtime.sendMessage(message).then(unwrapData);
   }
 
   function sendTab(tabId, data, options, target = 'tab') {
-    return tabSend(tabId, {data, target}, options)
+    return browser.tabs.sendMessage(tabId, {data, target}, options)
       .then(unwrapData);
   }
 
@@ -99,7 +101,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
   }
 
   function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
-    return tabQuery({})
+    return browser.tabs.query({})
       // TODO: send to activated tabs first?
       .then(tabs => {
         const requests = [];
@@ -123,7 +125,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
           const message = {data: dataObj, target};
           if (tab && tab.id) {
             requests.push(
-              tabSend(tab.id, message, options)
+              browser.tabs.sendMessage(tab.id, message, options)
                 .then(unwrapData)
                 .catch(ignoreError)
             );
diff --git a/js/polyfill.js b/js/polyfill.js
index 2b95b10d..3b22c96d 100644
--- a/js/polyfill.js
+++ b/js/polyfill.js
@@ -3,6 +3,8 @@
 // eslint-disable-next-line no-unused-expressions
 self.INJECTED !== 1 && (() => {
 
+  // this part runs in workers, content scripts, our extension pages
+
   if (!Object.entries) {
     Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]);
   }
@@ -10,9 +12,38 @@ self.INJECTED !== 1 && (() => {
     Object.values = obj => Object.keys(obj).map(k => obj[k]);
   }
 
-  // the above was shared by content scripts and workers,
-  // the rest is only needed for our extension pages
-  if (!self.chrome || !self.chrome.tabs) return;
+  // don't use self.chrome. It is undefined in Firefox
+  if (typeof chrome !== 'object') return;
+  // the rest is for content scripts and our extension pages
+
+  self.browser = polyfillBrowser();
+
+  /* Promisifies the specified `chrome` methods into `browser`.
+    The definitions is an object like this: {
+      'storage.sync': ['get', 'set'], // if deeper than one level, combine the path via `.`
+      windows: ['create', 'update'], // items and sub-objects will only be created if present in `chrome`
+    } */
+  self.promisifyChrome = definitions => {
+    for (const [scopeName, methods] of Object.entries(definitions)) {
+      const path = scopeName.split('.');
+      const src = path.reduce((obj, p) => obj && obj[p], chrome);
+      if (!src) continue;
+      const dst = path.reduce((obj, p) => obj[p] || (obj[p] = {}), browser);
+      for (const name of methods) {
+        const fn = src[name];
+        if (!fn || dst[name] && !dst[name].isTrap) continue;
+        dst[name] = (...args) => new Promise((resolve, reject) =>
+          fn.call(src, ...args, (...results) =>
+            chrome.runtime.lastError ?
+              reject(chrome.runtime.lastError) :
+              resolve(results.length <= 1 ? results[0] : results)));
+              // a couple of callbacks have 2 parameters (we don't use those methods, but just in case)
+      }
+    }
+  };
+
+  if (!chrome.tabs) return;
+  // the rest is for our extension pages
 
   if (typeof document === 'object') {
     const ELEMENT_METH = {
@@ -75,4 +106,27 @@ self.INJECTED !== 1 && (() => {
   } catch (err) {
     Object.defineProperty(self, 'sessionStorage', {value: {}});
   }
+
+  function polyfillBrowser() {
+    if (typeof browser === 'object' && browser.runtime) {
+      return browser;
+    }
+    return createTrap(chrome, null);
+
+    function createTrap(base, parent) {
+      const target = typeof base === 'function' ? () => {} : {};
+      target.isTrap = true;
+      return new Proxy(target, {
+        get: (target, prop) => {
+          if (target[prop]) return target[prop];
+          if (base[prop] && (typeof base[prop] === 'object' || typeof base[prop] === 'function')) {
+            target[prop] = createTrap(base[prop], base);
+            return target[prop];
+          }
+          return base[prop];
+        },
+        apply: (target, thisArg, args) => base.apply(parent, args)
+      });
+    }
+  }
 })();
diff --git a/js/prefs.js b/js/prefs.js
index de4aed43..52e2ad7a 100644
--- a/js/prefs.js
+++ b/js/prefs.js
@@ -1,4 +1,4 @@
-/* global promisify */
+/* global promisifyChrome */
 'use strict';
 
 self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
@@ -107,10 +107,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
     specific: new Map(),
   };
 
-  const syncSet = promisify(chrome.storage.sync.set.bind(chrome.storage.sync));
-  const syncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync));
+  promisifyChrome({
+    'storage.sync': ['get', 'set'],
+  });
 
-  const initializing = syncGet('settings')
+  const initializing = browser.storage.sync.get('settings')
     .then(result => {
       if (result.settings) {
         setAll(result.settings, true);
@@ -237,7 +238,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
     return new Promise((resolve, reject) => {
       setTimeout(() => {
         timer = null;
-        syncSet({settings: values})
+        browser.storage.sync.set({settings: values})
           .then(resolve, reject);
       });
     });
diff --git a/js/promisify.js b/js/promisify.js
deleted file mode 100644
index 89605a37..00000000
--- a/js/promisify.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-/*
-Convert chrome APIs into promises. Example:
-
-  const storageSyncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync));
-  storageSyncGet(['key']).then(result => {...});
-
-*/
-self.promisify = self.INJECTED === 1 ? self.promisify : fn =>
-  (...args) =>
-    new Promise((resolve, reject) => {
-      fn(...args, (...result) => {
-        if (chrome.runtime.lastError) {
-          reject(chrome.runtime.lastError);
-          return;
-        }
-        resolve(
-          result.length === 0 ? undefined :
-          result.length === 1 ? result[0] : result
-        );
-      });
-    });
diff --git a/js/storage-util.js b/js/storage-util.js
index 044ba56a..2e966523 100644
--- a/js/storage-util.js
+++ b/js/storage-util.js
@@ -1,7 +1,12 @@
-/* global loadScript tryJSONparse */
+/* global loadScript tryJSONparse promisifyChrome */
 /* exported chromeLocal chromeSync */
 'use strict';
 
+promisifyChrome({
+  'storage.local': ['get', 'remove', 'set'],
+  'storage.sync': ['get', 'remove', 'set'],
+});
+
 const [chromeLocal, chromeSync] = (() => {
   return [
     createWrapper('local'),
@@ -9,11 +14,11 @@ const [chromeLocal, chromeSync] = (() => {
   ];
 
   function createWrapper(name) {
-    const storage = chrome.storage[name];
+    const storage = browser.storage[name];
     const wrapper = {
-      get: data => new Promise(resolve => storage.get(data, resolve)),
-      set: data => new Promise(resolve => storage.set(data, () => resolve(data))),
-      remove: data => new Promise(resolve => storage.remove(data, resolve)),
+      get: storage.get.bind(storage),
+      set: data => storage.set(data).then(() => data),
+      remove: storage.remove.bind(storage),
 
       /**
        * @param {String} key
diff --git a/manage.html b/manage.html
index 76e0c7ce..d168af14 100644
--- a/manage.html
+++ b/manage.html
@@ -147,7 +147,6 @@
   
 
   
-  
   
   
   
diff --git a/manifest.json b/manifest.json
index 5ad60993..89a62be7 100644
--- a/manifest.json
+++ b/manifest.json
@@ -26,7 +26,6 @@
   "background": {
     "scripts": [
       "js/polyfill.js",
-      "js/promisify.js",
       "js/messaging.js",
       "js/msg.js",
       "js/storage-util.js",
@@ -77,7 +76,6 @@
       "match_about_blank": true,
       "js": [
         "js/polyfill.js",
-        "js/promisify.js",
         "js/msg.js",
         "js/prefs.js",
         "content/style-injector.js",
diff --git a/options.html b/options.html
index 0a02e415..6ea9a78b 100644
--- a/options.html
+++ b/options.html
@@ -21,7 +21,6 @@
 
   
   
-  
   
   
   
diff --git a/popup.html b/popup.html
index fd80f988..41cd7852 100644
--- a/popup.html
+++ b/popup.html
@@ -179,7 +179,6 @@
   
 
   
-  
   
   
   
diff --git a/popup/search-results.js b/popup/search-results.js
index c22a7ae7..d46982ac 100755
--- a/popup/search-results.js
+++ b/popup/search-results.js
@@ -1,6 +1,6 @@
 /* global tabURL handleEvent $ $$ prefs template FIREFOX chromeLocal debounce
   $create t API tWordBreak formatDate tryCatch tryJSONparse LZString
-  ignoreChromeError download */
+  promisifyChrome download */
 'use strict';
 
 window.addEventListener('showStyles:done', function _() {
@@ -88,6 +88,9 @@ window.addEventListener('showStyles:done', function _() {
   return;
 
   function init() {
+    promisifyChrome({
+      'storage.local': ['getBytesInUse'], // FF doesn't implement it
+    });
     setTimeout(() => document.body.classList.add(BODY_CLASS));
 
     $('#find-styles-inline-group').classList.add('hidden');
@@ -711,7 +714,7 @@ window.addEventListener('showStyles:done', function _() {
         return chromeLocal.loadLZStringScript().then(() =>
           tryJSONparse(LZString.decompressFromUTF16(item.payload)));
       } else if (item) {
-        chrome.storage.local.remove(key);
+        chromeLocal.remove(key);
       }
     });
   }
@@ -742,16 +745,8 @@ window.addEventListener('showStyles:done', function _() {
 
   function cleanupCache() {
     chromeLocal.remove(CACHE_CLEANUP_NEEDED);
-    if (chrome.storage.local.getBytesInUse) {
-      chrome.storage.local.getBytesInUse(null, size => {
-        if (size > CACHE_SIZE) {
-          chrome.storage.local.get(null, cleanupCacheInternal);
-        }
-        ignoreChromeError();
-      });
-    } else {
-      chrome.storage.local.get(null, cleanupCacheInternal);
-    }
+    Promise.resolve(!browser.storage.local.getBytesInUse ? 1e99 : browser.storage.local.getBytesInUse())
+      .then(size => size > CACHE_SIZE && chromeLocal.get().then(cleanupCacheInternal));
   }
 
   function cleanupCacheInternal(storage) {
@@ -764,9 +759,8 @@ window.addEventListener('showStyles:done', function _() {
       sortedByTime.slice(0, sortedByTime.length / 2);
     const toRemove = expired.length ? expired : sortedByTime;
     if (toRemove.length) {
-      chrome.storage.local.remove(toRemove.map(item => item.key), ignoreChromeError);
+      chromeLocal.remove(toRemove.map(item => item.key));
     }
-    ignoreChromeError();
   }
 
   //endregion