Skip to content

Implement support for the [Exposed] extended attribute #192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,12 @@ Performs the Web IDL conversion algorithm for this interface, converting _value_

In practice, this means doing a type-check equivalent to `is(value)`, and if it passes, returns the corresponding impl. If the type-check fails, it throws an informative exception. _context_ can be used to describe the provided value in any resulting error message.

#### `install(globalObject)`
#### `install(globalObject, globalNames)`

This method creates a brand new wrapper constructor and prototype and attach it to the passed `globalObject`. It also registers the created constructor with the `globalObject`'s global constructor registry, which makes `create()`, `createImpl()`, and `setup()` work. (Thus, it is important to invoke `install()` before invoking those methods, as otherwise they will throw.)

The second argument `globalNames` is an array containing the [global names](https://heycam.github.io/webidl/#dfn-global-name) of the interface that `globalObject` implements. This is used for the purposes of deciding which interfaces are [exposed](https://heycam.github.io/webidl/#dfn-exposed). For example, this array should be `["Window"]` for a [`Window`](https://html.spec.whatwg.org/multipage/window-object.html#window) global object. But for a [`DedicatedWorkerGlobalScope`](https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope) global object, this array should be `["Worker", "DedicatedWorker"]`. Note that we do not yet implement [`[SecureContext]`](https://heycam.github.io/webidl/#SecureContext), so the "exposed" check is not fully implemented.

#### `create(globalObject, constructorArgs, privateData)`

Creates a new instance of the wrapper class and corresponding implementation class, passing in the `globalObject`, the `constructorArgs` array and `privateData` object to the implementation class constructor. Then returns the wrapper class.
Expand Down Expand Up @@ -454,6 +456,7 @@ webidl2js is implementing an ever-growing subset of the Web IDL specification. S
- Variadic arguments
- `[Clamp]`
- `[EnforceRange]`
- `[Exposed]`
- `[LegacyArrayClass]`
- `[LegacyUnenumerableNamedProperties]`
- `[LegacyWindowAlias]`
Expand All @@ -478,7 +481,6 @@ Notable missing features include:
- `[AllowShared]`
- `[Default]` (for `toJSON()` operations)
- `[Global]`'s various consequences, including the named properties object and `[[SetPrototypeOf]]`
- `[Exposed]`
- `[LenientSetter]`
- `[LenientThis]`
- `[NamedConstructor]`
Expand Down
33 changes: 32 additions & 1 deletion lib/constructs/callback-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ class CallbackInterface {

this._analyzed = false;
this._outputStaticProperties = new Map();

const exposed = utils.getExtAttr(this.idl.extAttrs, "Exposed");
if (this.idl.members.some(member => member.type === "const") && !exposed) {
throw new Error(`Callback interface ${this.name} with defined constants lacks the [Exposed] extended attribute`);
}

if (exposed) {
if (!exposed.rhs || (exposed.rhs.type !== "identifier" && exposed.rhs.type !== "identifier-list")) {
throw new Error(`[Exposed] must take an identifier or an identifier list in callback interface ${this.name}`);
}

if (exposed.rhs.type === "identifier") {
this.exposed = new Set([exposed.rhs.value]);
} else {
this.exposed = new Set(exposed.rhs.value.map(token => token.value));
}
} else {
this.exposed = new Set();
}
}

_analyzeMembers() {
Expand Down Expand Up @@ -178,14 +197,24 @@ class CallbackInterface {
}

generateInstall() {
if (this.constants.size > 0) {
this.str += `
const exposed = new Set(${JSON.stringify([...this.exposed])});
`;
}

this.str += `
exports.install = function install(globalObject) {
exports.install = (globalObject, globalNames) => {
`;

if (this.constants.size > 0) {
const { name } = this;

this.str += `
if (!globalNames.some(globalName => exposed.has(globalName))) {
return;
}

const ${name} = () => {
throw new TypeError("Illegal invocation");
};
Expand Down Expand Up @@ -234,4 +263,6 @@ class CallbackInterface {
}
}

CallbackInterface.prototype.type = "callback interface";

module.exports = CallbackInterface;
9 changes: 7 additions & 2 deletions lib/constructs/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,12 @@ class Interface {
const { idl, name } = this;

this.str += `
exports.install = (globalObject, globalName) => {
const exposed = new Set(${JSON.stringify([...this.exposed])});

exports.install = (globalObject, globalNames) => {
if (!globalNames.some(globalName => exposed.has(globalName))) {
return;
}
`;

if (idl.inheritance) {
Expand Down Expand Up @@ -1500,7 +1505,7 @@ class Interface {

if (this.legacyWindowAliases) {
this.str += `
if (globalName === "Window") {
if (globalNames.includes("Window")) {
`;

for (const legacyWindowAlias of this.legacyWindowAliases) {
Expand Down
Loading