Merge pull request #808 from fitzgen/web-sys-in-the-guide

web-sys gets its own section in the guide
This commit is contained in:
Alex Crichton 2018-09-11 10:08:06 -07:00 committed by GitHub
commit 1d2d397f55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 423 additions and 284 deletions

View File

@ -3,6 +3,8 @@ name = "web-sys"
version = "0.1.0" version = "0.1.0"
authors = ["The wasm-bindgen Developers"] authors = ["The wasm-bindgen Developers"]
readme = "./README.md" readme = "./README.md"
homepage = "https://rustwasm.github.io/wasm-bindgen/web-sys/index.html"
documentation = "https://rustwasm.github.io/wasm-bindgen/api/web_sys/"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@ -1,10 +1,10 @@
# `web-sys` # `web-sys`
[Documentation](https://rustwasm.github.io/wasm-bindgen/api/web_sys/)
Raw bindings to Web APIs for projects using `wasm-bindgen`. Raw bindings to Web APIs for projects using `wasm-bindgen`.
The book: https://rustwasm.github.io/wasm-bindgen/web-sys.html * [The `web-sys` section of the `wasm-bindgen`
guide](https://rustwasm.github.io/wasm-bindgen/web-sys/index.html)
* [API Documentation](https://rustwasm.github.io/wasm-bindgen/api/web_sys/)
## Crate features ## Crate features

View File

@ -1,7 +1,7 @@
[package] [package]
name = "fetch" name = "fetch"
version = "0.1.0" version = "0.1.0"
authors = ["Andrew Chin <achin@eminence32.net>"] authors = ["The wasm-bindgen Developers"]
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]

View File

@ -1,21 +1,23 @@
extern crate wasm_bindgen;
extern crate js_sys;
extern crate web_sys;
extern crate wasm_bindgen_futures;
extern crate futures; extern crate futures;
extern crate js_sys;
extern crate wasm_bindgen;
extern crate wasm_bindgen_futures;
extern crate web_sys;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
use futures::{future, Future};
use js_sys::Promise;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use js_sys::Promise;
use web_sys::{Request, RequestInit, RequestMode, Response, Window};
use wasm_bindgen_futures::JsFuture;
use futures::{future, Future};
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response, Window};
// A struct to hold some data from the github Branch API. /// A struct to hold some data from the github Branch API.
// Note how we don't have to define every member -- serde will ignore extra data when deserializing ///
/// Note how we don't have to define every member -- serde will ignore extra
/// data when deserializing
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Branch { pub struct Branch {
pub name: String, pub name: String,
@ -42,37 +44,38 @@ pub struct Signature {
#[wasm_bindgen] #[wasm_bindgen]
pub fn run() -> Promise { pub fn run() -> Promise {
let mut request_options = RequestInit::new(); let mut opts = RequestInit::new();
request_options.method("GET"); opts.method("GET");
request_options.mode(RequestMode::Cors); opts.mode(RequestMode::Cors);
let req = Request::new_with_str_and_init("https://api.github.com/repos/rustwasm/wasm-bindgen/branches/master", &request_options).unwrap(); let request = Request::new_with_str_and_init(
"https://api.github.com/repos/rustwasm/wasm-bindgen/branches/master",
&opts,
).unwrap();
// the RequestInit struct will eventually support setting headers, but that's missing right now request.headers()
req.headers().set("Accept", "application/vnd.github.v3+json").unwrap(); .set("Accept", "application/vnd.github.v3+json")
.unwrap();
let req_promise = Window::fetch_with_request(&req); let request_promise = Window::fetch_with_request(&request);
let to_return = JsFuture::from(req_promise).and_then(|resp_value| { let future = JsFuture::from(request_promise)
// resp_value is a Response object .and_then(|resp_value| {
// `resp_value` is a `Response` object.
assert!(resp_value.is_instance_of::<Response>()); assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into().unwrap(); let resp: Response = resp_value.dyn_into().unwrap();
resp.json() resp.json()
}).and_then(|json_value: Promise| { }).and_then(|json_value: Promise| {
// convert this other promise into a rust Future // Convert this other `Promise` into a rust `Future`.
JsFuture::from(json_value) JsFuture::from(json_value)
}).and_then(|json| { }).and_then(|json| {
// Use serde to parse this into a struct // Use serde to parse the JSON into a struct.
let branch_info: Branch = json.into_serde().unwrap(); let branch_info: Branch = json.into_serde().unwrap();
// Send the Branch struct back to javascript as an object // Send the `Branch` struct back to JS as an `Object`.
future::ok(JsValue::from_serde(&branch_info).unwrap()) future::ok(JsValue::from_serde(&branch_info).unwrap())
}); });
// Convert this rust future back into a javascript promise. // Convert this Rust `Future` back into a JS `Promise`.
// Return it to javascript so that it can be driven to completion. future_to_promise(future)
future_to_promise(to_return)
} }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "webaudio" name = "webaudio"
version = "0.1.0" version = "0.1.0"
authors = ["Andrew Chin <achin@eminence32.net>"] authors = ["The wasm-bindgen Developers"]
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]

View File

@ -3,16 +3,9 @@
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/> <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head> </head>
<body> <body>
<script>
// Global variable, so a user can play with it via the JS console
var fm;
function play() {
console.log("Rust module not loaded yet!");
}
</script>
<script src='./index.js'></script> <script src='./index.js'></script>
<input type="button" value="Click me first to turn on audio" onclick="javascript: play();" /> <input id="play" type="button" value="Click me first to turn on audio"/>
(headphone users, please make sure your volume is not too loud!) (headphone users, please make sure your volume is not too loud!)
<div> <div>

View File

@ -1,40 +1,35 @@
const rust = import('./webaudio'); import('./webaudio').then(rust_module => {
let fm = null;
const play_button = document.getElementById("play");
// Most browsers don't let WebAudio autoplay without some interaction from the user. So once the module is loaded, play_button.addEventListener("click", event => {
// it's passed to this function which will set up the UI elements for the user to interact with if (fm === null) {
function setup(rust_module) {
play = function() {
console.log("About to create some music!");
fm = new rust_module.FmOsc(); fm = new rust_module.FmOsc();
fm.set_note(50); fm.set_note(50);
fm.set_fm_frequency(0); fm.set_fm_frequency(0);
fm.set_fm_amount(0); fm.set_fm_amount(0);
fm.set_gain(0.8); fm.set_gain(0.8);
}
});
};
// create some UI elements
const primary_slider = document.getElementById("primary_input"); const primary_slider = document.getElementById("primary_input");
primary_slider.oninput = (e) => { primary_slider.addEventListener("input", event => {
fm.set_note(e.target.value); if (fm) {
}; fm.set_note(event.target.value);
}
});
const fm_freq = document.getElementById("fm_freq"); const fm_freq = document.getElementById("fm_freq");
fm_freq.oninput = (e) => { fm_freq.addEventListener("input", event => {
fm.set_fm_frequency(e.target.value); if (fm) {
}; fm.set_fm_frequency(event.target.value);
}
});
const fm_amount = document.getElementById("fm_amount"); const fm_amount = document.getElementById("fm_amount");
fm_amount.oninput = (e) => { fm_amount.addEventListener("input", event => {
fm.set_fm_amount(e.target.value); if (fm) {
}; fm.set_fm_amount(event.target.value);
}
console.log("Ready! Press the play button!"); });
}
rust.then(m => {
setup(m);
}); });

View File

@ -4,6 +4,7 @@
}, },
"devDependencies": { "devDependencies": {
"webpack": "^4.16.5", "webpack": "^4.16.5",
"webpack-serve": "^2.0.2" "webpack-cli": "^2.0.10",
"webpack-dev-server": "^3.1.0"
} }
} }

View File

@ -2,7 +2,9 @@ extern crate wasm_bindgen;
extern crate web_sys; extern crate web_sys;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys::{AudioContext, BaseAudioContext, AudioNode, AudioScheduledSourceNode, OscillatorType}; use web_sys::{
AudioContext, AudioNode, AudioScheduledSourceNode, BaseAudioContext, OscillatorType,
};
/// Converts a midi note to frequency /// Converts a midi note to frequency
/// ///
@ -32,16 +34,12 @@ pub struct FmOsc {
fm_freq_ratio: f32, fm_freq_ratio: f32,
fm_gain_ratio: f32, fm_gain_ratio: f32,
} }
#[wasm_bindgen] #[wasm_bindgen]
impl FmOsc { impl FmOsc {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new() -> FmOsc { pub fn new() -> FmOsc {
// TODO, how to throw from a constructor?
let ctx = web_sys::AudioContext::new().unwrap(); let ctx = web_sys::AudioContext::new().unwrap();
let primary; let primary;
let fm_osc; let fm_osc;
@ -51,14 +49,14 @@ impl FmOsc {
{ {
let base: &BaseAudioContext = ctx.as_ref(); let base: &BaseAudioContext = ctx.as_ref();
// create our web audio objects // Create our web audio objects.
primary = base.create_oscillator().unwrap(); primary = base.create_oscillator().unwrap();
fm_osc = base.create_oscillator().unwrap(); fm_osc = base.create_oscillator().unwrap();
gain = base.create_gain().unwrap(); gain = base.create_gain().unwrap();
fm_gain = base.create_gain().unwrap(); fm_gain = base.create_gain().unwrap();
} }
// some initial settings: // Some initial settings:
primary.set_type(OscillatorType::Sine); primary.set_type(OscillatorType::Sine);
primary.frequency().set_value(440.0); // A4 note primary.frequency().set_value(440.0); // A4 note
gain.gain().set_value(0.0); // starts muted gain.gain().set_value(0.0); // starts muted
@ -66,7 +64,6 @@ impl FmOsc {
fm_osc.set_type(OscillatorType::Sine); fm_osc.set_type(OscillatorType::Sine);
fm_osc.frequency().set_value(0.0); fm_osc.frequency().set_value(0.0);
// Create base class references: // Create base class references:
{ {
let primary_node: &AudioNode = primary.as_ref(); let primary_node: &AudioNode = primary.as_ref();
@ -77,26 +74,36 @@ impl FmOsc {
let destination = base.destination(); let destination = base.destination();
let destination_node: &AudioNode = destination.as_ref(); let destination_node: &AudioNode = destination.as_ref();
// Connect the nodes up!
// connect them up: // The primary oscillator is routed through the gain node, so that
// it can control the overall output volume.
// The primary oscillator is routed through the gain node, so that it can control the overall output volume
primary_node.connect_with_audio_node(gain.as_ref()).unwrap(); primary_node.connect_with_audio_node(gain.as_ref()).unwrap();
// Then connect the gain node to the AudioContext destination (aka your speakers)
// Then connect the gain node to the AudioContext destination (aka
// your speakers).
gain_node.connect_with_audio_node(destination_node).unwrap(); gain_node.connect_with_audio_node(destination_node).unwrap();
// the FM oscillator is connected to its own gain node, so it can control the amount of modulation // The FM oscillator is connected to its own gain node, so it can
fm_osc_node.connect_with_audio_node(fm_gain.as_ref()).unwrap(); // control the amount of modulation.
fm_osc_node
.connect_with_audio_node(fm_gain.as_ref())
.unwrap();
// Connect the FM oscillator to the frequency parameter of the main oscillator, so that the // Connect the FM oscillator to the frequency parameter of the main
// FM node can modulate its frequency // oscillator, so that the FM node can modulate its frequency.
fm_gain_node.connect_with_audio_param(&primary.frequency()).unwrap(); fm_gain_node
.connect_with_audio_param(&primary.frequency())
.unwrap();
} }
// Start the oscillators!
// start the oscillators! AsRef::<AudioScheduledSourceNode>::as_ref(&primary)
AsRef::<AudioScheduledSourceNode>::as_ref(&primary).start().unwrap(); .start()
AsRef::<AudioScheduledSourceNode>::as_ref(&fm_osc).start().unwrap(); .unwrap();
AsRef::<AudioScheduledSourceNode>::as_ref(&fm_osc)
.start()
.unwrap();
FmOsc { FmOsc {
ctx, ctx,
@ -107,14 +114,17 @@ impl FmOsc {
fm_freq_ratio: 0.0, fm_freq_ratio: 0.0,
fm_gain_ratio: 0.0, fm_gain_ratio: 0.0,
} }
} }
/// Sets the gain for this oscillator, between 0.0 and 1.0 /// Sets the gain for this oscillator, between 0.0 and 1.0.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_gain(&self, mut gain: f32) { pub fn set_gain(&self, mut gain: f32) {
if gain > 1.0 { gain = 1.0; } if gain > 1.0 {
if gain < 0.0 { gain = 0.0; } gain = 1.0;
}
if gain < 0.0 {
gain = 0.0;
}
self.gain.gain().set_value(gain); self.gain.gain().set_value(gain);
} }
@ -122,11 +132,10 @@ impl FmOsc {
pub fn set_primary_frequency(&self, freq: f32) { pub fn set_primary_frequency(&self, freq: f32) {
self.primary.frequency().set_value(freq); self.primary.frequency().set_value(freq);
// The frequency of the FM oscillator depends on the frequency of the primary oscillator, so // The frequency of the FM oscillator depends on the frequency of the
// we update the frequency of both in this method // primary oscillator, so we update the frequency of both in this method.
self.fm_osc.frequency().set_value(self.fm_freq_ratio * freq); self.fm_osc.frequency().set_value(self.fm_freq_ratio * freq);
self.fm_gain.gain().set_value(self.fm_gain_ratio * freq); self.fm_gain.gain().set_value(self.fm_gain_ratio * freq);
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -135,21 +144,22 @@ impl FmOsc {
self.set_primary_frequency(freq); self.set_primary_frequency(freq);
} }
/// This should be between 0 and 1, though higher values are accepted /// This should be between 0 and 1, though higher values are accepted.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_fm_amount(&mut self, amt: f32) { pub fn set_fm_amount(&mut self, amt: f32) {
self.fm_gain_ratio = amt; self.fm_gain_ratio = amt;
self.fm_gain.gain().set_value(self.fm_gain_ratio * self.primary.frequency().value()); self.fm_gain
.gain()
.set_value(self.fm_gain_ratio * self.primary.frequency().value());
} }
/// This should be between 0 and 1, though higher values are accepted /// This should be between 0 and 1, though higher values are accepted.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_fm_frequency(&mut self, amt: f32) { pub fn set_fm_frequency(&mut self, amt: f32) {
self.fm_freq_ratio = amt; self.fm_freq_ratio = amt;
self.fm_osc.frequency().set_value(self.fm_freq_ratio * self.primary.frequency().value()); self.fm_osc
.frequency()
.set_value(self.fm_freq_ratio * self.primary.frequency().value());
} }
} }

View File

@ -50,23 +50,35 @@
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
- [Contributing](./contributing.md) - [`web-sys`](./web-sys/index.md)
- [Testing](./testing.md) - [Using `web-sys`](./web-sys/using-web-sys.md)
- [Internal Design](./design.md) - [Cargo Features](./web-sys/cargo-features.md)
- [JS Objects in Rust](./design/js-objects-in-rust.md) - [Function Overloads](./web-sys/function-overloads.md)
- [Exporting a function to JS](./design/exporting-rust.md) - [Type Translations](./web-sys/type-translations.md)
- [Exporting a struct to JS](./design/exporting-rust-struct.md) - [Examples](./web-sys/examples/index.md)
- [Importing a function from JS](./design/importing-js.md) - [The `fetch` API](./web-sys/examples/fetch.md)
- [Importing a class from JS](./design/importing-js-struct.md) - [2D Canvas](./web-sys/examples/2d-canvas.md)
- [Rust Type conversions](./design/rust-type-conversions.md) - [WebAudio](./web-sys/examples/web-audio.md)
- [Types in `wasm-bindgen`](./design/describe.md)
- [`js-sys`](./js-sys.md) --------------------------------------------------------------------------------
- [Testing](./js-sys/testing.md)
- [Adding More APIs](./js-sys/adding-more-apis.md) - [Contributing](./contributing/index.md)
- [`web-sys`](./web-sys.md) - [Testing](./contributing/testing.md)
- [Overview](./web-sys/overview.md) - [Internal Design](./contributing/design/index.md)
- [Testing](./web-sys/testing.md) - [JS Objects in Rust](./contributing/design/js-objects-in-rust.md)
- [Logging](./web-sys/logging.md) - [Exporting a function to JS](./contributing/design/exporting-rust.md)
- [Supporting More Web APIs](./web-sys/supporting-more-web-apis.md) - [Exporting a struct to JS](./contributing/design/exporting-rust-struct.md)
- [Publishing](./publishing.md) - [Importing a function from JS](./contributing/design/importing-js.md)
- [Team](./team.md) - [Importing a class from JS](./contributing/design/importing-js-struct.md)
- [Rust Type conversions](./contributing/design/rust-type-conversions.md)
- [Types in `wasm-bindgen`](./contributing/design/describe.md)
- [`js-sys`](./contributing/js-sys/index.md)
- [Testing](./contributing/js-sys/testing.md)
- [Adding More APIs](./contributing/js-sys/adding-more-apis.md)
- [`web-sys`](./contributing/web-sys/index.md)
- [Overview](./contributing/web-sys/overview.md)
- [Testing](./contributing/web-sys/testing.md)
- [Logging](./contributing/web-sys/logging.md)
- [Supporting More Web APIs](./contributing/web-sys/supporting-more-web-apis.md)
- [Publishing](./contributing/publishing.md)
- [Team](./contributing/team.md)

View File

@ -0,0 +1,24 @@
# Publishing New `wasm-bindgen` Releases
1. <input type="checkbox"/> Compile the `publish.rs` script:
```
rustc publish.rs
```
2. <input type="checkbox"/> Bump every crate's minor version:
```
# Make sure you are in the root of the wasm-bindgen repo!
./publish bump
```
3. <input type="checkbox"/> Send a pull request for the version bump.
4. <input type="checkbox"/> After the pull request's CI is green and it has been
merged, publish to cargo:
```
# Make sure you are in the root of the wasm-bindgen repo!
./publish publish
```

View File

@ -0,0 +1,10 @@
# `web-sys`
The `web-sys` crate provides raw bindings to all of the Web's APIs, and its
source lives at `wasm-bindgen/crates/web-sys`.
The `web-sys` crate is **entirely** mechanically generated inside `build.rs`
using `wasm-bindgen`'s WebIDL frontend and the WebIDL interface definitions for
Web APIs. This means that `web-sys` isn't always the most ergonomic crate to
use, but it's intended to provide verified and correct bindings to the web
platform, and then better interfaces can be iterated on crates.io!

View File

@ -1,40 +0,0 @@
# Publishing New `wasm-bindgen` Releases
* [ ] Make sure that your git working copy is clean.
* [ ] Make sure that you are on the latest `master`:
```
git pull origin master
```
* [ ] Run `rustc ./publish.rs`
* [ ] Run `./publish bump` - this will update all version numbers
* [ ] Write a "0.X.Z" entry in the CHANGELOG.md
* [ ] Commit the version bump:
```
git commit -m "Bump to version 0.X.Z"
```
* [ ] Send a PR to the `wasm-bindgen` repository, get it merged
* [ ] Check out the merge commit of `wasm-bindgen`
* [ ] Comment out the `[patch]` section in the root `Cargo.toml` that only
exists to make sure that `console_error_panic_hook` in tests is using
*this* `wasm-bindgen` rather than one from crates.io.
* [ ] Run `rustc ./publish.rs`
* [ ] Run `./publish publish`
* [ ] Tag the release and push it:
```
git tag 0.X.Z
git push origin --tags
```

View File

@ -1,103 +0,0 @@
# `web-sys`
The `web-sys` crate provides raw bindings to all of the Web's APIs, and its
source lives at `wasm-bindgen/crates/web-sys`.
The `web-sys` crate is **entirely** mechanically generated inside `build.rs`
using `wasm-bindgen`'s WebIDL frontend and the WebIDL interface definitions for
Web APIs. This means that `web-sys` isn't always the most ergonomic crate to
use, but it's intended to provide verified and correct bindings to the web
platform, and then better interfaces can be iterated on crates.io!
### Using `web-sys`
Let's say you want to use an API defined on the web. Chances are this API is
defined in `web-sys`, so let's go through some steps necessary to use it!
First up, search the [api documentation][api] for your API. For example if
we're looking for JS's [`fetch`][jsfetch] API we'd start out by [searching for
`fetch`][search-fetch]. The first thing you'll probably notice is that there's
no function called `fetch`! Fear not, though, as the API exists in multiple
forms:
* [`Window::fetch_with_str`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str)
* [`Window::fetch_with_request`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request)
* [`Window::fetch_with_str_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/str_and_inituct.Window.html#method.fetch_with_str_and_init)
* [`Window::fetch_with_request_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request_and_init)
What's happening here is that the [`fetch` function][fetchfn] actually supports
multiple signatures of arguments, and we've taken the WebIDL definition for this
function and expanded it to unique signatures in Rust (as Rust doesn't have
function name overloading).
When an API is selected it should have documentation pointing at MDN indicating
what web API its binding. This is often a great way to double check arguments
and such as well, MDN is a great resource! You'll also notice in the
documentation that the API may require some `web-sys` Cargo features to be
activated. For example [`fetch_with_str`] requires the `Window` feature to be
activated. In general an API needs features corresponding to all the types
you'll find in the signature to be activated.
To load up this API let's depend on `web-sys`:
```toml
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.1", features = ['Window'] }
# Or optionally,
# [target.wasm32-unknown-unknown.dependencies]
# ...
```
> **Note**: Currently `web-sys` is not available on crates.io so you'll also
> need to do this in your manifest:
>
> ```toml
> [patch.crates-io]
> web-sys = { git = 'https://github.com/rustwasm/wasm-bindgen' }
> wasm-bindgen = { git = 'https://github.com/rustwasm/wasm-bindgen' }
> ```
And next up we can use the API like so:
```rust
extern crate web_sys;
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
use web_sys::Window;
#[wasm_bindgen]
pub fn run() {
let promise = Window::fetch_with_str("http://example.com/");
// ...
}
```
and you should be good to go!
### Type translations in `web-sys`
Most of the types specified in WebIDL have relatively straightforward
translations into `web-sys`, but it's worth calling out a few in particular:
* `BufferSource` and `ArrayBufferView` - these two types show up in a number of
APIs that generally deal with a buffer of bytes. We bind them in `web-sys`
with two different types, `Object` and `&mut [u8]`. Using `Object` allows
passing in arbitrary JS values which represent a view of bytes (like any typed
array object), and `&mut [u8]` allows using a raw slice in Rust. Unfortunately
we must pessimistically assume that JS will modify all slices as we don't
currently have information of whether they're modified or not.
* Callbacks are all represented as `js_sys::Function`. This means that all
callbacks going through `web-sys` are a raw JS value. You can work with this
by either juggling actual `js_sys::Function` instances or you can create a
`Closure<FnMut(...)>`, extract the underlying `JsValue` with `as_ref`, and
then use `JsCast::unchecked_ref` to convert it to a `js_sys::Function`.
[api]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/
[jsfetch]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
[search-fetch]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/?search=fetch
[fetchfn]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
[`fetch_with_str`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str

View File

@ -0,0 +1,15 @@
# Cargo Features in `web-sys`
To keep `web-sys` building as fast as possible, there is a cargo feature for
every type defined in `web-sys`. To access that type, you must enable its
feature. To access a method, you must enable the feature for its `self` type and
the features for each of its argument types. In the [API documentation][], every
method lists the features that are required to enable it.
For example, [the `WebGlRenderingContext::compile_shader` function][compile_shader] requires these features:
* `WebGlRenderingContext`, because that is the method's `self` type
* `WebGlShader`, because it takes an argument of that type
[API documentation]: https://rustwasm.github.io/wasm-bindgen/api/web_sys
[compile_shader]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.WebGlRenderingContext.html#method.compile_shader

View File

@ -0,0 +1,29 @@
# 2D Canvas
Drawing a smiley face with the 2D canvas API. This is a port of part of [this
MDN
tutorial](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes#Moving_the_pen)
to `web-sys`.
[See the full source at
`wasm-bindgen/examples/canvas`.](https://github.com/rustwasm/wasm-bindgen/tree/master/examples/canvas)
![A smiley face](./2d-canvas.png)
## `Cargo.toml`
The `Cargo.toml` enables features necessary to query the DOM and work with 2D
canvas.
```toml
{{#include ../../../../examples/canvas/Cargo.toml}}
```
## `src/lib.rs`
Gets the `<canvas>` element, creates a 2D rendering context, and draws the
smiley face.
```rust
{{#include ../../../../examples/canvas/src/lib.rs}}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,23 @@
# The `fetch` API
This example uses the `fetch` API to make an HTTP request to the GitHub API and
then parses the resulting JSON.
[See the full source at
`wasm-bindgen/examples/fetch`.](https://github.com/rustwasm/wasm-bindgen/tree/master/examples/fetch)
## `Cargo.toml`
The `Cargo.toml` enables a number of features related to the `fetch` API and
types used: `Headers`, `Request`, etc. It also enables `wasm-bindgen`'s `serde`
support.
```toml
{{#include ../../../../examples/fetch/Cargo.toml}}
```
## `src/lib.rs`
```rust
{{#include ../../../../examples/fetch/src/lib.rs}}
```

View File

@ -0,0 +1,3 @@
# Examples of using `web-sys`
This subsection contains examples of using the `web-sys` crate.

View File

@ -0,0 +1,36 @@
# WebAudio
This example creates an [FM
oscillator](https://en.wikipedia.org/wiki/Frequency_modulation_synthesis) using
the [WebAudio
API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and
`web-sys`.
[See the full source at
`wasm-bindgen/examples/webaudio`.](https://github.com/rustwasm/wasm-bindgen/tree/master/examples/webaudio)
## `Cargo.toml`
The `Cargo.toml` enables the types needed to use the relevant bits of the
WebAudio API.
```toml
{{#include ../../../../examples/webaudio/Cargo.toml}}
```
## `src/lib.rs`
The Rust code implements the FM oscillator.
```rust
{{#include ../../../../examples/webaudio/src/lib.rs}}
```
## `index.js`
A small bit of JavaScript glues the rust module to input widgets and translates
events into calls into wasm code.
```js
{{#include ../../../../examples/webaudio/index.js}}
```

View File

@ -0,0 +1,20 @@
# Function Overloads
Many Web APIs are overloaded to take different types of arguments or to skip
arguments completely. `web-sys` contains multiple bindings for these functions
that each specialize to a particular overload and set of argument types.
For example, [the `fetch` API][mdn-fetch] can be given a URL string, or a
`Request` object, and it might also optionally be given a `RequestInit` options
object. Therefore, we end up with these `web-sys` functions that all bind to the
`window.fetch` function:
* [`Window::fetch_with_str`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str)
* [`Window::fetch_with_request`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request)
* [`Window::fetch_with_str_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/str_and_inituct.Window.html#method.fetch_with_str_and_init)
* [`Window::fetch_with_request_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request_and_init)
Note that different overloads can use different interfaces, and therefore can
require different sets of cargo features to be enabled.
[mdn-fetch]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

View File

@ -0,0 +1,23 @@
# The `web-sys` Crate
The `web-sys` crate provides raw `wasm-bindgen` imports for all of the Web's
APIs. This includes:
* `window.fetch`
* `Node.prototype.appendChild`
* WebGL
* WebAudio
* and many more!
It's sort of like the `libc` crate, but for the Web.
It does *not* include the JavaScript APIs that are guaranteed to exist in all
standards-compliant ECMAScript environments, such as `Array`, `Date`, and
`eval`. Bindings for these APIs can be found in [the `js-sys` crate][js-sys].
## API Documentation
[**Read the `web-sys` API documentation here!**][api]
[api]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/
[js-sys]: https://crates.io/crates/js-sys

View File

@ -0,0 +1,22 @@
# Type Translations in `web-sys`
Most of the types specified in [WebIDL (the interface definition language for
all Web APIs)][webidl] have relatively straightforward translations into
`web-sys`, but it's worth calling out a few in particular:
* `BufferSource` and `ArrayBufferView` - these two types show up in a number of
APIs that generally deal with a buffer of bytes. We bind them in `web-sys`
with two different types, `js_sys::Object` and `&mut [u8]`. Using
`js_sys::Object` allows passing in arbitrary JS values which represent a view
of bytes (like any typed array object), and `&mut [u8]` allows using a raw
slice in Rust. Unfortunately we must pessimistically assume that JS will
modify all slices as we don't currently have information of whether they're
modified or not.
* Callbacks are all represented as `js_sys::Function`. This means that all
callbacks going through `web-sys` are a raw JS value. You can work with this
by either juggling actual `js_sys::Function` instances or you can create a
`Closure<FnMut(...)>`, extract the underlying `JsValue` with `as_ref`, and
then use `JsCast::unchecked_ref` to convert it to a `js_sys::Function`.
[webidl]: https://heycam.github.io/webidl/

View File

@ -0,0 +1,61 @@
# Using `web-sys`
## Add `web-sys` as a dependency to your `Cargo.toml`
***Note:** `web-sys` is not available on crates.io yet, so you'll need to depend
on the git version of it, and of `wasm-bindgen`:*
```toml
[dependencies]
wasm-bindgen = { git = "https://github.com/rustwasm/wasm-bindgen" }
web-sys = {
git = "https://github.com/rustwasm/wasm-bindgen",
features = [
]
}
```
## Enable the cargo features for the APIs you're using
To keep build times super speedy, [`web-sys` gates each Web interface behind a
cargo feature](./cargo-features.html). Find the type or method you want to use
in the [API documentation][api]; it will list the features that must be enabled
to access that API.
For example, if we're looking for [the `window.resizeTo`
function][js-resize-to], we would [search for `resizeTo` in the API
documentation][search-resize-to]. We would find [the
`web_sys::Window::resize_to` function][rust-resize-to], which requires the
`Window` feature. To get access to that function, we enable the `Window` feature
in `Cargo.toml`:
```toml
web-sys = {
git = "https://github.com/rustwasm/wasm-bindgen",
features = [
"Window",
]
}
```
## Call the method!
```rust
extern crate web_sys;
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
use web_sys::Window;
#[wasm_bindgen]
pub fn make_the_window_small() {
// Resize the window to 500px by 500px.
Window::resize_to(500, 500)
.expect("could not resize the window");
}
```
[api]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/
[js-resize-to]: https://developer.mozilla.org/en-US/docs/Web/API/window/resizeTo
[search-resize-to]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/?search=resizeTo
[rust-resize-to]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.resize_to