diff --git a/options.js b/options.js index 57a2c00..782a93c 100644 --- a/options.js +++ b/options.js @@ -1,9 +1,156 @@ // Copyright 2017; Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0 if (typeof globalThis.browser === "undefined" && typeof chrome !== "undefined") { - globalThis.browser = chrome; + globalThis.browser = { + ...chrome, + + runtime: { + ...chrome.runtime, + + sendMessage: function(message) { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage(message, response => { + const err = chrome.runtime.lastError; + + // Chrome sets lastError when a message intentionally has no direct response. + // Firefox-style Nazeka code often uses async side-channel replies instead. + if (err) { + console.warn("Nazeka sendMessage warning:", err.message); + resolve(undefined); + } else { + resolve(response); + } + }); + }); + }, + + getPlatformInfo: function() { + return new Promise((resolve, reject) => { + chrome.runtime.getPlatformInfo(info => { + const err = chrome.runtime.lastError; + if (err) reject(err); + else resolve(info); + }); + }); + }, + + getURL: chrome.runtime.getURL.bind(chrome.runtime), + + onMessage: { + addListener: function(listener) { + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + try { + const result = listener(message, sender); + + if (result && typeof result.then === "function") { + result.then(sendResponse).catch(error => { + console.error("Nazeka message listener error:", error); + sendResponse(undefined); + }); + return true; + } + + if (result !== undefined) { + sendResponse(result); + } + + return false; + } catch (error) { + console.error("Nazeka message listener exception:", error); + sendResponse(undefined); + return false; + } + }); + } + } + }, + + storage: { + ...chrome.storage, + + local: { + ...chrome.storage.local, + + get: function(keys) { + return new Promise((resolve, reject) => { + chrome.storage.local.get(keys, result => { + const err = chrome.runtime.lastError; + if (err) reject(err); + else resolve(result); + }); + }); + }, + + set: function(items) { + return new Promise((resolve, reject) => { + chrome.storage.local.set(items, () => { + const err = chrome.runtime.lastError; + if (err) reject(err); + else resolve(); + }); + }); + }, + + remove: function(keys) { + return new Promise((resolve, reject) => { + chrome.storage.local.remove(keys, () => { + const err = chrome.runtime.lastError; + if (err) reject(err); + else resolve(); + }); + }); + } + }, + + onChanged: chrome.storage.onChanged + }, + + tabs: chrome.tabs ? { + ...chrome.tabs, + + getCurrent: function() { + return new Promise((resolve, reject) => { + chrome.tabs.getCurrent(tab => { + const err = chrome.runtime.lastError; + if (err) reject(err); + else resolve(tab); + }); + }); + }, + + sendMessage: function(tabId, message) { + return new Promise((resolve, reject) => { + chrome.tabs.sendMessage(tabId, message, response => { + const err = chrome.runtime.lastError; + if (err) { + console.warn("Nazeka tabs.sendMessage warning:", err.message); + resolve(undefined); + } else { + resolve(response); + } + }); + }); + }, + + create: chrome.tabs.create.bind(chrome.tabs) +} : undefined, + + windows: chrome.windows ? { + ...chrome.windows, + create: chrome.windows.create.bind(chrome.windows) +} : undefined, + + extension: { + ...chrome.extension, + getURL: chrome.runtime.getURL.bind(chrome.runtime) + }, + + browserAction: chrome.browserAction || undefined, +contextMenus: chrome.contextMenus || undefined + }; } + function storageGet(key) { return new Promise((resolve, reject) => { browser.storage.local.get(key, result => {