Code Monkey home page Code Monkey logo

chrome-promise's Introduction

chrome-promise

npm version build status

Chrome API using promises.

Note: I'm no longer adding features to this library (only bug fixing). You can check other alternative libraries at the end of this file.

Installation

Use npm

npm install chrome-promise

Or yarn

yarn add chrome-promise

Or download chrome-promise.js file.

You can include it in your HTML like this:

<script src="chrome-promise.js" data-instance="chromep"></script>

Or you can use ES2015 import statement:

import chromep from 'chrome-promise';

Compatibility

It supports Chrome 34 or higher, but it should work in older versions. Create an issue if it doesn't work for your version.

Examples

Use local storage.

chromep.storage.local.set({foo: 'bar'}).then(function() {
  alert('foo set');
  return chromep.storage.local.get('foo');
}).then(function(items) {
  alert(JSON.stringify(items)); // => {"foo":"bar"}
});

Detect languages of all tabs.

chromep.tabs.query({}).then((tabs) => {
  const promises = tabs.map(tab => chromep.tabs.detectLanguage(tab.id));
  return Promise.all(promises);
}).then((languages) => {
  alert('Languages: ' + languages.join(', '));
}).catch((err) => {
  alert(err.message);
});

Options

If you are testing with node, you can provide a mock for the chrome API (sinon-chrome is a good choice) using the constructor.

const ChromePromise = require('chrome-promise/constructor');
const chrome = require('sinon-chrome');

const chromep = new ChromePromise({ chrome });

The constructor accepts an options parameter with the following properties:

  • chrome: the chrome API object. By default (or when null or undefined are used), it is the 'chrome' global property.

  • Promise: the object used to create promises. By default, it is the 'Promise' global property.

Notes

This library is not a replacement of the chrome api. It should be used only for functions that have a callback.

// this returns a rejected promise (because a callback is added to getManifest)
chromep.runtime.getManifest();

// this works
chrome.runtime.getManifest();

When there's a callback with multiple parameters, the promise will return an array with the callback arguments.

chromep.hid.receive(4).then(function(args) {
  const reportId = args[0];
  const data = args[1];
  console.log(reportId, data);
});

// Using babel or chrome >= 49
chromep.hid.receive(4).then(([reportId, data]) => {
  console.log(reportId, data);
});

Only APIs that are enabled in the manifest will be available in the ChromePromise instance. If the API is undefined, first check the permissions in the manifest.json of your project.

// manifest.json
{
  "permissions": [
    "tabs"
  ]
}

// main.js
console.log(typeof chromep.tabs)  // "object"
console.log(typeof chromep.bookmarks)  // "undefined"

Synchronous-looking code

If you are using babel or chrome ≥ 55, you can use async/await to achieve this.

async function main() {
  await chromep.storage.local.set({foo: 'bar'});
  alert('foo set');
  const items = await chromep.storage.local.get('foo');
  alert(JSON.stringify(items));
}
main();

// try...catch
async function main2() {
  try {
    const tabs = await chromep.tabs.query({});
    const promises = tabs.map(tab => chromep.tabs.detectLanguage(tab.id));
    const languages = await Promise.all(promises);
    alert('Languages: ' + languages.join(', '));
  } catch(err) {
    alert(err);
  }
}
main2();

If you are not using babel and you need to support chrome ≥ 39, it can be done with generator functions. Using the methods Q.async and Q.spawn from the Q library, the previous examples can be rewritten as:

Q.spawn(function* () {
  yield chromep.storage.local.set({foo: 'bar'});
  alert('foo set');
  const items = yield chromep.storage.local.get('foo');
  alert(JSON.stringify(items));
});

// try...catch
Q.spawn(function* () {
  try {
    const tabs = yield chromep.tabs.query({});
    const promises = tabs.map(tab => chromep.tabs.detectLanguage(tab.id));
    const languages = yield Promise.all(promises);
    alert('Languages: ' + languages.join(', '));
  } catch(err) {
    alert(err);
  }
});

// promise.catch
Q.async(function* () {
  const tabs = yield chromep.tabs.query({});
  const languages = yield Promise.all(tabs.map((tab) => (
    chromep.tabs.detectLanguage(tab.id);
  )));
  alert('Languages: ' + languages.join(', '));
})().catch(function(err) {
  alert(err);
});

You can also use the co library instead of Q.

Alternative libraries

chrome-promise's People

Contributors

errorx666 avatar foray1010 avatar lmk123 avatar rafis avatar riggs avatar tfoxy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

chrome-promise's Issues

ChromePromise instance doesn't update when optional permissions are requested

The ChromePromise constructor wraps whatever Chrome extension APIs are available when the library is first loaded. But it's also possible to request optional permissions after the extension first loads. If the user gives that permission, though, any existing ChromePromise instances aren't updated to provide the newly available API.

One way to solve this might be wrap the chrome.permissions.request() method and intercept the request and the response. If the response is true, then the instance could look at the requested permissions and update itself with those APIs.

Optionally replace existing methods

Hi,

It would be nice to be able to use directly the promisifyied api instead of accessing it through the chrome.promise namespace. I understand that this is more safe this way as it allows existing code using old-style callbacks to continue to work.
But if your entire project uses the promisifyied version, this is kind of cumbersome to have to type chrome.promise.

Do you think you could add an option to do this, or even make this behavior the default one and add an option to namespace promisifyied methods if needed?

Thanks!

fileSystem.retainEntry() incorrectly passed a callback?

The chrome.fileSystem.retainEntry() API does not take a callback. I am seeing chromep fail when calling this method, as Chrome appears to be doing some schema checking. I can't find anything about where this happens, and I should be able to work around it, but it seems like chromep should be aware of this.

E.g. consider this code:

var id = chromep.fileSystem.retainEntry(entry);

At this point I would expect id to be a string. Instead it is a rejected Promise. From the console:

Promise {[[PromiseStatus]]: "rejected",
 [[PromiseValue]]: Error: Invocation of form fileSystem.retainEntry(object, function) doesn't match definition fileSystem.retainEntry(object entry)}

Is this expected? Is it something else about my environment that is interfering with things? Or perhaps a bug?

Error with Typescript: "Property 'tts' does not exist on type 'typeof ChromePromise'."

I think you need to include/mirror and rename the chrome extensions API typescript (DefinitelyTyped) definitions,
I am using them with my typescript project and I love the intelisense code suggestions.
as documented I imported chromep like so import chromep from 'chrome-promise';
I didn't mind the lack of code suggestions for chromep, but it seems with out the definitions Typescript compiler shows errors and doesn't compile.

I'll try forking and fixing it now, if I do i'll submit a PR. but please don't refrain from commenting your input.

Can't import from Typescript

I'm trying to use chrome-promise in a Typescript Chrome Extension. The documented ES usage is:

import ChromePromise from 'chrome-promise';

const chromep = new ChromePromise();
chromep.something_or_other('param');

When I do the same in Typescript everything transpiles properly - ChromePromise is recognized with the right type information. Intellisense is shown, Typescript catches errors if I call a non-existing method - it's all fine.

At runtime, however, I can't create the ChromePromise instance. I get the following error:

Uncaught TypeError: chrome_promise_1.default is not a constructor
    at Object.__WEBPACK_AMD_DEFINE_ARRAY__ (background.js:77)
    at __webpack_require__ (background.js:20)
    at Object.defineProperty.value (background.js:63)
    at background.js:66

And indeed, chrome_promise_1.default is undefined, while chrome_promise_1 is a function.

Changing the import to import { ChromePromise } from ... results in an error saying ChromePromise is undefined.

import * as ChromePromise from ... yields an error saying ChromePromise has no type.

There's also a Stack Overflow question: https://stackoverflow.com/questions/47651244/cant-import-from-chrome-promise-in-typescript

chrome.permissions is not available in content scripts

Hi it seems the chrome.permissions.onAdded is added lately in the api. However the chrome.permissions variable is not supported in the content script and it prevents us from using chrome.permissions in content script.

We should add a check there before we add that api

chrome.promise.storage.local.get not work

chrome.promise = new ChromePromise();
function get(key) {
    return chrome.promise.storage.local.get(key);
}
TypeError: Cannot read property 'functionSchemas' of null
    at self.(anonymous function) (extensions::StorageArea:26:27)
    at setPromiseFunction (chrome-extension://***/assets/chrome-promise/chrome-promise.js:47:25)
    at Object.setPromiseFunction [as get] (chrome-extension://***/assets/chrome-promise/chrome-promise.js:35:24)

sinon-chrome support

Edit:
I see your unit test is using sinon-chrome, but for me it doesn't populate e.g. chromep.storage.local. Stepping through the debugger, the hasOwnProperty check in fillProperties returns false for this property.

Need support for Chrome 32

Hi @tfoxy,

wanna ask if we can support Chrome 32, my extension already have a large user base and I don't want a sudden increase of min chrome version which may result in decreasing my user numbers

I am available to make PR and test it using Chrome 32, I think we just need to remove the rest parameters

Why using `GLOBAL` in source code?

What does it refer to?
If it is useful, I suggest to use typeof GLOBAL === 'undefined' to check if it exists, because it causes error when using strict mode

this remapped to undefined when using babel and es6 modules

Hi, I am using babel + webpack and I used import to include chrome promise.

This is what the transpiled code looks like:

(function (root, factory) {
  if (true) {
    // AMD. Register as an anonymous module.
    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory.bind(null, ( false ? 'undefined' : (0, _typeof3.default)(exports)) === 'object' ? this : root)),
				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
				(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  } else if ((typeof exports === 'undefined' ? 'undefined' : (0, _typeof3.default)(exports)) === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(this);
  } else {
    // Browser globals (root is window)
    root.ChromePromise = factory(root);
  }
})(undefined, function (root) {

https://babeljs.io/faq/#why-is-this-being-remapped-to-undefined-

Is there any way to fix this without disabling strict mode?

await not working for chromep.windows.remove()

In the following TypeScript code, chromep.windows.getAll() returns the windows closed by chromep.windows.remove() despite the usage of await. If I add a breakpoint before the call to chromep.windows.getAll() and wait, the closed windows are absent (as they should be).

const allWindows: chrome.windows.Window[] = await chromep.windows.getAll();
console.log(allWindows);

// close a window
const currentTabId: number = (await chromep.tabs.getCurrent()).id;
for (const window of allWindows) {
  const tabIds: number[] = window.tabs.map(tab => tab.id);

  if (tabIds.indexOf(currentTabId) === -1) {
    await chromep.windows.remove(window.id);
    break;
  }
}

// still contains closed window
console.log(await chromep.windows.getAll());

I don't have a deep understanding of this library, but it seems the cause of this issue is that chromep.windows.remove() contains asynchronous tab-closing function calls that aren't awaited or resolved otherwise. My reasoning for the above is that the closed windows returned by chromep.windows.getAll() are sometimes missing at least one of their original tabs, meaning that the call is completed before chromep.windows.remove() has a chance to close every tab.

update to latest TypeScript

I am having problems with using [email protected] with chrome-promise. My project needs tsc v3.9.5 to compile as it uses es2020 and other features.

I have installed @types/node:latest and [email protected], and am getting the following :-

node_modules/chrome-promise/chrome-promise.d.ts:73:51 - error TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesGetArg'.

73         get(details: chrome.accessibilityFeatures.AccessibilityFeaturesGetArg): Promise<chrome.accessibilityFeatures.AccessibilityFeaturesCallbackArg>;
                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~

node_modules/chrome-promise/chrome-promise.d.ts:73:118 - error TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesCallbackArg'.

73         get(details: chrome.accessibilityFeatures.AccessibilityFeaturesGetArg): Promise<chrome.accessibilityFeatures.AccessibilityFeaturesCallbackArg>;
                                                                                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

node_modules/chrome-promise/chrome-promise.d.ts:81:51 - error TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesSetArg'.

81         set(details: chrome.accessibilityFeatures.AccessibilityFeaturesSetArg): Promise<void>;
                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~

node_modules/chrome-promise/chrome-promise.d.ts:89:53 - error TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesClearArg'.

89         clear(details: chrome.accessibilityFeatures.AccessibilityFeaturesClearArg): Promise<void>;
                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Found 4 errors.

New chrome errors when accessing extension.sendRequest, extension.onRequest, or extension.onRequestExternal

Hello!

This extension has been super useful for me so far.

However, a new version of chrome (I'm on Version 71.0.3578.98) seems to throw an error whenever accessing the properties sendRequest, onRequest, or onRequestExternal on the "extension" object.

This means that the code here:

if (hasOwnProperty.call(source, key)) {
          var val = source[key];

is causing this node module to throw an error, and the chrome plugin can't proceed. The exact error (in my case) is below:

background.js:592 Uncaught Error: extension.sendRequest, extension.onRequest, and extension.onRequestExternal are deprecated. Please use runtime.sendMessage, runtime.onMessage, and runtime.onMessageExternal instead.
    at fillProperties (background.js:592)
    at fillProperties (background.js:597)
    at new ChromePromise (background.js:547)
    at Object../node_modules/chrome-promise/index.js (background.js:634)
    at __webpack_require__ (background.js:20)
    at Object../app/extension/scripts/background.ts (background.js:107)
    at __webpack_require__ (background.js:20)
    at Object.0 (background.js:2178)
    at __webpack_require__ (background.js:20)
    at background.js:84

I'm happy to submit a pull request later today. I can think of two approaches to handling this:

  • Wrap the var val = source[key] property in a try-catch block, and skip properties that are now error-deprecated
  • create a hard-coded black-list of properties that are skipped no matter what.

Any preference as to which approach I take?

Types out of date

Upgraded chrome typings recently, and now chrome-promise is failing with the following errors:

/srv/package/node_modules/chrome-promise/chrome-promise.d.ts
ERROR in /srv/package/node_modules/chrome-promise/chrome-promise.d.ts(73,51):
TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesGetArg'.
/srv/package/node_modules/chrome-promise/chrome-promise.d.ts
ERROR in /srv/package/node_modules/chrome-promise/chrome-promise.d.ts(73,118):
TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesCallbackArg'.
/srv/package/node_modules/chrome-promise/chrome-promise.d.ts
ERROR in /srv/package/node_modules/chrome-promise/chrome-promise.d.ts(81,51):
TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesSetArg'.
/srv/package/node_modules/chrome-promise/chrome-promise.d.ts
ERROR in /srv/package/node_modules/chrome-promise/chrome-promise.d.ts(89,53):
TS2694: Namespace 'chrome.accessibilityFeatures' has no exported member 'AccessibilityFeaturesClearArg'.

Mention that only APIs that are enabled in the manifest will be available

This may be obvious to some, but I just ran into it. I wanted to access bookmarks via new ChromePromise().bookmarks.getTree() but it was undefined. At first I thought it might be something wrong with this library, as it seemed strange that it wouldn't support the bookmarks API. A quick look at the source made me realize it wasn't available because it wasn't on the chrome object, because I hadn't added the bookmarks permission to the manifest yet. Duh.

So it might be worth pointing that out in the Warnings section of the README. If something's undefined on a ChromePromise, it means you need to add it to the permissions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.