Enhance: promisify chrome into browser, drop promisify (#866)
				
					
				
			* promisify `chrome` into `browser` * comment * comment * comment * Add: a naive browser polyfill * Fix: polyfill doesn't detect content script env correctly Co-authored-by: eight04 <eight04@gmail.com>
This commit is contained in:
		
							parent
							
								
									3d94c641b3
								
							
						
					
					
						commit
						54b1f218e0
					
				|  | @ -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}); | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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)) { | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 | ||||
|     }) | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -64,7 +64,6 @@ | |||
|     <script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script> | ||||
| 
 | ||||
|     <script src="js/polyfill.js"></script> | ||||
|     <script src="js/promisify.js"></script> | ||||
|     <script src="js/dom.js"></script> | ||||
|     <script src="js/messaging.js"></script> | ||||
|     <script src="js/prefs.js"></script> | ||||
|  |  | |||
|  | @ -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 ( | ||||
|  |  | |||
|  | @ -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, | ||||
|       }))); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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()]; | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ | |||
|   <link href="install-usercss/install-usercss.css" rel="stylesheet"> | ||||
| 
 | ||||
|   <script src="js/polyfill.js"></script> | ||||
|   <script src="js/promisify.js"></script> | ||||
|   <script src="js/msg.js"></script> | ||||
|   <script src="js/messaging.js"></script> | ||||
|   <script src="js/prefs.js"></script> | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										24
									
								
								js/msg.js
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								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) | ||||
|             ); | ||||
|  |  | |||
|  | @ -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) | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| })(); | ||||
|  |  | |||
							
								
								
									
										11
									
								
								js/prefs.js
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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); | ||||
|       }); | ||||
|     }); | ||||
|  |  | |||
|  | @ -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 | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -147,7 +147,6 @@ | |||
|   </template> | ||||
| 
 | ||||
|   <script src="js/polyfill.js"></script> | ||||
|   <script src="js/promisify.js"></script> | ||||
|   <script src="js/dom.js"></script> | ||||
|   <script src="js/messaging.js"></script> | ||||
|   <script src="js/prefs.js"></script> | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -21,7 +21,6 @@ | |||
| 
 | ||||
|   <script src="js/polyfill.js"></script> | ||||
|   <script src="js/dom.js"></script> | ||||
|   <script src="js/promisify.js"></script> | ||||
|   <script src="js/messaging.js"></script> | ||||
|   <script src="js/msg.js"></script> | ||||
|   <script src="js/localization.js"></script> | ||||
|  |  | |||
|  | @ -179,7 +179,6 @@ | |||
|   <script src="manage/config-dialog.js"></script> | ||||
| 
 | ||||
|   <script src="js/polyfill.js"></script> | ||||
|   <script src="js/promisify.js"></script> | ||||
|   <script src="js/dom.js"></script> | ||||
|   <script src="js/messaging.js"></script> | ||||
|   <script src="js/localization.js"></script> | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user