chromeExtensionAsync icon indicating copy to clipboard operation
chromeExtensionAsync copied to clipboard

Suggestion: identify exhaustive whitelist of promisable functions

Open dlh3 opened this issue 5 years ago • 2 comments

@KeithHenry I noticed in a comment from last year that you didn't have a way to reliably detect the promiseable functions in the chrome API.

I took a stab at generating something that might help.

I started by collecting all documented permissions:

Array.from(
    document.querySelector("table:not([class])")
        .children[0]
        .children)
            .map(row => row.id)
            .join('", "');

I added those to an extension, then executed this from the background:

apis = {};
Object.keys(chrome)
  .filter(k => typeof chrome[k] === "object")
  .forEach(k => 
      Object.keys(chrome[k])
        .filter(j => typeof chrome[k][j] === "function")
        .forEach(j => {
            try {
                // trollolololol, or pass an unlikely number of arguments
                chrome[k][j](undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
            } catch (err) {
                if (err.message.includes("No matching signature.") && err.message.includes("function callback")) {
                    apis[k] = apis[k] || {};
                    apis[k][j] = `chrome.${k}.${j}`;
                }
            }
        }));
console.log(apis);
JSON.stringify(apis);

And that yielded this:

{
    "alarms": {
        "clear": "chrome.alarms.clear",
        "clearAll": "chrome.alarms.clearAll",
        "get": "chrome.alarms.get",
        "getAll": "chrome.alarms.getAll"
    },
    "bookmarks": {
        "create": "chrome.bookmarks.create",
        "get": "chrome.bookmarks.get",
        "getChildren": "chrome.bookmarks.getChildren",
        "getRecent": "chrome.bookmarks.getRecent",
        "getSubTree": "chrome.bookmarks.getSubTree",
        "getTree": "chrome.bookmarks.getTree",
        "move": "chrome.bookmarks.move",
        "remove": "chrome.bookmarks.remove",
        "removeTree": "chrome.bookmarks.removeTree",
        "search": "chrome.bookmarks.search",
        "update": "chrome.bookmarks.update"
    },
    "browserAction": {
        "disable": "chrome.browserAction.disable",
        "enable": "chrome.browserAction.enable",
        "getBadgeBackgroundColor": "chrome.browserAction.getBadgeBackgroundColor",
        "getBadgeText": "chrome.browserAction.getBadgeText",
        "getPopup": "chrome.browserAction.getPopup",
        "getTitle": "chrome.browserAction.getTitle",
        "setBadgeBackgroundColor": "chrome.browserAction.setBadgeBackgroundColor",
        "setBadgeText": "chrome.browserAction.setBadgeText",
        "setIcon": "chrome.browserAction.setIcon",
        "setPopup": "chrome.browserAction.setPopup",
        "setTitle": "chrome.browserAction.setTitle"
    },
    "browsingData": {
        "remove": "chrome.browsingData.remove",
        "removeAppcache": "chrome.browsingData.removeAppcache",
        "removeCache": "chrome.browsingData.removeCache",
        "removeCacheStorage": "chrome.browsingData.removeCacheStorage",
        "removeCookies": "chrome.browsingData.removeCookies",
        "removeDownloads": "chrome.browsingData.removeDownloads",
        "removeFileSystems": "chrome.browsingData.removeFileSystems",
        "removeFormData": "chrome.browsingData.removeFormData",
        "removeHistory": "chrome.browsingData.removeHistory",
        "removeIndexedDB": "chrome.browsingData.removeIndexedDB",
        "removeLocalStorage": "chrome.browsingData.removeLocalStorage",
        "removePasswords": "chrome.browsingData.removePasswords",
        "removePluginData": "chrome.browsingData.removePluginData",
        "removeServiceWorkers": "chrome.browsingData.removeServiceWorkers",
        "removeWebSQL": "chrome.browsingData.removeWebSQL",
        "settings": "chrome.browsingData.settings"
    },
    "clipboard": {
        "setImageData": "chrome.clipboard.setImageData"
    },
    "contextMenus": {
        "create": "chrome.contextMenus.create",
        "remove": "chrome.contextMenus.remove",
        "removeAll": "chrome.contextMenus.removeAll",
        "update": "chrome.contextMenus.update"
    },
    "cookies": {
        "get": "chrome.cookies.get",
        "getAll": "chrome.cookies.getAll",
        "getAllCookieStores": "chrome.cookies.getAllCookieStores",
        "remove": "chrome.cookies.remove",
        "set": "chrome.cookies.set"
    },
    "debugger": {
        "attach": "chrome.debugger.attach",
        "detach": "chrome.debugger.detach",
        "getTargets": "chrome.debugger.getTargets",
        "sendCommand": "chrome.debugger.sendCommand"
    },
    "declarativeNetRequest": {
        "getDynamicRules": "chrome.declarativeNetRequest.getDynamicRules",
        "getEnabledRulesets": "chrome.declarativeNetRequest.getEnabledRulesets",
        "getMatchedRules": "chrome.declarativeNetRequest.getMatchedRules",
        "updateDynamicRules": "chrome.declarativeNetRequest.updateDynamicRules",
        "updateEnabledRulesets": "chrome.declarativeNetRequest.updateEnabledRulesets"
    },
    "desktopCapture": {
        "chooseDesktopMedia": "chrome.desktopCapture.chooseDesktopMedia"
    },
    "downloads": {
        "acceptDanger": "chrome.downloads.acceptDanger",
        "cancel": "chrome.downloads.cancel",
        "download": "chrome.downloads.download",
        "erase": "chrome.downloads.erase",
        "getFileIcon": "chrome.downloads.getFileIcon",
        "pause": "chrome.downloads.pause",
        "removeFile": "chrome.downloads.removeFile",
        "resume": "chrome.downloads.resume",
        "search": "chrome.downloads.search"
    },
    "extension": {
        "isAllowedFileSchemeAccess": "chrome.extension.isAllowedFileSchemeAccess",
        "isAllowedIncognitoAccess": "chrome.extension.isAllowedIncognitoAccess"
    },
    "fontSettings": {
        "clearDefaultFixedFontSize": "chrome.fontSettings.clearDefaultFixedFontSize",
        "clearDefaultFontSize": "chrome.fontSettings.clearDefaultFontSize",
        "clearFont": "chrome.fontSettings.clearFont",
        "clearMinimumFontSize": "chrome.fontSettings.clearMinimumFontSize",
        "getDefaultFixedFontSize": "chrome.fontSettings.getDefaultFixedFontSize",
        "getDefaultFontSize": "chrome.fontSettings.getDefaultFontSize",
        "getFont": "chrome.fontSettings.getFont",
        "getFontList": "chrome.fontSettings.getFontList",
        "getMinimumFontSize": "chrome.fontSettings.getMinimumFontSize",
        "setDefaultFixedFontSize": "chrome.fontSettings.setDefaultFixedFontSize",
        "setDefaultFontSize": "chrome.fontSettings.setDefaultFontSize",
        "setFont": "chrome.fontSettings.setFont",
        "setMinimumFontSize": "chrome.fontSettings.setMinimumFontSize"
    },
    "gcm": {
        "register": "chrome.gcm.register",
        "send": "chrome.gcm.send",
        "unregister": "chrome.gcm.unregister"
    },
    "history": {
        "addUrl": "chrome.history.addUrl",
        "deleteAll": "chrome.history.deleteAll",
        "deleteRange": "chrome.history.deleteRange",
        "deleteUrl": "chrome.history.deleteUrl",
        "getVisits": "chrome.history.getVisits",
        "search": "chrome.history.search"
    },
    "i18n": {
        "detectLanguage": "chrome.i18n.detectLanguage",
        "getAcceptLanguages": "chrome.i18n.getAcceptLanguages"
    },
    "identity": {
        "getAuthToken": "chrome.identity.getAuthToken",
        "getProfileUserInfo": "chrome.identity.getProfileUserInfo",
        "launchWebAuthFlow": "chrome.identity.launchWebAuthFlow",
        "removeCachedAuthToken": "chrome.identity.removeCachedAuthToken"
    },
    "idle": {
        "queryState": "chrome.idle.queryState"
    },
    "instanceID": {
        "deleteID": "chrome.instanceID.deleteID",
        "deleteToken": "chrome.instanceID.deleteToken",
        "getCreationTime": "chrome.instanceID.getCreationTime",
        "getID": "chrome.instanceID.getID",
        "getToken": "chrome.instanceID.getToken"
    },
    "management": {
        "createAppShortcut": "chrome.management.createAppShortcut",
        "generateAppForLink": "chrome.management.generateAppForLink",
        "get": "chrome.management.get",
        "getAll": "chrome.management.getAll",
        "getPermissionWarningsById": "chrome.management.getPermissionWarningsById",
        "getPermissionWarningsByManifest": "chrome.management.getPermissionWarningsByManifest",
        "getSelf": "chrome.management.getSelf",
        "launchApp": "chrome.management.launchApp",
        "setEnabled": "chrome.management.setEnabled",
        "setLaunchType": "chrome.management.setLaunchType",
        "uninstall": "chrome.management.uninstall",
        "uninstallSelf": "chrome.management.uninstallSelf"
    },
    "notifications": {
        "clear": "chrome.notifications.clear",
        "create": "chrome.notifications.create",
        "getAll": "chrome.notifications.getAll",
        "getPermissionLevel": "chrome.notifications.getPermissionLevel",
        "update": "chrome.notifications.update"
    },
    "pageCapture": {
        "saveAsMHTML": "chrome.pageCapture.saveAsMHTML"
    },
    "permissions": {
        "getAll": "chrome.permissions.getAll"
    },
    "runtime": {
        "getBackgroundPage": "chrome.runtime.getBackgroundPage",
        "getPackageDirectoryEntry": "chrome.runtime.getPackageDirectoryEntry",
        "getPlatformInfo": "chrome.runtime.getPlatformInfo",
        "openOptionsPage": "chrome.runtime.openOptionsPage",
        "requestUpdateCheck": "chrome.runtime.requestUpdateCheck",
        "restartAfterDelay": "chrome.runtime.restartAfterDelay",
        "setUninstallURL": "chrome.runtime.setUninstallURL"
    },
    "sessions": {
        "getDevices": "chrome.sessions.getDevices",
        "getRecentlyClosed": "chrome.sessions.getRecentlyClosed",
        "restore": "chrome.sessions.restore"
    },
    "tabCapture": {
        "capture": "chrome.tabCapture.capture",
        "captureOffscreenTab": "chrome.tabCapture.captureOffscreenTab",
        "getCapturedTabs": "chrome.tabCapture.getCapturedTabs",
        "getMediaStreamId": "chrome.tabCapture.getMediaStreamId"
    },
    "tabs": {
        "captureVisibleTab": "chrome.tabs.captureVisibleTab",
        "create": "chrome.tabs.create",
        "detectLanguage": "chrome.tabs.detectLanguage",
        "discard": "chrome.tabs.discard",
        "duplicate": "chrome.tabs.duplicate",
        "executeScript": "chrome.tabs.executeScript",
        "get": "chrome.tabs.get",
        "getAllInWindow": "chrome.tabs.getAllInWindow",
        "getCurrent": "chrome.tabs.getCurrent",
        "getSelected": "chrome.tabs.getSelected",
        "getZoom": "chrome.tabs.getZoom",
        "getZoomSettings": "chrome.tabs.getZoomSettings",
        "goBack": "chrome.tabs.goBack",
        "goForward": "chrome.tabs.goForward",
        "highlight": "chrome.tabs.highlight",
        "insertCSS": "chrome.tabs.insertCSS",
        "move": "chrome.tabs.move",
        "query": "chrome.tabs.query",
        "reload": "chrome.tabs.reload",
        "remove": "chrome.tabs.remove",
        "setZoom": "chrome.tabs.setZoom",
        "setZoomSettings": "chrome.tabs.setZoomSettings",
        "update": "chrome.tabs.update"
    },
    "topSites": {
        "get": "chrome.topSites.get"
    },
    "tts": {
        "getVoices": "chrome.tts.getVoices",
        "isSpeaking": "chrome.tts.isSpeaking",
        "speak": "chrome.tts.speak"
    },
    "webNavigation": {
        "getAllFrames": "chrome.webNavigation.getAllFrames",
        "getFrame": "chrome.webNavigation.getFrame"
    },
    "webRequest": {
        "handlerBehaviorChanged": "chrome.webRequest.handlerBehaviorChanged"
    },
    "windows": {
        "create": "chrome.windows.create",
        "get": "chrome.windows.get",
        "getAll": "chrome.windows.getAll",
        "getCurrent": "chrome.windows.getCurrent",
        "getLastFocused": "chrome.windows.getLastFocused",
        "remove": "chrome.windows.remove",
        "update": "chrome.windows.update"
    }
}

Hope this helps!

dlh3 avatar Sep 22 '20 07:09 dlh3

@dlh3 Cheers. That's a really nice way of programmatically finding all the methods with a callback function.

I think there is a route to a programmatically configured API here, but the current list (while not dynamic) is mostly tested and includes some manual removals (such as #26).

I suspect the risk of breaking existing implementation with this change might be higher than the potential benefit of supporting new or missing APIs.

Next steps would be to compare this generated list to the existing one, and test and manually add missing ones.

KeithHenry avatar Sep 29 '20 15:09 KeithHenry

Happy to help. I agree that a programmatic solution could follow from this, though I believe it would be fairly inefficient. My intent was really to provide a way for generating a list for the purpose of comparing against the currently implemented list.

dlh3 avatar Sep 29 '20 16:09 dlh3