Planet MozillaMozilla Communities Speaker Series #PrivacyMonth

Mozilla Communities Speaker Series #PrivacyMonth As a part of the Privacy Month initiative, Mozilla volunteers are hosting a couple of speaker series webinars on Privacy, Security and related topics. The...

Planet MozillaMozilla Communities Speaker Series #PrivacyMonth

Mozilla Communities Speaker Series #PrivacyMonth As a part of the Privacy Month initiative, Mozilla volunteers are hosting a couple of speaker series webinars on Privacy, Security and related topics. The...

Planet MozillaSeeking SOS Fund Projects

I’m spending some time over the next few days looking for the next round of projects which might benefit from an SOS Fund security audit. (Here‘s what’s been done and published so far; a few more are in the works.) The criteria for what makes a good project are recorded on the MOSS website. We have two hard-and-fast criteria:

  • The software must be open source/free software, with a license that is OSI-certified and/or FSF-approved
  • The software must be actively maintained

And then we have a series of factors we consider when evaluating an application:

  • How commonly used is the software?
  • Is the software network-facing or does it regularly process untrusted data?
  • How vital is the software to the continued functioning of the Internet or the Web?
  • Is the project known for something besides the code we are relying on?
  • Does the software depend on closed-source code, e.g. in a web service?
  • Are the software’s maintainers aware of and supportive of the application for support from the SOS fund?
  • Has the software been audited before? If so, when and how extensively? Was the audit made public? If so, where?
  • Does the software have existing corporate backing or involvement?

People do have a tendency to suggest the entirely impractical, such as “Linux Mint” or “Copperhead OS”. We aren’t able to do full audits on corpuses of software of that size. In general, if it’s more than about 200kloc, we are going to have to pick and choose.

If you know of a project which fits, please submit a suggestion, or drop me an email. Thanks!

Planet MozillaThese Weeks in Firefox: Issue 30

Highlights

Friends of the Firefox team

Project Updates

Add-ons

Activity Stream

Browser Architecture

  • XUL Template feature has been removed (thanks Gijs)
  • Metabug filed for removing XUL overlays
  • XBL replacement has continued to make good progress. Expect a newsletter update soon.
    • Bindings removed since last meeting – thanks to Dão, Ian Moody, ntim, Paolo, 86ecce74, and Myk: tabbrowser-close-tab-button, toolbarbutton-image, datetimepicker-base, timepicker, datepicker, datepicker-grid, datepicker-popup, timed-textbox, addengine-icon, expander, preferences, preference, prefwindow, prefpane, panebutton, dialogheader, optionsDialog, stringbundleset.

Firefox Core Engineering

  • Crash machinery:
    • The last piece of crash ping support (bringing the crash ping to Fennec) will be done within the month. Follow along here.
  • Migration performance improvements:
    • Doug Thayer has landed several performance improvements to migration in 59 (as he did in 58). Too many to list, so check them out.
  • Update Agent:
    • Will allow for checking for and downloading of updates async from Firefox.
    • Was originally slated for incremental release, but has been changed to release as a complete feature, targeted for 62.
    • Tracking meta bug is 1343669.
  • Installer:
    • Rename of Windows shortcut (first attempted for 57) from “Mozilla Firefox” to just “Firefox” landed in beta 58. (bringing Windows in line with Mac and Linux)
  • Enterprise Policies Engine:

Form Autofill

  • V2 features were complete and got Pre-Beta sign off (yellow)
  • The edit dialog of Address Autofill in Preferences now is able to change the layout according to the selected country
  • Implemented a summary in the update doorhanger to let users know which profile will be updated
  • Slightly refactored the core modules for better supporting sections
  • Fixed some issues of Clear Form button caused by the cache mechanism in the autocomplete module
  • Fixed a few RTL issues
  • Fixed a lot of website compatible issues

Lint

  • Lint’s dependencies (python & node) should now be installed when running ./mach bootstrap (on Mac/Linux, Windows has them already installed for mozilla-build).

Performance

Privacy/Security

Search and Navigation

Address Bar & Search
Places

Web Payments

  • Landed our multiline rich-select custom element that will be used for the address selector as well as the basic-card selector.

Planet Mozilla“Ewoks or Porgs?” and Other Important Questions

You ever go to a party where you decide to ask people REAL questions about themselves, rather than just boring chit chat? Us, too! That’s why we’ve included questions that … Read more

The post “Ewoks or Porgs?” and Other Important Questions appeared first on The Firefox Frontier.

Planet MozillaOxidizing Source Maps with Rust and WebAssembly

Tom Tromey and I have replaced the most performance-sensitive portions of the source-map JavaScript Library’s source map parser with Rust code that is compiled to WebAssembly. The WebAssembly is up to 5.89 times faster than the JavaScript implementation on realistic benchmarks operating on real world source maps. Additionally, performance is also more consistent: relative standard deviations decreased.

We removed JavaScript code that had been written in a convoluted and unidiomatic way in the name of performance, and replaced it with idiomatic Rust code that performs even better.

We hope that, by sharing our experience, we inspire others to follow suit and rewrite performance-sensitive JavaScript in Rust via WebAssembly.

Background

The Source Map Format

The source map format provides a bidirectional mapping between locations in some generated JavaScript code that was emitted by a compiler0, minifier, or package bundling tool back to locations in the original sources that a programmer authored. JavaScript developer tools use source maps to symbolicate backtraces, and to implement source-level stepping in debuggers. Source maps encode debug information similar to that found in DWARF’s .debug_line section.

A source map is a JSON object with a handful of fields. The "mappings" field is a string that makes up the bulk of the source map, and contains the bidirectional mappings between source and object locations.

We will describe the "mappings" string’s grammar in extended Backus-Naur form (EBNF).

Mappings are grouped by generated JavaScript line number, which is incremented every time a semicolon lays between individual mappings. When consecutive mappings are on the same generated JavaScript line, they are separated by a comma:

<mappings> = [ <generated-line> ] ';' <mappings>
           | ''
           ;

<generated-line> = <mapping>
                 | <mapping> ',' <generated-line>
                 ;

Each individual mapping has a location in the generated JavaScript, and optionally a location in the original source text, which might also contain an associated name:

<mapping> = <generated-column> [ <source> <original-line> <original-column> [ <name> ] ] ;

Every component of a mapping is a Variable Length Quantity (VLQ) encoded integer. Filenames and associated names are encoded as indices into side tables stored in other fields of the source map’s JSON object. Every value is relative to the last occurrence of its type, i.e. a given <source> value is the delta since the previous <source> value. These deltas tend to be smaller than their absolute values, which means they are more compact when encoded:

<generated-column> = <vlq> ;
<source> = <vlq> ;
<original-line> = <vlq> ;
<original-column> = <vlq> ;
<name> = <vlq> ;

Each character in a VLQ is drawn from a set of 64 printable ASCII characters comprising the upper- and lower-case letters, the decimal digits, and some symbols. Each character represents a particular 6-bit value. The most significant bit is set on all but the last character in the VLQ; the remaining five bits are prepended to the integer value represented.

Rather than attempting to translate this definition into EBNF, we provide pseudocode for parsing and decoding a single VLQ below:

constant SHIFT = 5
constant CONTINUATION_BIT = 1 << SHIFT
constant MASK = (1 << SHIFT) - 1

decode_vlq(input):
    let accumulation = 0
    let shift = 0

    let next_digit_part_of_this_number = true;
    while next_digit_part_of_this_number:
        let [character, ...rest] = input
        let digit = decode_base_64(character)
        accumulation += (digit & MASK) << shift
        shift += SHIFT;
        next_digit_part_of_this_number = (digit & CONTINUATION_BIT) != 0
        input = rest

    let is_negative = accumulation & 1
    let value = accumulation >> 1
    if is_negative:
        return (-value, input)
    else:
        return (value, input)

The source-map JavaScript Library

The source-map library is published on npm and maintained by the Firefox Developer Tools team at Mozilla. It is one of the most transitively depended-upon libraries by the JavaScript community, and is downloaded 10M times per week.

Like many software projects, the source-map library was written first for correctness, and then later modified to improve performance. By this time, it has seen a good amount of performance work.

When consuming source maps, the majority of time is spent parsing the "mappings" string and constructing a pair of arrays: one sorted by generated JavaScript location, the other sorted by original source location. Queries use binary search on the appropriate array. The parsing and sorting both happen lazily, and don’t occur until a particular query requires it. This allows a debugger to list sources, for example, without needing to parse and sort mappings. Once parsed and sorted, querying tends not to be a performance bottleneck.

The VLQ decoding function takes a string as input, parses and decodes a VLQ from the string, and returns a pair of the value and the rest of the input that was not consumed. This function was originally written in an idiomatic, referentially transparent style to return the pair as an Object literal with two properties:

function decodeVlqBefore(input) {
  // ...
  return { result, rest };
}

Returning a pair that way was found to be costly. JavaScript Just-in-Time (JIT) compilers’ optimized compilation tiers were unable to eliminate this allocation. Because the VLQ decoding routine is called frequently, this allocation was creating undue pressure on the garbage collector, leading to more frequent garbage collection pauses.

To remove the allocation, we modified the procedure to take a second parameter: an Object that gets mutated and serves as an out parameter. Rather than returning a freshly allocated Object, the out parameter’s properties are overwritten. This way, we could reuse the same object for every VLQ parse. This is less idiomatic, but much more performant:

function decodeVlqAfter(input, out) {
  // ...
  out.result = result;
  out.rest = rest;
}

When reaching an unexpected end-of-string or an invalid base 64 character, the VLQ decoding function would throw an Error. We found that JavaScript JITs would emit faster code when we changed the single base 64 digit decoding function to return -1 on failure instead of throwing an Error. This is less idiomatic, but once again it improved performance.

When profiling with SpiderMonkey’s JITCoach prototype, we found that SpiderMonkey’s JIT was using slow-path polymorphic inline caches for Object property gets and sets on our mapping Objects. The JIT was not emitting the desired fast-path code with direct object slot accesses, because it was not detecting a common “shape” (or “hidden class”) shared by all mapping Objects. Some properties would be added in a different order, or omitted completely, for example, when there was no associated name for a mapping. By creating a constructor for mapping Objects that initialized every property we would ever use, we helped the JIT create a common shape for all mapping Objects. This resulted in another performance improvement:

function Mapping() {
  this.generatedLine = 0;
  this.generatedColumn = 0;
  this.lastGeneratedColumn = null;
  this.source = null;
  this.originalLine = null;
  this.originalColumn = null;
  this.name = null;
}

When sorting the two mapping arrays, we use custom comparator functions. When the source-map library was first written, SpiderMonkey’s Array.prototype.sort was implemented in C++ for performance1. However, when sort is passed an explicit comparator function and a large array, the sorting code must call the comparator function many times. Calls from C++ to JavaScript are relatively expensive, so passing a custom comparator function made the sorts perform poorly.

To avoid this, we implemented our own Quicksort in JavaScript. This not only avoided the C++-to-JavaScript calls, but also let the JavaScript JITs inline the comparator function into the sorting function, enabling further optimizations. This brought us another large performance boost, and again made the code less idiomatic.

WebAssembly

WebAssembly is a new, low-level, architecture-independent byte code developed for the Web. It is designed for safe and efficient execution and compact binaries, and is developed in the open as a Web standard. It is supported by all major Web browsers.

WebAssembly exposes a stack machine that maps well onto modern processor architectures. Its instructions operate on a large, linear buffer of memory. It does not support garbage collection, although extending it to work with garbage-collected JavaScript objects is planned for the future. Control flow is structured, rather than allowing arbitrary jumps and labels. It is designed to be deterministic and consistent, even where different architectures diverge on edge cases, such as out-of-range shifts, overflows, and canonicalizing NaNs.

WebAssembly aims to match or beat native execution speed. It is currently within 1.5x native execution on most benchmarks.

Because of the lack of a garbage collector, languages that target WebAssembly are currently limited to languages without a runtime or garbage collector to speak of, unless the collector and runtime are also compiled to WebAssembly. That doesn’t happen in practice. Right now, the languages developers are actually compiling to WebAssembly are C, C++, and Rust.

Rust

Rust is a systems programming language that emphasizes safety and speed. It provides memory safety without relying on a garbage collector. Instead, it statically tracks ownership and borrowing of resources to determine where to emit code to run destructors or free memory.

Rust is in a great position for compiling to WebAssembly. Since Rust does not require garbage collection, Rust authors don’t have to jump through extra hoops to target WebAssembly. Rust has many of the goodies that Web developers have come to expect, and which C and C++ lack:

  • Easy building, packaging, publishing, sharing, and documenting of libraries. Rust has rustup, cargo, and the crates.io ecosystem; C and C++ don’t have any widely used equivalent.

  • Memory safety. Not having to chase down memory corruption in gdb. Rust prevents most of these pitfalls at compile time.

Parsing and Querying Source Maps in Rust

When we decided to rewrite the hot portions of source map parsing and querying in Rust, we had to decide where the boundary between JavaScript and the WebAssembly emitted by the Rust compiler would be. Crossing the boundary between JavaScript’s JITed code to WebAssembly and back currently imposes overhead similar to that of the C++-to-JavaScript calls we mentioned earlier.2 So it was important to place this boundary in so as to minimize the number of times we had to cross it.

A poor choice of where to place the boundary between WebAssembly and JavaScript would have been at the VLQ decoding function. The VLQ decoding function is invoked between one and four times for every mapping encoded in the "mappings" string, therefore we would have to cross the JavaScript-to-WebAssembly boundary that many times during parsing.

Instead, we decided to parse the whole "mappings" string in Rust/WebAssembly, and then keep the parsed data there. The WebAssembly heap owns the parsed data in its queryable form. This means that we never have to copy the data out of the WebAssembly heap, which would involve crossing the JavaScript/WebAssembly boundary more often. Instead, we only cross that boundary once each way for every query, plus once each way for every mapping that is a result of that query. Queries tend to produce a single result, or perhaps a handful.

To have confidence that our Rust implementation was correct, not only did we ensure that our new implementation passed the source-map library’s existing test suite, but we also wrote a suite of quickcheck property tests. These tests construct random "mappings" input strings, parse them, and then assert that various properties hold.

Our Rust implementation of "mappings" parsing and querying is available on crates.io as the source-map-mappings crate.

Base 64 Variable Length Quantities

The first step to parsing a source map’s mappings is decoding VLQs. We implemented a vlq library in Rust and published it to crates.io.

The decode64 function decodes a single base 64 digit. It uses pattern matching and the idiomatic Result-style error handling.

A Result<T, E> is either Ok(v), indicating that the operation succeeded and produced the value v of type T, or Err(error), indicating that the operation failed, with the value error of type E providing the details. The decode64 function’s return type Result<u8, Error> indicates that it returns a u8 value on success, or a vlq::Error value on failure:

fn decode64(input: u8) -> Result<u8, Error> {
    match input {
        b'A'...b'Z' => Ok(input - b'A'),
        b'a'...b'z' => Ok(input - b'a' + 26),
        b'0'...b'9' => Ok(input - b'0' + 52),
        b'+' => Ok(62),
        b'/' => Ok(63),
        _ => Err(Error::InvalidBase64(input)),
    }
}

With the decode64 function in hand, we can decode whole VLQ values. The decode function takes a mutable reference to an iterator of bytes as input, consumes as many bytes as it needs to decode the VLQ, and finally returns a Result of the decoded value:

pub fn decode<B>(input: &mut B) -> Result<i64>
where
    B: Iterator<Item = u8>,
{
    let mut accum: u64 = 0;
    let mut shift = 0;

    let mut keep_going = true;
    while keep_going {
        let byte = input.next().ok_or(Error::UnexpectedEof)?;
        let digit = decode64(byte)?;
        keep_going = (digit & CONTINUED) != 0;

        let digit_value = ((digit & MASK) as u64)
            .checked_shl(shift as u32)
            .ok_or(Error::Overflow)?;

        accum = accum.checked_add(digit_value).ok_or(Error::Overflow)?;
        shift += SHIFT;
    }

    let abs_value = accum / 2;
    if abs_value > (i64::MAX as u64) {
        return Err(Error::Overflow);
    }

    // The low bit holds the sign.
    if (accum & 1) != 0 {
        Ok(-(abs_value as i64))
    } else {
        Ok(abs_value as i64)
    }
}

Unlike the JavaScript code it is replacing, this code does not sacrifice idiomatic error handling for performance. Idiomatic error handling is performant, and does not involve boxing values on the heap or unwinding the stack.

The "mappings" String

We begin by defining some small helper functions. The is_mapping_separator predicate function returns true if the given byte is a separator between mappings, and false if it is not. There is a nearly identical JavaScript function:

#[inline]
fn is_mapping_separator(byte: u8) -> bool {
    byte == b';' || byte == b','
}

Next, we define a helper function for reading a single VLQ delta value and adding it to the previous value. The JavaScript does not have an equivalent function, and instead repeats this code each time it wants to read a VLQ delta. JavaScript does not let us control how our parameters are represented in memory, but Rust does. We cannot pass a reference to a number in a zero-cost manner with JavaScript. We could pass a reference to an Object with a number property or we could close over the number’s variable in a local closure function, but both of these solutions have associated runtime costs:

#[inline]
fn read_relative_vlq<B>(
    previous: &mut u32,
    input: &mut B,
) -> Result<(), Error>
where
    B: Iterator<Item = u8>,
{
    let decoded = vlq::decode(input)?;
    let (new, overflowed) = (*previous as i64).overflowing_add(decoded);
    if overflowed || new > (u32::MAX as i64) {
        return Err(Error::UnexpectedlyBigNumber);
    }

    if new < 0 {
        return Err(Error::UnexpectedNegativeNumber);
    }

    *previous = new as u32;
    Ok(())
}

All in all, the Rust implementation of "mappings" parsing is quite similar to the JavaScript implementation it replaces. However, with the Rust, we have control over what is or isn’t boxed, whereas in the JavaScript, we do not. Every single parsed mapping Object is boxed in the JavaScript implementation. Much of the Rust implementation’s advantage stems from avoiding these allocations and their associated garbage collections:

pub fn parse_mappings(input: &[u8]) -> Result<Mappings, Error> {
    let mut generated_line = 0;
    let mut generated_column = 0;
    let mut original_line = 0;
    let mut original_column = 0;
    let mut source = 0;
    let mut name = 0;

    let mut mappings = Mappings::default();
    let mut by_generated = vec![];

    let mut input = input.iter().cloned().peekable();

    while let Some(byte) = input.peek().cloned() {
        match byte {
            b';' => {
                generated_line += 1;
                generated_column = 0;
                input.next().unwrap();
            }
            b',' => {
                input.next().unwrap();
            }
            _ => {
                let mut mapping = Mapping::default();
                mapping.generated_line = generated_line;

                read_relative_vlq(&mut generated_column, &mut input)?;
                mapping.generated_column = generated_column as u32;

                let next_is_sep = input.peek()
                    .cloned()
                    .map_or(true, is_mapping_separator);
                mapping.original = if next_is_sep {
                    None
                } else {
                    read_relative_vlq(&mut source, &mut input)?;
                    read_relative_vlq(&mut original_line, &mut input)?;
                    read_relative_vlq(&mut original_column, &mut input)?;

                    let next_is_sep = input.peek()
                        .cloned()
                        .map_or(true, is_mapping_separator);
                    let name = if next_is_sep {
                        None
                    } else {
                        read_relative_vlq(&mut name, &mut input)?;
                        Some(name)
                    };

                    Some(OriginalLocation {
                        source,
                        original_line,
                        original_column,
                        name,
                    })
                };

                by_generated.push(mapping);
            }
        }
    }

    quick_sort::<comparators::ByGeneratedLocation, _>(&mut by_generated);
    mappings.by_generated = by_generated;
    Ok(mappings)
}

Finally, we still use a custom Quicksort implementation in the Rust code, and this is the only unidiomatic thing about the Rust code. We found that although the standard library’s built-in sorting is much faster when targeting native code, our custom Quicksort is faster when targeting WebAssembly. (This is surprising, but we have not investigated why it is so.)

Interfacing with JavaScript

The WebAssembly foreign function interface (FFI) is constrained to scalar values, so any function exposed from Rust to JavaScript via WebAssembly may only use scalar value types as parameters and return types. Therefore, the JavaScript code must ask the Rust to allocate a buffer with space for the "mappings" string and return a pointer to that buffer. Next, the JavaScript must copy the "mappings" string into that buffer, which it can do without crossing the FFI boundary because it has write access to the whole WebAssembly linear memory. After the buffer is initialized, the JavaScript calls the parse_mappings function and is returned a pointer to the parsed mappings. Then, the JavaScript can perform any number of queries through the WebAssembly API, passing in the pointer to the parsed mappings when doing so. Finally, when no more queries will be made, the JavaScript tells the WebAssembly to free the parsed mappings.

Exposing WebAssembly APIs from Rust

All the WebAssembly APIs we expose live in a small glue crate that wraps the source-map-mappings crate. This separation is useful because it allows us to test the source-map-mappings crate with our native host target, and only compile the WebAssembly glue when targeting WebAssembly.

In addition to the constraints of what types can cross the FFI boundary, each exported function requires that

  • it has the #[no_mangle] attribute so that it is not name-mangled, and JavaScript can easily call it, and
  • it is marked extern "C" so that it is publicly exported in the final .wasm file.

Unlike the core library, this glue code that exposes functionality to JavaScript via WebAssembly does, by necessity, frequently use unsafe. Calling extern functions and using pointers received from across an FFI boundary is always unsafe, because the Rust compiler cannot verify the safety of the other side. Ultimately this is less concerning with WebAssembly than it might be with other targets — the worst we can do is trap (which causes an Error to be raised on the JavaScript side) or return an incorrect answer. Much tamer than the worst case scenarios with native binaries where executable memory is in the same address space as writable memory, and an attacker can trick the program into jumping to memory where they’ve inserted some shell code.

The simplest function we export is the function to get the last error that occurred in the library. This provides similar functionality as errno from libc, and is called by JavaScript when some API call fails and the JavaScript would like to know what kind of failure it was. We always maintain the most recent error in a global, and this function retrieves that error value:

static mut LAST_ERROR: Option<Error> = None;

#[no_mangle]
pub extern "C" fn get_last_error() -> u32 {
    unsafe {
        match LAST_ERROR {
            None => 0,
            Some(e) => e as u32,
        }
    }
}

The first interaction between JavaScript and Rust is allocating a buffer with space to hold the "mappings" string. We desire an owned, contiguous chunk of u8s, which suggests using Vec<u8>, but we want to expose a single pointer to JavaScript. A single pointer can cross the FFI boundary, and is easier to wrangle on the JavaScript side. We can either add a layer of indirection with Box<Vec<u8>> or we can save the extra data needed to reconstruct the vector on the side. We choose the latter approach.

A vector is a triple of

  1. a pointer to the heap-allocated elements,
  2. the capacity of that allocation, and
  3. the length of the initialized elements.

Since we are only exposing the pointer to the heap-allocated elements to JavaScript, we need a way to save the length and capacity so we can reconstruct the Vec on demand. We allocate two extra words of space and store the length and capacity in the first two words of the heap elements. Then we give JavaScript a pointer to the space just after those extra words:

#[no_mangle]
pub extern "C" fn allocate_mappings(size: usize) -> *mut u8 {
    // Make sure that we don't lose any bytes from size in the remainder.
    let size_in_units_of_usize = (size + mem::size_of::<usize>() - 1)
        / mem::size_of::<usize>();

    // Make room for two additional `usize`s: we'll stuff capacity and
    // length in there.
    let mut vec: Vec<usize> = Vec::with_capacity(size_in_units_of_usize + 2);

    // And do the stuffing.
    let capacity = vec.capacity();
    vec.push(capacity);
    vec.push(size);

    // Leak the vec's elements and get a pointer to them.
    let ptr = vec.as_mut_ptr();
    debug_assert!(!ptr.is_null());
    mem::forget(vec);

    // Advance the pointer past our stuffed data and return it to JS,
    // so that JS can write the mappings string into it.
    let ptr = ptr.wrapping_offset(2) as *mut u8;
    assert_pointer_is_word_aligned(ptr);
    ptr
}

After initializing the buffer for the "mappings" string, JavaScript passes ownership of the buffer to parse_mappings, which parses the string into a queryable structure. A pointer to the queryable Mappings structure is returned, or NULL if there was some kind of parse failure.

The first thing that parse_mappings must do is recover the Vec‘s length and capacity. Next, it constructs a slice of the "mappings" string data, constrains the lifetime of the slice to the current scope to double check that we don’t access the slice again after its deallocated, and call into our library’s "mappings" string parsing function. Regardless whether parsing succeeded or not, we deallocate the buffer holding the "mappings" string, and return a pointer to the parsed structure or save any error that may have occurred and return NULL.

/// Force the `reference`'s lifetime to match the `scope`'s
/// lifetime. Certain `unsafe` operations, such as dereferencing raw
/// pointers, return references with un-constrained lifetimes, and we
/// use this function to ensure we can't accidentally use the
/// references after they become invalid.
#[inline]
fn constrain<'a, T>(_scope: &'a (), reference: &'a T) -> &'a T
where
    T: ?Sized
{
    reference
}

#[no_mangle]
pub extern "C" fn parse_mappings(mappings: *mut u8) -> *mut Mappings {
    assert_pointer_is_word_aligned(mappings);
    let mappings = mappings as *mut usize;

    // Unstuff the data we put just before the pointer to the mappings
    // string.
    let capacity_ptr = mappings.wrapping_offset(-2);
    debug_assert!(!capacity_ptr.is_null());
    let capacity = unsafe { *capacity_ptr };

    let size_ptr = mappings.wrapping_offset(-1);
    debug_assert!(!size_ptr.is_null());
    let size = unsafe { *size_ptr };

    // Construct the input slice from the pointer and parse the mappings.
    let result = unsafe {
        let input = slice::from_raw_parts(mappings as *const u8, size);
        let this_scope = ();
        let input = constrain(&this_scope, input);
        source_map_mappings::parse_mappings(input)
    };

    // Deallocate the mappings string and its two prefix words.
    let size_in_usizes = (size + mem::size_of::<usize>() - 1) / mem::size_of::<usize>();
    unsafe {
        Vec::<usize>::from_raw_parts(capacity_ptr, size_in_usizes + 2, capacity);
    }

    // Return the result, saving any errors on the side for later inspection by
    // JS if required.
    match result {
        Ok(mappings) => Box::into_raw(Box::new(mappings)),
        Err(e) => {
            unsafe {
                LAST_ERROR = Some(e);
            }
            ptr::null_mut()
        }
    }
}

When we run queries, we need a way to translate the results across the FFI boundary. The results of a query are either a single Mapping or set of many Mappings, and a Mapping can’t cross the FFI boundary as-is unless we box it. We do not wish to box Mappings since we would also need to provide getters for each of its fields, causing code bloat on top of the costs of allocation and indirection. Our solution is to call an imported function for each Mapping query result.

The mappings_callback is an extern function without definition, which translates to an imported function in WebAssembly that the JavaScript must provide when instantiating the WebAssembly module. The mappings_callback takes a Mapping that is exploded into its member parts: each field in a transitively flattened Mapping is translated into a parameter so that it can cross the FFI boundary. For Option<T> members, we add a bool parameter that serves as a discriminant, determining whether the Option<T> is Some or None, and therefore whether the following T parameter is valid or garbage:

extern "C" {
    fn mapping_callback(
        // These two parameters are always valid.
        generated_line: u32,
        generated_column: u32,

        // The `last_generated_column` parameter is only valid if
        // `has_last_generated_column` is `true`.
        has_last_generated_column: bool,
        last_generated_column: u32,

        // The `source`, `original_line`, and `original_column`
        // parameters are only valid if `has_original` is `true`.
        has_original: bool,
        source: u32,
        original_line: u32,
        original_column: u32,

        // The `name` parameter is only valid if `has_name` is `true`.
        has_name: bool,
        name: u32,
    );
}

#[inline]
unsafe fn invoke_mapping_callback(mapping: &Mapping) {
    let generated_line = mapping.generated_line;
    let generated_column = mapping.generated_column;

    let (
        has_last_generated_column,
        last_generated_column,
    ) = if let Some(last_generated_column) = mapping.last_generated_column {
        (true, last_generated_column)
    } else {
        (false, 0)
    };

    let (
        has_original,
        source,
        original_line,
        original_column,
        has_name,
        name,
    ) = if let Some(original) = mapping.original.as_ref() {
        let (
            has_name,
            name,
        ) = if let Some(name) = original.name {
            (true, name)
        } else {
            (false, 0)
        };

        (
            true,
            original.source,
            original.original_line,
            original.original_column,
            has_name,
            name,
        )
    } else {
        (
            false,
            0,
            0,
            0,
            false,
            0,
        )
    };

    mapping_callback(
        generated_line,
        generated_column,
        has_last_generated_column,
        last_generated_column,
        has_original,
        source,
        original_line,
        original_column,
        has_name,
        name,
    );
}

All of the exported query functions have a similar structure. They begin by converting a raw *mut Mappings pointer into an &mut Mappings mutable reference. The &mut Mappings lifetime is constrained to the current scope to enforce that it is only used in this function call, and not saved away somewhere where it might be used after it is deallocated. Next, the exported query function forwards the query to the corresponding Mappings method. For each resulting Mapping, the exported function invokes the mapping_callback.

A typical example is the exported all_generated_locations_for query function, which wraps the Mappings::all_generated_locations_for method, and finds all the mappings corresponding to the given original source location:

#[inline]
unsafe fn mappings_mut<'a>(
    _scope: &'a (),
    mappings: *mut Mappings,
) -> &'a mut Mappings {
    mappings.as_mut().unwrap()
}

#[no_mangle]
pub extern "C" fn all_generated_locations_for(
    mappings: *mut Mappings,
    source: u32,
    original_line: u32,
    has_original_column: bool,
    original_column: u32,
) {
    let this_scope = ();
    let mappings = unsafe { mappings_mut(&this_scope, mappings) };

    let original_column = if has_original_column {
        Some(original_column)
    } else {
        None
    };

    let results = mappings.all_generated_locations_for(
        source,
        original_line,
        original_column,
    );
    for m in results {
        unsafe {
            invoke_mapping_callback(m);
        }
    }
}

Finally, when the JavaScript is finished querying the Mappings, it must deallocate them with the exported free_mappings function:

#[no_mangle]
pub extern "C" fn free_mappings(mappings: *mut Mappings) {
    unsafe {
        Box::from_raw(mappings);
    }
}

Compiling Rust into .wasm Files

The addition of the wasm32-unknown-unknown target makes compiling Rust into WebAssembly possible, and rustup makes installing a Rust compiler toolchain targeting wasm32-unknown-unknown easy:

$ rustup update
$ rustup target add wasm32-unknown-unknown

Now that we have a wasm32-unknown-unknown compiler, the only difference between building for our host platform and WebAssembly is a --target flag:

$ cargo build --release --target wasm32-unknown-unknown

The resulting .wasm file is at target/wasm32-unknown-unknown/release/source_map_mappings_wasm_api.wasm.

Although we now have a working .wasm file, we are not finished: this .wasm file is still much larger than it needs to be. To produce the smallest .wasm file we can, we leverage the following tools:

  • wasm-gc, which is like a linker’s --gc-sections flag that removes unused object file sections, but for .wasm files instead of ELF, Mach-O, etc. object files. It finds all functions that are not transitively reachable from an exported function and removes them from the .wasm file.

  • wasm-snip, which is used to replace a WebAssembly function’s body with a single unreachable instruction. This is useful for manually removing functions that will never be called at runtime, but which the compiler and wasm-gc couldn’t statically prove were unreachable. Snipping a function can make other functions statically unreachable, so it makes sense to run wasm-gc again afterwards.

  • wasm-opt, which runs binaryen‘s optimization passes over a .wasm file, shrinking its size and improving its runtime performance. Eventually, when LLVM’s WebAssembly backend matures, this may no longer be necessary.

Our post-build pipeline goes wasm-gcwasm-snipwasm-gcwasm-opt.

Using WebAssembly APIs in JavaScript

The first concern when using WebAssembly in JavaScript is how to load the .wasm files. The source-map library is primarily used in three environments:

  1. Node.js
  2. The Web
  3. Inside Firefox Developer Tools

Different environments can have different methods for loading a .wasm file’s bytes into an ArrayBuffer that can then be compiled by the JavaScript runtime. On the Web and inside Firefox, we can use the standard fetch API to make an HTTP request to load the .wasm file. It is the library consumer’s responsibility to provide a URL pointing to the .wasm file before parsing any source maps. When used with Node.js, the library uses the fs.readFile API to read the .wasm file from disk. No initialization is required before parsing any source maps in this scenario. We provide a uniform interface regardless which environment the library is loaded in by using feature detection to select the correct .wasm loading implementation.

When compiling and instantiating the WebAssembly module, we must provide the mapping_callback. This callback cannot change across the lifetime of the instantiated WebAssembly module, but depending on what kind of query we are performing, we want to do different things with the resulting mappings. So the actual mapping_callback we provide is only responsible for translating the exploded mapping members into a structured object and then trampolining that result to a closure function that we set depending on what query we are running.

let currentCallback = null;

// ...

WebAssembly.instantiate(buffer, {
  env: {
    mapping_callback: function (
      generatedLine,
      generatedColumn,

      hasLastGeneratedColumn,
      lastGeneratedColumn,

      hasOriginal,
      source,
      originalLine,
      originalColumn,

      hasName,
      name
    ) {
      const mapping = new Mapping;
      mapping.generatedLine = generatedLine;
      mapping.generatedColumn = generatedColumn;

      if (hasLastGeneratedColumn) {
        mapping.lastGeneratedColumn = lastGeneratedColumn;
      }

      if (hasOriginal) {
        mapping.source = source;
        mapping.originalLine = originalLine;
        mapping.originalColumn = originalColumn;

        if (hasName) {
          mapping.name = name;
        }
      }

      currentCallback(mapping);
    }
  }
})

To make setting and unsetting the currentCallback ergonomic, we define a withMappingCallback helper that takes two functions: one to set as the currentCallback, and another to invoke immediately. Once the second function’s execution finishes, we reset the currentCallback to null. This is the JavaScript equivalent of RAII:

function withMappingCallback(mappingCallback, f) {
  currentCallback = mappingCallback;
  try {
    f();
  } finally {
    currentCallback = null;
  }
}

Recall that JavaScript’s first task, when parsing a source map, is to tell the WebAssembly to allocate space for the "mappings" string, and then copy the string into the allocated buffer:

const size = mappingsString.length;
const mappingsBufPtr = this._wasm.exports.allocate_mappings(size);
const mappingsBuf = new Uint8Array(
  this._wasm.exports.memory.buffer,
  mappingsBufPtr,
  size
);
for (let i = 0; i < size; i++) {
  mappingsBuf[i] = mappingsString.charCodeAt(i);
}

Once JavaScript has initialized the buffer, it calls the exported parse_mappings WebAssembly function and translates any failures into an Error that gets thrown.

const mappingsPtr = this._wasm.exports.parse_mappings(mappingsBufPtr);
if (!mappingsPtr) {
  const error = this._wasm.exports.get_last_error();
  let msg = `Error parsing mappings (code ${error}): `;
  // XXX: keep these error codes in sync with `fitzgen/source-map-mappings`.
  switch (error) {
    case 1:
      msg += "the mappings contained a negative line, column, source index or name index";
      break;
    case 2:
      msg += "the mappings contained a number larger than 2**32";
      break;
    case 3:
      msg += "reached EOF while in the middle of parsing a VLQ";
      break;
    case 4:
      msg += "invalid base 64 character while parsing a VLQ";
      break
    default:
      msg += "unknown error code";
      break;
  }

  throw new Error(msg);
}

this._mappingsPtr = mappingsPtr;

The various query methods that call into WebAssembly have similar structure, just like the exported functions on the Rust side do. They validate query parameters, set up a temporary mappings callback closure with withMappingCallback that aggregates results, calls into WebAssembly, and then returns the results.

Here is what allGeneratedPositionsFor looks like in JavaScript:

BasicSourceMapConsumer.prototype.allGeneratedPositionsFor = function ({
  source,
  line,
  column,
}) {
  const hasColumn = column === undefined;
  column = column || 0;

  source = this._findSourceIndex(source);
  if (source < 0) {
    return [];
  }

  if (originalLine < 1) {
    throw new Error("Line numbers must be >= 1");
  }

  if (originalColumn < 0) {
    throw new Error("Column numbers must be >= 0");
  }

  const results = [];

  this._wasm.withMappingCallback(
    m => {
      let lastColumn = m.lastGeneratedColumn;
      if (this._computedColumnSpans && lastColumn === null) {
        lastColumn = Infinity;
      }
      results.push({
        line: m.generatedLine,
        column: m.generatedColumn,
        lastColumn,
      });
    }, () => {
      this._wasm.exports.all_generated_locations_for(
        this._getMappingsPtr(),
        source,
        line,
        hasColumn,
        column
      );
    }
  );

  return results;
};

When JavaScript is done querying the source map, the library consumer should call SourceMapConsumer.prototype.destroy which then calls into the exported free_mappings WebAssembly function:

BasicSourceMapConsumer.prototype.destroy = function () {
  if (this._mappingsPtr !== 0) {
    this._wasm.exports.free_mappings(this._mappingsPtr);
    this._mappingsPtr = 0;
  }
};

Benchmarks

All tests were performed on a MacBook Pro from mid 2014 with a 2.8 GHz Intel Core i7 processor and 16 GB 1600 MHz DDR3 memory. The laptop was plugged into power for every test, and the benchmark Webpage was refreshed between tests. The tests were performed in Chrome Canary 65.0.3322.0, Firefox Nightly 59.0a1 (2018-01-15), and Safari 11.0.2 (11604.4.7.1.6)3. For each benchmark, to warm up the browser’s JIT compiler, we performed five iterations before collecting timings. After warm up, we recorded timings for 100 iterations.

We used a variety of input source maps with our benchmarks. We used three source maps found in the wild of varying sizes:

  1. The source map for the minified-to-unminified versions of the original JavaScript implementation of the source-map library. This source map is created by UglifyJS and its "mappings" string is 30,081 characters long.

  2. The latest Angular.JS’s minified-to-unminified source map. This source map’s "mappings" string is 391,473 characters long.

  3. The Scala.JS runtime’s Scala-to-JavaScript source map. This source map is the largest, and its "mappings" string is 14,964,446 characters long.

Additionally, we augmented the input set with two more artificially constructed source maps:

  1. The Angular.JS source map inflated to ten times its original size. This results in a "mappings" string of size 3,914,739.

  2. The Scala.JS source map inflated to twice its original size. This results in a "mappings" string of size 29,928,893. For this input source map, we only collected 40 iterations on each benchmark.

Astute readers will have noticed an extra 9 and 1 characters in the inflated source maps’ sizes respectively. These are from the ; separator characters between each copy of the original "mappings" string when gluing them together to create the inflated version.

We will pay particular attention to the Scala.JS source map. It is the largest non-artificial source map we tested. Additionally, it is the largest source map for which we have measurements for all combinations of browser and library implementation. There is no data for the largest input (the twice inflated Scala.JS source map) on Chrome. With the JavaScript implementation, we were unable to take any measurements with this combination because none of the benchmarks could complete without the tab’s content process crashing. With the WebAssembly implementation, Chrome would erroneously throw RuntimeError: memory access out of bounds. Using Chrome’s debugger, the supposed out-of-bounds access was happening in an instruction sequence that does not exist in the .wasm file. All other browser’s WebAssembly implementations successfully ran the benchmark with this input, so I am inclined to believe it is a bug in the Chrome implementation.

For all benchmarks, lower values are better.

Setting a Breakpoint for the First Time

The first benchmark simulates a stepping debugger setting a breakpoint on some line in the original source for the first time. This requires that the source map’s "mappings" string is parsed, and that the parsed mappings are sorted by their original source locations so we can binary search to the breakpoint’s line’s mappings. The query returns every generated JavaScript location that corresponds to any mapping on the original source line.

The WebAssembly implementation outperforms its JavaScript counterpart in all browsers. For the Scala.JS source map, the WebAssembly implementation takes 0.65x the amount of time its JavaScript counterpart takes in Chrome, 0.30x in Firefox, and 0.37x in Safari. The WebAssembly implementation is fastest in Safari, taking 702ms on average compared to Chrome’s 1140ms and Firefox’s 877ms.

Furthermore, the relative standard deviation of the WebAssembly implementation is more narrow than the JavaScript implementation, particularly in Firefox. For the Scala.JS source map with the JavaScript implementation, Chrome’s relative standard deviation is ±4.07%, Firefox’s is ±10.52%, and Safari’s is ±6.02%. In WebAssembly, the spreads shrink down to ±1.74% in Chrome, ±2.44% in Firefox, and ±1.58% in Safari.

Pausing at an Exception for the First Time

The second benchmark exercises the code path for the first time that a generated JavaScript location is used to look up its corresponding original source location. This happens when a stepping debugger pauses at an uncaught exception originating from within the generated JavaScript code, when a console message is logged from within the generated JavaScript code, or when stepping into the generated JavaScript code from some other JavaScript source.

To translate a generated JavaScript location into an original source location, the "mappings" string must be parsed. The parsed mappings must then be sorted by generated JavaScript location, so that we can binary search for the closest mapping, and finally that closest mapping’s original source location is returned.

Once again, the WebAssembly implementation outperforms the JavaScript implementation in all browsers — by an even larger lead this time. With the Scala.JS source map, in Chrome, the WebAssembly implementation takes 0.23x the amount time that the JavaScript implementation takes. In both Firefox and Safari, the WebAssembly takes 0.17x the time the JavaScript takes. Once more, Safari runs the WebAssembly fastest (305ms), followed by Firefox (397ms), and then Chrome (486ms).

The WebAssembly implementation is also less noisy than the JavaScript implementation again. The relative standard deviations fell from ±4.04% to 2.35±% in Chrome, from ±13.75% to ±2.03% in Firefox, and from ±6.65% to ±3.86% in Safari for the Scala.JS source map input.

Subsequent Breakpoints and Pauses at Exceptions

The third and fourth benchmarks observe the time it takes to set subsequent breakpoints after the first one, or to pause at subsequent uncaught exceptions, or to translate subsequent logged messages’ locations. Historically, these operations have never been a performance bottleneck: the expensive part is the initial "mappings" string parse and construction of queryable data structures (the sorted arrays).

Nevertheless, we want to make sure it stays that way: we don’t want these operations to suddenly become costly.

Both of these benchmarks are measuring the time it takes to binary search for the closest mapping to the target location and returning that mapping’s co-location.

These benchmark results should be seasoned with more salt than the other benchmarks’ results. Looking at both of the Scala.JS source map input plots, we see clear strata formations. This layering happens because the benchmarked operations run in such little time that the resolution of our timer becomes visible. We can see that Chrome exposes timers with resolution to tenths of a millisecond, Firefox exposes them with resolution to .02 milliseconds, and Safari exposes millisecond resolution.

All we can conclude, based on this data, is that subsequent queries largely remain sub-millisecond operations in both JavaScript and WebAssembly implementations. Subsequent queries have never been a performance bottleneck, and they do not become a bottleneck in the new WebAssembly implementation.

Iterating Over All Mappings

The final two benchmarks measure the time it takes to parse a source map and immediately iterate over its mappings, and to iterate over an already-parsed source map’s mappings. These are common operations performed by build tools that are composing or consuming source maps. They are also sometimes performed by stepping debuggers to highlight to the user which lines within an original source the user can set breakpoints on — it doesn’t make sense to set a breakpoint on a line that doesn’t translate into any location in the generated JavaScript.

This benchmark was the one we worried about: it involves the most JavaScript↔WebAssembly boundary crossing, an FFI call for every mapping in the source map. For all other benchmarks tested, our design minimized the number of such FFI calls.

It turns out our worry was unfounded. The WebAssembly implementation doesn’t just meet the JavaScript implementation’s performance, even when the source map is already parsed, it surpasses the JavaScript implementation’s performance. For the parse-and-iterate and iterate-already-parsed benchmarks, the WebAssembly takes 0.61x and 0.71x the time of the JavaScript in Chrome. In Firefox, the WebAssembly takes 0.56x and 0.77x the time of the JavaScript. In Safari, the WebAssembly implementation takes 0.63x and 0.87x the amount of time that the JavaScript implementation takes. Once again, Safari runs the WebAssembly implementation the quickest, and Firefox and Chrome are essentially tied for second place. Safari deserves special acknowledgment for its JavaScript performance on the iterate-already-parsed benchmark: beyond just outperforming the other browsers’ JavaScript times, Safari runs the JavaScript faster than the other browsers run the WebAssembly!

True to the trend from earlier benchmarks, we also see reductions in the relative standard deviations in the WebAssembly compared to the JavaScript implementation. When both parsing and iterating, Chrome’s relative standard deviation fell from ±1.80% to ±0.33%, Firefox’s from ±11.63% to ±1.41%, and Safari’s from ±2.73% to ±1.51%. When iterating over an already-parsed source map’s mappings, Firefox’s relative standard deviation dropped from ±12.56% to ±1.40%, and Safari’s from ±1.97% to ±1.40%. Chrome’s relative standard deviation grew from ±0.61% to ±1.18%, making it the only browser to buck the trend, but only on this one benchmark.

Code Size

One of advantages of the wasm32-unknown-unknown target over the wasm32-unknown-emscripten target, is that it generates leaner WebAssembly code. The wasm32-unknown-emscripten target includes polyfills for most of libc and a file system built on top of IndexedDB, among other things, but wasm32-unknown-unknown does not. With the source-map library, we’ve only used wasm32-unknown-unknown.

We consider the code size of the JavaScript and WebAssembly that is delivered to the client. That is, we’re looking at the code size after bundling JavaScript modules together into a single .js file. We look at the effects of using wasm-gc, wasm-snip, and wasm-opt to shrink .wasm file size, and using gzip compression, which is ubiquitously supported on the Web.

In these measurements, the JavaScript size reported is always the size of minified JavaScript, created with the Google Closure Compiler at the “simple” optimization level. We used the Closure Compiler because UglifyJS does not support some newer ECMAScript forms that we introduced (for example let and arrow functions). We used the “simple” optimization level because the “advanced” optimization level is destructive for JavaScript that wasn’t authored with Closure Compiler in mind.

The bars labeled “JavaScript” are for variations of the original, pure-JavaScript source-map library implementation. The bars labeled “WebAssembly” are for variations of the new source-map library implementation that uses WebAssembly for parsing the "mappings" string and querying the parsed mappings. Note that the “WebAssembly” implementation still uses JavaScript for all other functionality! The source-map library has additional features, such as generating source maps, that are still implemented in JavaScript. For the “WebAssembly” implementation, we report the sizes of both the WebAssembly and JavaScript.

At its smallest, the new WebAssembly implementation has a larger total code size than the old JavaScript implementation: 20,996 bytes versus 8,365 bytes respectively. However, by using tools for shrinking .wasm size, we brought the size of the WebAssembly down to 0.16x its original size. Both implementations have similar amounts of JavaScript.

If we replaced JavaScript parsing and querying code with WebAssembly, why doesn’t the WebAssembly implementation contain less JavaScript? There are two factors contributing to the lack of JavaScript code size reductions. First, there is some small amount of new JavaScript introduced to load the .wasm file and interface with the WebAssembly. Second, and more importantly, some of the JavaScript routines that we “replaced” were previously shared with other parts of the source-map library. Now, although those routines are no longer shared, they are still in use by those other portions of the library.

Let us turn our focus towards what is contributing to the size of the pre-gziped .wasm file. Running wasm-objdump -h gives us the sizes of each section:

The Code and Data sections effectively account for the whole .wasm file’s size. The Code section contains the encoded WebAssembly instructions that make up function bodies. The Data section consists of static data to be loaded into the WebAssembly module’s linear memory space.

Using wasm-objdump to manually inspect the Data section’s contents shows that it mostly consists of string fragments used for constructing diagnostic messages if the Rust code were to panic. However, when targeting WebAssembly, Rust panics translate into WebAssembly traps, and traps do not carry extra diagnostic information. We consider it a bug in rustc that these string fragments are even emitted. Unfortunately, wasm-gc cannot currently remove unused Data segments either, so we are stuck with this bloat for the meantime. WebAssembly and its tooling is still relatively immature, and we expect the toolchain to improve in this respect with time.

Next, we post-process wasm-objdump‘s disassembly output to compute the size of each function body inside the Code section, and group sizes by Rust crate:

The heaviest crate is dlmalloc which is used by the alloc crate, which implements Rust’s low-level allocation APIs. Together, dlmalloc and alloc clock in at 10,126 bytes, or 50.98% the total function size. In a sense, this is a relief: the code size of the allocator is a constant that will not grow as we port more JavaScript code to Rust.

The sum of code sizes from crates that we authored (vlq, source_map_mappings, and source_map_mappings_wasm_api) is 9,320 bytes, or 46.92% the total function size. That leaves only 417 bytes (2.10%) of space consumed by the other crates. This speaks to the efficacy of wasm-gc, wasm-snip, and wasm-opt: although crates like std are much larger than our crates, we only use a tiny fraction of its APIs and we only pay for what we use.

Conclusions and Future Work

Rewriting the most performance-sensitive portions of source map parsing and querying in Rust and WebAssembly has been a success. On our benchmarks, the WebAssembly implementation takes a fraction the time that the original JavaScript implementation takes — as little as 0.17x the time. We observe that, for all browsers, the WebAssembly implementation is faster than the JavaScript implementation. Furthermore, the WebAssembly implementation offers more consistent and reliable performance: relative standard deviation between iterations dropped significantly compared to the JavaScript implementation.

The JavaScript implementation has accumulated convoluted code in the name of performance, and we replaced it with idiomatic Rust. Rust does not force us to choose between clearly expressing intent and runtime performance.

That said, there is still more work to do.

The most pressing next step is investigating why the Rust standard library’s sorting is not as fast as our custom Quicksort when targeting WebAssembly. This is the sole unidiomatic bit of Rust code in the rewrite. This behavior is surprising since our Quicksort implementation is naive, and the standard library’s quick sort is pattern defeating and opportunistically uses insertion sort for small and mostly-sorted ranges. In fact, the standard library’s sorting routine is faster than ours when targeting native code. We speculate that inlining heuristics are changing across targets, and that our comparator functions aren’t being inlined into the standard library’s sorting routine when targeting WebAssembly. It requires further investigation.

We found size profiling WebAssembly was more difficult than necessary. To get useful information presented meaningfully, we were forced to write our own home-grown script to post-process wasm-objdump. The script constructs the call graph, and lets us query who the callers of some function were, helping us understand why the function was emitted in the .wasm file, even if we didn’t expect it to be. It is pretty hacky, and doesn’t expose information about inlined functions. A proper WebAssembly size profiler would have helped, and would be a benefit for anyone following in our tracks.

The relatively large code size footprint of the allocator suggests that writing or adapting an allocator that is focused on tiny code size could provide considerable utility for the WebAssembly ecosystem. At least for our use case, allocator performance is not a concern, and we only make a small handful of dynamic allocations. For an allocator, we would choose small code size over performance in a heartbeat.

The unused segments in our Data section highlight the need for wasm-gc, or another tool, to detect and remove static data that is never referenced.

There are still some JavaScript API improvements that we can make for the library’s downstream users. The introduction of WebAssembly in our current implementation requires the introduction of manually freeing the parsed mappings when the user is finished with it. This does not come naturally to most JavaScript programmers, who are used to relying on a garbage collector, and do not typically think about the lifetime of any particular object. We could introduce a SourceMapConsumer.with function that took a raw, un-parsed source map, and an async function. The with function would construct a SourceMapConsumer instance, invoke the async function with it, and then call destroy on the SourceMapConsumer instance once the async function call completes. This is like async RAII for JavaScript.

SourceMapConsumer.with = async function (rawSourceMap, f) {
  const consumer = await new SourceMapConsumer(rawSourceMap);
  try {
    await f(consumer);
  } finally {
    consumer.destroy();
  }
};

Another alternative way to make the API easier to work with for JavaScript programmers would be to give every SourceMapConsumer its own instance of the WebAssembly module. Then, because the SourceMapConsumer instance would have the sole GC edge to its WebAssembly module instance, we could let the garbage collector manage all of the SourceMapConsumer instance, the WebAssembly module instance, and the module instance’s heap. With this strategy, we would have a single static mut MAPPINGS: Mappings in the Rust WebAssembly glue code, and the Mappings instance would be implicit in all exported query function calls. No more Box::new(mappings) in the parse_mappings function, and no more passing around *mut Mappings pointers. With some care, we might be able to remove all allocation from the Rust library, which would shrink the emitted WebAssembly to half its current size. Of course, this all depends on creating multiple instances of the same WebAssembly module being a relatively cheap operation, which requires further investigation.

The wasm-bindgen project aims to remove the need to write FFI glue code by hand, and automates interfacing between WebAssembly and JavaScript. Using it, we should be able to remove all the hand-written unsafe pointer manipulation code involved in exporting Rust APIs to JavaScript.

In this project, we ported source map parsing and querying to Rust and WebAssembly, but this is only half of the source-map library’s functionality. The other half is generating source maps, and it is also performance sensitive. We would like to rewrite the core of building and encoding source maps in Rust and WebAssembly sometime in the future as well. We expect to see similar speed ups to what we observed for parsing source maps.

The pull request adding the WebAssembly implementation to the mozilla/source-map library is in the process of merging here. That pull request contains the benchmarking code, so that results can be reproduced, and we can continue to improve them.

Finally, I’d like to thank Tom Tromey for hacking on this project with me. I’d also like to thank Aaron Turon, Alex Crichton, Benjamin Bouvier, Jeena Lee, Jim Blandy, Lin Clark, Luke Wagner, Mike Cooper, and Till Schneidereit for reading early drafts and providing valuable feedback. This document, our benchmarks, and the source-map library are all better thanks to them.


0 Or “transpiler” if you must insist.

1 SpiderMonkey now uses a self-hosted JavaScript implementation of Array.prototype.sort when there is a custom comparator function, and a C++ implementation when there is not.

2 The overhead of calls between WebAssembly and JavaScript should mostly disappear in Firefox once bug 1319203 lands. After that, calls between JavaScript and WebAssembly will have similar overheads to out-of-line calls between JavaScript functions. But that patch hasn’t landed yet, and other browsers haven’t landed equivalent improvements yet either.

3 For Firefox and Chrome, we tested with the latest nightly builds. We did not do the same with Safari because the latest Safari Technology Preview requires a newer macOS version than El Capitan, which this laptop was running.

Planet MozillaReps Weekly Meeting, 18 Jan 2018

Reps Weekly Meeting This is a weekly call with some of the Reps to discuss all matters about/affecting Reps and invite Reps to share their work with everyone.

Planet MozillaReps Weekly Meeting, 18 Jan 2018

Reps Weekly Meeting This is a weekly call with some of the Reps to discuss all matters about/affecting Reps and invite Reps to share their work with everyone.

Planet MozillaAnnouncing WebBook Level 1, a new Web-based format for electronic books

TL;DR: the title says it all, and it's available there.

Eons ago, at a time BlueGriffon was only a Wysiwyg editor for the Web, my friend Mohamed Zergaoui asked why I was not turning BlueGriffon into an EPUB editor... I had been observing the electronic book market since the early days of Cytale and its Cybook but I was not involved into it on a daily basis. That seemed not only an excellent idea, but also a fairly workable one. EPUB is based on flavors of HTML so I would not have to reinvent the wheel.

I started diving into the EPUB specs the very same day, EPUB 2.0.1 (released in 2009) at that time. I immediately discovered a technology that was not far away from the Web but that was also clearly not the Web. In particular, I immediately saw that two crucial features were missing: it was impossible to aggregate a set of Web pages into a EPUB book through a trivial zip, and it was impossible to unzip a EPUB book and make it trivially readable inside a Web browser even with graceful degradation.

When the IDPF started working on EPUB 3.0 (with its 3.0.1 revision) and 3.1, I said this was coming too fast, and that the lack of Test Suites with interoperable implementations as we often have in W3C exit criteria was a critical issue. More importantly, the market was, in my opinion, not ready to absorb so quickly two major and one minor revisions of EPUB given the huge cost on both publishing chains and existing ebook bases. I also thought - and said - the EPUB 3.x specifications were suffering from clear technical issues, including the two missing features quoted above.

Today, times have changed and the Standards Committee that oversaw the future of EPUB, the IDPF, has now merged with the World Wide Web Consortium (W3C). As Jeff Jaffe, CEO of the W3C, said at that time,

Working together, Publishing@W3C will bring exciting new capabilities and features to the future of publishing, authoring and reading using Web technologies

Since the beginning of 2017, and with a steep acceleration during spring 2017, the Publishing@W3C activity has restarted work on the EPUB 3.x line and the future EPUB 4 line, creating a EPUB 3 Community Group (CG) for the former and a Publishing Working Group (WG) for the latter. If I had some reservations about the work division between these two entities, the whole thing seemed to be a very good idea. In fact, I started advocating for the merger between IDPF and W3C back in 2012, at a moment only a handful of people were willing to listen. It seemed to me that Publishing was a underrated first-class user of Web technologies and EPUB's growth was suffering from two critical ailments:

  1. IDPF members were not at W3C so they could not confront their technical choices to browser vendors and the Web industry. It also meant they were inventing new solutions in a silo without bringing them to W3C standardization tables and too often without even knowing if the rendering engine vendors would implement them.
  2. on another hand, W3C members had too little knowledge of the Publishing activity, that was historically quite skeptical about the Web... Working Groups at W3C were lacking ebook expertise and were therefore designing things without having ebooks in mind.

I was then particularly happy when the merger I advocated for was announced.

As I recently wrote on Medium, I am not any more. I am not convinced by the current approach implemented by Publishing@W3C on many counts:

  • the organization of the Publishing@W3C activity, with a Publishing Business Group (BG) formally ruling (see Process section, second paragraph) the EPUB3 CG and a Steering Committee (see Process section, first paragraph) recreated the former IDPF structure inside W3C.  The BG Charter even says that it « advises W3C on the direction of current and future publishing activity work » as if the IDPF and W3C did not merge and as if W3C was still only a Liaison. It also says « the initial members of the Steering Committee shall be the individuals who served on IDPF’s Board of Directors immediately prior to the effective date of the Combination of IDPF with W3C », maintaining the silo we wanted to eliminate.
  • the EPUB3 Community Group faces a major technical challenge, recently highlighted by representatives of the Japanese Publishing Industry: EPUB 3.1 represents too much of a technical change compared to EPUB 3.0.1 and is not implementable at a reasonable cost in a reasonable timeframe for them. Since EPUB 3 is recommended by the Japanese Government as the official ebook format in Japan, that's a bit of a blocker for EPUB 3.1 and its successors. The EPUB3 CG is then actively discussing a potential rescindment of EPUB 3.1, an extraction of the good bits we want to preserve, and the release of a EPUB 3.0.2 specification based on 3.0.1 plus those good bits. In short, the EPUB 3.1 line, that saw important clarifying changes from 3.0.1, is dead.
  • the Publishing Working Group is working on a collection of specifications known as Web Publications (WP), Packaged Web Publications (PWP), and EPUB 4. What these specifications represent is extremely complicated to describe. With a daily observation of the activities of the Working Group, I still can't firmly say what they're up to, even if I am already convinced that some technological choices (for instance JSON-LD for manifests) are highly questionable and do not « lead Publishing to its full Web potential », to paraphrase the famous W3C motto. It must also be said that the EPUB 3.1 hiatus in the EPUB3 CG shakes the EPUB 4 plan to the ground, since it's now extremely clear the ebook market is not ready at all to move to yet another EPUB version, potentially incompatible with EPUB 3.x (for the record, backwards-compatibility in the EPUB world is a myth).
  • the original sins of EPUB quoted above, including the two missing major features quoted in the second paragraph of the present article, are a minor requirement only. Editability of EPUB, one of the greatest flaws of that ecosystem, is still not a first-class requirement if not a requirement at all. Convergence with the Web is severely encumbered by personal agendas and technical choices made by one implementation vendor for its own sake; the whole W3C process based on consensus is worked around not because there is no consensus (the WG minutes show consensus all the time) but mostly beacause the rendering engine vendors are still not in the loop and their potential crucial contributions are sadly missed. And they are not in the loop because they don't understand a strategy that seems decorrelated from the Web; the financial impact of any commitment to the Publishing@W3C is then a understandable no-go.
  • the original design choices of EPUB, using painful-to-edit-or-render XML dialects, were also an original sin. We're about to make the same mistake, again and again, either retaining things that partly block the software ecosystem or imagining new silos that won't be editable nor grokable by a Web Browser. Simplicity, Web-centricity and mainstream implementations are not in sight.

Since the whole organization of Publishing @W3C is governed by the merger agreement between IDPF and W3C, I do not expect to change anyone's mind with the present article. I only felt the need to express my opinion, in both public and private fora. Unsurprisingly, the feedback to my private warnings was fairly negative. In short, it works as expected and I should stop spitting in the soup. Well, if that works as expected, the expectations were pretty low, sorry to say, and were not worth a merger between two Standard Bodies.

I have then decided to work on a different format for electronic books, called WebBook. A format strictly based on Web technologies and when I say "Web technologies", I mean the most basic ones: html, CSS, JavaScript, SVG and friends; the class of specifications all Web authors use and master on a daily basis. Not all details are decided or even ironed, the proposal is still a work in progress at this point, but I know where I want to go to.

I will of course happily accept all feedback. If people like my idea, great! If people disagree with it, too bad for me but fine! At least during the early moments of my proposal, and because my guts tell me my goals are A Good Thing™️, I'm running this as a Benevolent Dictator, not as a consensus-based effort. Convince me and your suggestions will make it in.

I have started from a list of requirements, something that was never done that way in the EPUB world:

  1. one URL is enough to retrieve a remote WebBook instance, there is no need to download every resource composing that instance

  2. the contents of a WebBook instance can be placed inside a Web site’s directory and are directly readable by a Web browser using the URL for that directory

  3. the contents of a WebBook instance can be placed inside a local directory and are directly readable by a Web browser opening its index.html or index.xhtml topmost file

  4. each individual resource in a WebBook instance, on a Web site or on a local disk, is directly readable by a Web browser

  5. any html document can be used as content document inside a WebBook instance, without restriction

  6. any stylesheet, replaced resource (images, audio, video, etc.) or additional resource useable by a html document (JavaScript, manifests, etc.) can be used inside the navigation document or the content documents of a WebBook instance, without restriction

  7. the navigation document and the content documents inside a WebBook instance can be created and edited by any html editor

  8. the metadata, table of contents contained in the navigation document of a WebBook instance can be created and edited by any html editor

  9. the WebBook specification is backwards-compatible

  10. the WebBook specification is forwards-compatible, at the potential cost of graceful degradation of some content

  11. WebBook instances can be recognized without having to detect their MIME type

  12. it’s possible to deliver electronic books in a form that is compatible with both WebBook and EPUB 3.0.1

I also made a strong design choice: the Level 1 of the specification will not be a fit-all-cases document. WebBook will start small, simple and extensible, and each use case will be evaluated individually, sequentially and will result in light extensions at a speed the Publishing industry can bear with. So don't tell me WebBook Level 1 doesn't support a given type of ebook or is not at parity level with EPUB 3.x. It's on purpose.

With that said, the WebBook Level 1 is available here and, again, I am happily accepting Issues and PR on github. You'll find in the spec references to:

  • « Moby Dick » released as a WebBook instance
  • « Moby Dick » released as a EPUB3-compatible WebBook instance
  • a script usable with Node.js to automagically convert a EPUB3 package into a EPUB3-compatible WebBook

My EPUB Editor BlueGriffon is already modified to deal with WebBook. The next public version will allow users to create EPUB3-compatible WebBooks.

I hope this proposal will show stakeholders of the Publishing@W3C activity another path to greater convergence with the Web is possible. Should this proposal be considered by them, I will of course happily contribute to the debate, and hopefully the solution.

Planet MozillaOxidizing Source Maps with Rust and WebAssembly

Note: I am cross-posting this to my personal blog from hacks.mozilla.org, where it was originally published.

Tom Tromey and I have replaced the most performance-sensitive portions of the source-map JavaScript library’s source map parser with Rust code that is compiled to WebAssembly. The WebAssembly is up to 5.89 times faster than the JavaScript implementation on realistic benchmarks operating on real world source maps. Additionally, performance is also more consistent: relative standard deviations decreased.

We removed JavaScript code that had been written in a convoluted and unidiomatic way in the name of performance, and replaced it with idiomatic Rust code that performs even better.

We hope that, by sharing our experience, we inspire others to follow suit and rewrite performance-sensitive JavaScript in Rust via WebAssembly.

Background

The Source Map Format

The source map format provides a bidirectional mapping between locations in some generated JavaScript code that was emitted by a compiler0, minifier, or package bundling tool back to locations in the original sources that a programmer authored. JavaScript developer tools use source maps to symbolicate backtraces, and to implement source-level stepping in debuggers. Source maps encode debug information similar to that found in DWARF’s .debug_line section.

A source map is a JSON object with a handful of fields. The "mappings" field is a string that makes up the bulk of the source map, and contains the bidirectional mappings between source and object locations.

We will describe the "mappings" string’s grammar in extended Backus-Naur form (EBNF).

Mappings are grouped by generated JavaScript line number, which is incremented every time a semicolon lays between individual mappings. When consecutive mappings are on the same generated JavaScript line, they are separated by a comma.

<figure class="highlight">
<mappings> = [ <generated-line> ] ';' <mappings>
           | ''
           ;

<generated-line> = <mapping>
                 | <mapping> ',' <generated-line>
                 ;
</figure>

Each individual mapping has a location in the generated JavaScript, and optionally a location in the original source text, which might also contain an associated name.

<figure class="highlight">
<mapping> = <generated-column> [ <source> <original-line> <original-column> [ <name> ] ] ;
</figure>

Every component of a mapping is a Variable Length Quantity (VLQ) encoded integer. Filenames and associated names are encoded as indices into side tables stored in other fields of the source map’s JSON object. Every value is relative to the last occurrence of its type, i.e. a given <source> value is the delta since the previous <source> value. These deltas tend to be smaller than their absolute values, which means they are more compact when encoded.

<figure class="highlight">
<generated-column> = <vlq> ;
<source> = <vlq> ;
<original-line> = <vlq> ;
<original-column> = <vlq> ;
<name> = <vlq> ;
</figure>

Each character in a VLQ is drawn from a set of 64 printable ASCII characters comprising the upper- and lower-case letters, the decimal digits, and some symbols. Each character represents a particular 6-bit value. The most significant bit is set on all but the last character in the VLQ; the remaining five bits are prepended to the integer value represented.

Rather than attempting to translate this definition into EBNF, we provide pseudocode for parsing and decoding a single VLQ below:

<figure class="highlight">
constant SHIFT = 5
constant CONTINUATION_BIT = 1 << SHIFT
constant MASK = (1 << SHIFT) - 1

decode_vlq(input):
    let accumulation = 0
    let shift = 0

    let next_digit_part_of_this_number = true;
    while next_digit_part_of_this_number:
        let [character, ...rest] = input
        let digit = decode_base_64(character)
        accumulation += (digit & MASK) << shift
        shift += SHIFT;
        next_digit_part_of_this_number = (digit & CONTINUATION_BIT) != 0
        input = rest

    let is_negative = accumulation & 1
    let value = accumulation >> 1
    if is_negative:
        return (-value, input)
    else:
        return (value, input)
</figure>

The source-map JavaScript Library

The source-map library is published on npm and maintained by the Firefox Developer Tools team at Mozilla. It is one of the most transitively depended-upon libraries by the JavaScript community, and is downloaded 10M times per week.

Like many software projects, the source-map library was written first for correctness, and then later modified to improve performance. By this time, it has seen a good amount of performance work.

When consuming source maps, the majority of time is spent parsing the "mappings" string and constructing a pair of arrays: one sorted by generated JavaScript location, and other sorted by original source location. Queries use binary search on the appropriate array. The parsing and sorting both happen lazily, and don’t occur until a particular query requires it. This allows a debugger to list sources, for example, without needing to parse and sort mappings. Once parsed and sorted, querying tends not to be a performance bottleneck.

The VLQ decoding function takes a string as input, parses and decodes a VLQ from the string, and returns a pair of the value and the rest of the input that was not consumed. This function was originally written in an idiomatic, referentially transparent style to return the pair as an Object literal with two properties.

<figure class="highlight">
function decodeVlqBefore(input) {
  // ...
  return { result, rest };
}
</figure>

Returning a pair that way was found to be costly. JavaScript Just-in-Time (JIT) compilers’ optimized compilation tiers were unable to eliminate this allocation. Because the VLQ decoding routine is called frequently, this allocation was creating undue pressure on the garbage collector, leading to more frequent garbage collection pauses.

To remove the allocation, we modified the procedure to take a second parameter: an Object that gets mutated and serves as an out parameter. Rather than returning a freshly allocated Object, the out parameter’s properties are overwritten. This way, we could reuse the same object for every VLQ parse. This is less idiomatic, but much more performant.

<figure class="highlight">
function decodeVlqAfter(input, out) {
  // ...
  out.result = result;
  out.rest = rest;
}
</figure>

When reaching an unexpected end-of-string or an invalid base 64 character, the VLQ decoding function would throw an Error. We found that JavaScript JITs would emit faster code when we changed the single base 64 digit decoding function to return -1 on failure instead of throwing an Error. This is less idiomatic, but once again it improved performance.

When profiling with SpiderMonkey’s JITCoach prototype, we found that SpiderMonkey’s JIT was using slow-path polymorphic inline caches for Object property gets and sets on our mapping Objects. The JIT was not emitting the desired fast-path code with direct object slot accesses, because it was not detecting a common “shape” (or “hidden class”) shared by all mapping Objects. Some properties would be added in a different order, or omitted completely, for example, when there was no associated name for a mapping. By creating a constructor for mapping Objects that initialized every property we would ever use, we helped the JIT create a common shape for all mapping Objects. This resulted in another performance improvement.

<figure class="highlight">
function Mapping() {
  this.generatedLine = 0;
  this.generatedColumn = 0;
  this.lastGeneratedColumn = null;
  this.source = null;
  this.originalLine = null;
  this.originalColumn = null;
  this.name = null;
}
</figure>

When sorting the two mapping arrays, we use custom comparator functions. When the source-map library was first written, SpiderMonkey’s Array.prototype.sort was implemented in C++ for performance1. However, when sort is passed an explicit comparator function and a large array, the sorting code must call the comparator function many times. Calls from C++ to JavaScript are relatively expensive, so passing a custom comparator function made the sorts perform poorly.

To avoid this, we implemented our own Quicksort in JavaScript. This not only avoided the C++-to-JavaScript calls, but also let the JavaScript JITs inline the comparator function into the sorting function, enabling further optimizations. This brought us another large performance boost, and again made the code less idiomatic.

WebAssembly

WebAssembly is a new, low-level, architecture-independent byte code developed for the Web. It is designed for safe and efficient execution and compact binaries, and is developed in the open as a Web standard. It is supported by all major Web browsers.

WebAssembly exposes a stack machine that maps well onto modern processor architectures. Its instructions operate on a large, linear buffer of memory. It does not support garbage collection, although extending it to work with garbage collected JavaScript objects is planned for the future. Control flow is structured, rather than allowing arbitrary jumps and labels. It is designed to be deterministic and consistent, even where different architectures diverge on edge cases, such as out-of-range shifts, overflows, and canonicalizing NaNs.

WebAssembly aims to match or beat native execution speed. It is currently within 1.5x native execution on most benchmarks.

Because of the lack of a garbage collector, languages that target WebAssembly are currently limited to languages without a runtime or garbage collector to speak of, unless the collector and runtime are also compiled to WebAssembly. That doesn’t happen in practice. Right now, the languages developers are actually compiling to WebAssembly are C, C++, and Rust.

Rust

Rust is a systems programming language that emphasizes safety and speed. It provides memory safety without relying on a garbage collector. Instead, it statically tracks ownership and borrowing of resources to determine where to emit code to run destructors or free memory.

Rust is in a great position for compiling to WebAssembly. Since Rust does not require garbage collection, Rust authors don’t have to jump through extra hoops to target WebAssembly. Rust has many of the goodies that Web developers have come to expect, and which C and C++ lack:

  • Easy building, packaging, publishing, sharing, and documenting of libraries. Rust has rustup, cargo, and the crates.io ecosystem; C and C++ don’t have any widely used equivalent.

  • Memory safety. Not having to chase down memory corruption in gdb. Rust prevents most of these pitfalls at compile time.

Parsing and Querying Source Maps in Rust

When we decided to rewrite the hot portions of source map parsing and querying in Rust, we had to decide where the boundary between JavaScript and the WebAssembly emitted by the Rust compiler would be. Crossing the boundary between JavaScript’s JITed code to WebAssembly and back currently imposes overhead similar to that of the C++-to-JavaScript calls we mentioned earlier.2 So it was important to place this boundary in so as to minimize the number of times we had to cross it.

A poor choice of where to place the boundary between WebAssembly and JavaScript would have been at the VLQ decoding function. The VLQ decoding function is invoked between one and four times for every mapping encoded in the "mappings" string, therefore we would have to cross the JavaScript-to-WebAssembly boundary that many times during parsing.

Instead, we decided to parse the whole "mappings" string in Rust/WebAssembly, and then keep the parsed data there. The WebAssembly heap owns the parsed data in its queryable form. This means that we never have to copy the data out of the WebAssembly heap, which would involve crossing the JavaScript/WebAssembly boundary more often. Instead, we only cross that boundary once each way for every query, plus once each way for every mapping that is a result of that query. Queries tend to produce a single result, or perhaps a handful.

To have confidence that our Rust implementation was correct, not only did we ensure that our new implementation passed the source-map library’s existing test suite, but we also wrote a suite of quickcheck property tests. These tests construct random "mappings" input strings, parse them, and then assert that various properties hold.

Our Rust implementation of "mappings" parsing and querying is available on crates.io as the source-map-mappings crate.

Base 64 Variable Length Quantities

The first step to parsing a source map’s mappings is decoding VLQs. We implemented a vlq library in Rust and published it to crates.io.

The decode64 function decodes a single base 64 digit. It uses pattern matching and the idiomatic Result-style error handling.

A Result<T, E> is either Ok(v), indicating that the operation succeeded and produced the value v of type T, or Err(error), indicating that the operation failed, with the value error of type E providing the details. The decode64 function’s return type Result<u8, Error> indicates that it returns a u8 value on success, or a vlq::Error value on failure.

<figure class="highlight">
fn decode64(input: u8) -> Result<u8, Error> {
    match input {
        b'A'...b'Z' => Ok(input - b'A'),
        b'a'...b'z' => Ok(input - b'a' + 26),
        b'0'...b'9' => Ok(input - b'0' + 52),
        b'+' => Ok(62),
        b'/' => Ok(63),
        _ => Err(Error::InvalidBase64(input)),
    }
}
</figure>

With the decode64 function in hand, we can decode whole VLQ values. The decode function takes a mutable reference to an iterator of bytes as input, consumes as many bytes as it needs to decode the VLQ, and finally returns a Result of the decoded value.

<figure class="highlight">
pub fn decode<B>(input: &mut B) -> Result<i64>
where
    B: Iterator<Item = u8>,
{
    let mut accum: u64 = 0;
    let mut shift = 0;

    let mut keep_going = true;
    while keep_going {
        let byte = input.next().ok_or(Error::UnexpectedEof)?;
        let digit = decode64(byte)?;
        keep_going = (digit & CONTINUED) != 0;

        let digit_value = ((digit & MASK) as u64)
            .checked_shl(shift as u32)
            .ok_or(Error::Overflow)?;

        accum = accum.checked_add(digit_value).ok_or(Error::Overflow)?;
        shift += SHIFT;
    }

    let abs_value = accum / 2;
    if abs_value > (i64::MAX as u64) {
        return Err(Error::Overflow);
    }

    // The low bit holds the sign.
    if (accum & 1) != 0 {
        Ok(-(abs_value as i64))
    } else {
        Ok(abs_value as i64)
    }
}
</figure>

Unlike the JavaScript code it is replacing, this code does not sacrifice idiomatic error handling for performance. Idiomatic error handling is performant, and does not involve boxing values on the heap or unwinding the stack.

The "mappings" String

We begin by defining some small helper functions. The is_mapping_separator predicate function returns true if the given byte is a separator between mappings, and false if it is not. There is a nearly identical JavaScript function.

<figure class="highlight">
#[inline]
fn is_mapping_separator(byte: u8) -> bool {
    byte == b';' || byte == b','
}
</figure>

Next, we define a helper function for reading a single VLQ delta value and adding it to the previous value. The JavaScript does not have an equivalent function, and instead repeats this code each time it wants to read a VLQ delta. JavaScript does not let us control how our parameters are represented in memory, but Rust does. We cannot pass a reference to a number in a zero-cost manner with JavaScript. We could pass a reference to an Object with a number property or we could close over the number’s variable in a local closure function, but both of these solutions have associated runtime costs.

<figure class="highlight">
#[inline]
fn read_relative_vlq<B>(
    previous: &mut u32,
    input: &mut B,
) -> Result<(), Error>
where
    B: Iterator<Item = u8>,
{
    let decoded = vlq::decode(input)?;
    let (new, overflowed) = (*previous as i64).overflowing_add(decoded);
    if overflowed || new > (u32::MAX as i64) {
        return Err(Error::UnexpectedlyBigNumber);
    }

    if new < 0 {
        return Err(Error::UnexpectedNegativeNumber);
    }

    *previous = new as u32;
    Ok(())
}
</figure>

All in all, the Rust implementation of "mappings" parsing is quite similar to the JavaScript implementation it replaces. However, with the Rust, we have control over what is or isn’t boxed, whereas in the JavaScript, we do not. Every single parsed mapping Object is boxed in the JavaScript implementation. Much of the Rust implementation’s advantage stems from avoiding these allocations and their associated garbage collections.

<figure class="highlight">
pub fn parse_mappings(input: &[u8]) -> Result<Mappings, Error> {
    let mut generated_line = 0;
    let mut generated_column = 0;
    let mut original_line = 0;
    let mut original_column = 0;
    let mut source = 0;
    let mut name = 0;

    let mut mappings = Mappings::default();
    let mut by_generated = vec![];

    let mut input = input.iter().cloned().peekable();

    while let Some(byte) = input.peek().cloned() {
        match byte {
            b';' => {
                generated_line += 1;
                generated_column = 0;
                input.next().unwrap();
            }
            b',' => {
                input.next().unwrap();
            }
            _ => {
                let mut mapping = Mapping::default();
                mapping.generated_line = generated_line;

                read_relative_vlq(&mut generated_column, &mut input)?;
                mapping.generated_column = generated_column as u32;

                let next_is_sep = input.peek()
                    .cloned()
                    .map_or(true, is_mapping_separator);
                mapping.original = if next_is_sep {
                    None
                } else {
                    read_relative_vlq(&mut source, &mut input)?;
                    read_relative_vlq(&mut original_line, &mut input)?;
                    read_relative_vlq(&mut original_column, &mut input)?;

                    let next_is_sep = input.peek()
                        .cloned()
                        .map_or(true, is_mapping_separator);
                    let name = if next_is_sep {
                        None
                    } else {
                        read_relative_vlq(&mut name, &mut input)?;
                        Some(name)
                    };

                    Some(OriginalLocation {
                        source,
                        original_line,
                        original_column,
                        name,
                    })
                };

                by_generated.push(mapping);
            }
        }
    }

    quick_sort::<comparators::ByGeneratedLocation, _>(&mut by_generated);
    mappings.by_generated = by_generated;
    Ok(mappings)
}
</figure>

Finally, we still use a custom Quicksort implementation in the Rust code, and this is the only unidiomatic thing about the Rust code. We found that although the standard library’s built in sorting is much faster when targeting native code, our custom Quicksort is faster when targeting WebAssembly. (This is surprising, but we have not investigated why it is so.)

Interfacing with JavaScript

The WebAssembly foreign function interface (FFI) is constrained to scalar values, so any function exposed from Rust to JavaScript via WebAssembly may only use scalar value types as parameters and return types. Therefore, the JavaScript code must ask the Rust to allocate a buffer with space for the "mappings" string and return a pointer to that buffer. Next, the JavaScript must copy the "mappings" string into that buffer, which it can do without crossing the FFI boundary because it has write access to the whole WebAssembly linear memory. After the buffer is initialized, the JavaScript calls the parse_mappings function and is returned a pointer to the parsed mappings. Then, the JavaScript can perform any number of queries through the WebAssembly API, passing in the pointer to the parsed mappings when doing so. Finally, when no more queries will be made, the JavaScript tells the WebAssembly to free the parsed mappings.

Exposing WebAssembly APIs from Rust

All the WebAssembly APIs we expose live in a small glue crate that wraps the source-map-mappings crate. This separation is useful because it allows us to test the source-map-mappings crate with our native host target, and only compile the WebAssembly glue when targeting WebAssembly.

In addition to the constraints of what types can cross the FFI boundary, each exported function requires that

  • it has the #[no_mangle] attribute so that it is not name-mangled, and JavaScript can easily call it, and
  • it is marked extern "C" so that it is publicly exported in the final .wasm file.

Unlike the core library, this glue code that exposes functionality to JavaScript via WebAssembly does, by necessity, frequently use unsafe. Calling extern functions and using pointers received from across an FFI boundary is always unsafe, because the Rust compiler cannot verify the safety of the other side. Ultimately this is less concerning with WebAssembly than it might be with other targets – the worst we can do is trap (which causes an Error to be raised on the JavaScript side) or return an incorrect answer. Much tamer than the worst case scenarios with native binaries where executable memory is in the same address space as writable memory, and an attacker can trick the program into jumping to memory where they’ve inserted some shell code.

The simplest function we export is the function to get the last error that occurred in the library. This provides similar functionality as errno from libc, and is called by JavaScript when some API call fails and the JavaScript would like to know what kind of failure it was. We always maintain the most recent error in a global, and this function retrieves that error value.

<figure class="highlight">
static mut LAST_ERROR: Option<Error> = None;

#[no_mangle]
pub extern "C" fn get_last_error() -> u32 {
    unsafe {
        match LAST_ERROR {
            None => 0,
            Some(e) => e as u32,
        }
    }
}
</figure>

The first interaction between JavaScript and Rust is allocating a buffer with space to hold the "mappings" string. We desire an owned, contiguous chunk of u8s, which suggests using Vec<u8>, but we want to expose a single pointer to JavaScript. A single pointer can cross the FFI boundary, and is easier to wrangle on the JavaScript side. We can either add a layer of indirection with Box<Vec<u8>> or we can save the extra data needed to reconstruct the vector on the side. We choose the latter approach.

A vector is a triple of

  1. a pointer to the heap-allocated elements,
  2. the capacity of that allocation, and
  3. the length of the initialized elements.

Since we are only exposing the pointer to the heap-allocated elements to JavaScript, so we need a way to save the length and capacity so we can reconstruct the Vec on demand. We allocate two extra words of space and store the length and capacity in the first two words of the heap elements and then give JavaScript a pointer to the space just after those extra words.

<figure class="highlight">
#[no_mangle]
pub extern "C" fn allocate_mappings(size: usize) -> *mut u8 {
    // Make sure that we don't lose any bytes from size in the remainder.
    let size_in_units_of_usize = (size + mem::size_of::<usize>() - 1)
        / mem::size_of::<usize>();

    // Make room for two additional `usize`s: we'll stuff capacity and
    // length in there.
    let mut vec: Vec<usize> = Vec::with_capacity(size_in_units_of_usize + 2);

    // And do the stuffing.
    let capacity = vec.capacity();
    vec.push(capacity);
    vec.push(size);

    // Leak the vec's elements and get a pointer to them.
    let ptr = vec.as_mut_ptr();
    debug_assert!(!ptr.is_null());
    mem::forget(vec);

    // Advance the pointer past our stuffed data and return it to JS,
    // so that JS can write the mappings string into it.
    let ptr = ptr.wrapping_offset(2) as *mut u8;
    assert_pointer_is_word_aligned(ptr);
    ptr
}
</figure>

After initializing the buffer for the "mappings" string, JavaScript passes ownership of the buffer to parse_mappings, which parses the string into a queryable structure. A pointer to the queryable Mappings structure is returned, or NULL if there was some kind of parse failure.

The first thing that parse_mappings must do is recover the Vec’s length and capacity. Next, it constructs a slice of the "mappings" string data, constrains the lifetime of the slice to the current scope to double check that we don’t access the slice again after its deallocated, and call into our library’s "mappings" string parsing function. Regardless whether parsing succeeded or not, we deallocate the buffer holding the "mappings" string, and return a pointer to the parsed structure or save any error that may have occurred and return NULL.

<figure class="highlight">
/// Force the `reference`'s lifetime to match the `scope`'s
/// lifetime. Certain `unsafe` operations, such as dereferencing raw
/// pointers, return references with un-constrained lifetimes, and we
/// use this function to ensure we can't accidentally use the
/// references after they become invalid.
#[inline]
fn constrain<'a, T>(_scope: &'a (), reference: &'a T) -> &'a T
where
    T: ?Sized
{
    reference
}

#[no_mangle]
pub extern "C" fn parse_mappings(mappings: *mut u8) -> *mut Mappings<Observer> {
    assert_pointer_is_word_aligned(mappings);
    let mappings = mappings as *mut usize;

    // Unstuff the data we put just before the pointer to the mappings
    // string.
    let capacity_ptr = mappings.wrapping_offset(-2);
    debug_assert!(!capacity_ptr.is_null());
    let capacity = unsafe { *capacity_ptr };

    let size_ptr = mappings.wrapping_offset(-1);
    debug_assert!(!size_ptr.is_null());
    let size = unsafe { *size_ptr };

    // Construct the input slice from the pointer and parse the mappings.
    let result = unsafe {
        let input = slice::from_raw_parts(mappings as *const u8, size);
        let this_scope = ();
        let input = constrain(&this_scope, input);
        source_map_mappings::parse_mappings(input)
    };

    // Deallocate the mappings string and its two prefix words.
    let size_in_usizes = (size + mem::size_of::<usize>() - 1) / mem::size_of::<usize>();
    unsafe {
        Vec::<usize>::from_raw_parts(capacity_ptr, size_in_usizes + 2, capacity);
    }

    // Return the result, saving any errors on the side for later inspection by
    // JS if required.
    match result {
        Ok(mappings) => Box::into_raw(Box::new(mappings)),
        Err(e) => {
            unsafe {
                LAST_ERROR = Some(e);
            }
            ptr::null_mut()
        }
    }
}
</figure>

When we run queries, we need a way to translate the results across the FFI boundary. The results of a query are either a single Mapping or set of many Mappings, and a Mapping can’t cross the FFI boundary as-is unless we box it. We do not wish to box Mappings since we would also need to provide getters for each of its fields, causing code bloat on top of the costs of allocation and indirection. Our solution is to call an imported function for each Mapping query result.

The mappings_callback is an extern function without definition, which translates to an imported function in WebAssembly that the JavaScript must provide when instantiating the WebAssembly module. The mappings_callback takes a Mapping that is exploded into its member parts: each field in a transitively flattened Mapping is translated into a parameter so that it can cross the FFI boundary. For Option<T> members, we add a bool parameter that serves as a discriminant, determining whether the Option<T> is Some or None, and therefore whether the following T parameter is valid or garbage.

<figure class="highlight">
extern "C" {
    fn mapping_callback(
        // These two parameters are always valid.
        generated_line: u32,
        generated_column: u32,

        // The `last_generated_column` parameter is only valid if
        // `has_last_generated_column` is `true`.
        has_last_generated_column: bool,
        last_generated_column: u32,

        // The `source`, `original_line`, and `original_column`
        // parameters are only valid if `has_original` is `true`.
        has_original: bool,
        source: u32,
        original_line: u32,
        original_column: u32,

        // The `name` parameter is only valid if `has_name` is `true`.
        has_name: bool,
        name: u32,
    );
}

#[inline]
unsafe fn invoke_mapping_callback(mapping: &Mapping) {
    let generated_line = mapping.generated_line;
    let generated_column = mapping.generated_column;

    let (
        has_last_generated_column,
        last_generated_column,
    ) = if let Some(last_generated_column) = mapping.last_generated_column {
        (true, last_generated_column)
    } else {
        (false, 0)
    };

    let (
        has_original,
        source,
        original_line,
        original_column,
        has_name,
        name,
    ) = if let Some(original) = mapping.original.as_ref() {
        let (
            has_name,
            name,
        ) = if let Some(name) = original.name {
            (true, name)
        } else {
            (false, 0)
        };

        (
            true,
            original.source,
            original.original_line,
            original.original_column,
            has_name,
            name,
        )
    } else {
        (
            false,
            0,
            0,
            0,
            false,
            0,
        )
    };

    mapping_callback(
        generated_line,
        generated_column,
        has_last_generated_column,
        last_generated_column,
        has_original,
        source,
        original_line,
        original_column,
        has_name,
        name,
    );
}
</figure>

All of the exported query functions have a similar structure. They begin by converting a raw *mut Mappings pointer into an &mut Mappings mutable reference. The &mut Mappings lifetime is constrained to the current scope to enforce that it is only used in this function call, and not saved away somewhere where it might be used after it is deallocated. Next, the exported query function forwards the query to the corresponding Mappings method. For each resulting Mapping, the exported function invokes the mapping_callback.

A typical example is the exported all_generated_locations_for query function, which wraps the Mappings::all_generated_locations_for method, and finds all the mappings corresponding to the given original source location.

<figure class="highlight">
#[inline]
unsafe fn mappings_mut<'a>(
    _scope: &'a (),
    mappings: *mut Mappings,
) -> &'a mut Mappings {
    mappings.as_mut().unwrap()
}

#[no_mangle]
pub extern "C" fn all_generated_locations_for(
    mappings: *mut Mappings,
    source: u32,
    original_line: u32,
    has_original_column: bool,
    original_column: u32,
) {
    let this_scope = ();
    let mappings = unsafe { mappings_mut(&this_scope, mappings) };

    let original_column = if has_original_column {
        Some(original_column)
    } else {
        None
    };

    let results = mappings.all_generated_locations_for(
        source,
        original_line,
        original_column,
    );
    for m in results {
        unsafe {
            invoke_mapping_callback(m);
        }
    }
}
</figure>

Finally, when the JavaScript is finished querying the Mappings, it must deallocate them with the exported free_mappings function.

<figure class="highlight">
#[no_mangle]
pub extern "C" fn free_mappings(mappings: *mut Mappings) {
    unsafe {
        Box::from_raw(mappings);
    }
}
</figure>

Compiling Rust into .wasm Files

The addition of the wasm32-unknown-unknown target makes compiling Rust into WebAssembly possible, and rustup makes installing a Rust compiler toolchain targeting wasm32-unknown-unknown easy:

$ rustup update
$ rustup target add wasm32-unknown-unknown

Now that we have a wasm32-unknown-unknown compiler, the only difference between building for our host platform and WebAssembly is a --target flag:

$ cargo build --release --target wasm32-unknown-unknown

The resulting .wasm file is at target/wasm32-unknown-unknown/release/source_map_mappings_wasm_api.wasm.

Although we now have a working .wasm file, we are not finished: this .wasm file is still much larger than it needs to be. To produce the smallest .wasm file we can, we leverage the following tools:

  • wasm-gc, which is like a linker’s --gc-sections flag that removes unused object file sections, but for .wasm files instead of ELF, Mach-O, etc. object files. It finds all functions that are not transitively reachable from an exported function and removes them from the .wasm file.

  • wasm-snip, which is used to replace a WebAssembly function’s body with a single unreachable instruction. This is useful for manually removing functions that will never be called at runtime, but which the compiler and wasm-gc couldn’t statically prove were unreachable. Snipping a function can make other functions statically unreachable, so it makes sense to run wasm-gc again afterwards.

  • wasm-opt, which runs binaryen’s optimization passes over a .wasm file, shrinking its size and improving its runtime performance. Eventually, when LLVM’s WebAssembly backend matures, this may no longer be necessary.

Our post-build pipeline goes wasm-gcwasm-snipwasm-gcwasm-opt.

Using WebAssembly APIs in JavaScript

The first concern when using WebAssembly in JavaScript is how to load the .wasm files. The source-map library is primarily used in three environments:

  1. Node.js
  2. The Web
  3. Inside Firefox’s Developer Tools

Different environments can have different methods for loading a .wasm file’s bytes into an ArrayBuffer that can then be compiled by the JavaScript runtime. On the Web and inside Firefox, we can use the standard fetch API to make an HTTP request to load the .wasm file. It is the library consumer’s responsibility to provide a URL pointing to the .wasm file before parsing any source maps. When used with Node.js, the library uses the fs.readFile API to read the .wasm file from disk. No initialization is required before parsing any source maps in this scenario. We provide a uniform interface regardless which environment the library is loaded in by using feature detection to select the correct .wasm loading implementation.

When compiling and instantiating the WebAssembly module, we must provide the mapping_callback. This callback cannot change across the lifetime of the instantiated WebAssembly module, but depending on what kind of query we are performing, we want to do different things with the resulting mappings. So the actual mapping_callback we provide is only responsible for translating the exploded mapping members into a structured object and then trampolining that result to a closure function that we set depending on what query we are running.

<figure class="highlight">
let currentCallback = null;

// ...

WebAssembly.instantiate(buffer, {
  env: {
    mapping_callback: function (
      generatedLine,
      generatedColumn,

      hasLastGeneratedColumn,
      lastGeneratedColumn,

      hasOriginal,
      source,
      originalLine,
      originalColumn,

      hasName,
      name
    ) {
      const mapping = new Mapping;
      mapping.generatedLine = generatedLine;
      mapping.generatedColumn = generatedColumn;

      if (hasLastGeneratedColumn) {
        mapping.lastGeneratedColumn = lastGeneratedColumn;
      }

      if (hasOriginal) {
        mapping.source = source;
        mapping.originalLine = originalLine;
        mapping.originalColumn = originalColumn;

        if (hasName) {
          mapping.name = name;
        }
      }

      currentCallback(mapping);
    }
  }
})
</figure>

To make setting and unsetting the currentCallback ergonomic, we define a withMappingCallback helper that takes two functions: one to set as the currentCallback, and another to invoke immediately. Once the second function’s execution finishes, we reset the currentCallback to null. This is the JavaScript equivalent of RAII.

<figure class="highlight">
function withMappingCallback(mappingCallback, f) {
  currentCallback = mappingCallback;
  try {
    f();
  } finally {
    currentCallback = null;
  }
}
</figure>

Recall that JavaScript’s first task, when parsing a source map, is to tell the WebAssembly to allocate space for the "mappings" string, and then copy the string into the allocated buffer.

<figure class="highlight">
const size = mappingsString.length;
const mappingsBufPtr = this._wasm.exports.allocate_mappings(size);
const mappingsBuf = new Uint8Array(
  this._wasm.exports.memory.buffer,
  mappingsBufPtr,
  size
);
for (let i = 0; i < size; i++) {
  mappingsBuf[i] = mappingsString.charCodeAt(i);
}
</figure>

Once JavaScript has initialized the buffer, it calls the exported parse_mappings WebAssembly function and translates any failures into an Error that gets thrown.

<figure class="highlight">
const mappingsPtr = this._wasm.exports.parse_mappings(mappingsBufPtr);
if (!mappingsPtr) {
  const error = this._wasm.exports.get_last_error();
  let msg = `Error parsing mappings (code ${error}): `;
  // XXX: keep these error codes in sync with `fitzgen/source-map-mappings`.
  switch (error) {
    case 1:
      msg += "the mappings contained a negative line, column, source index, or name index";
      break;
    case 2:
      msg += "the mappings contained a number larger than 2**32";
      break;
    case 3:
      msg += "reached EOF while in the middle of parsing a VLQ";
      break;
    case 4:
      msg += "invalid base 64 character while parsing a VLQ";
      break
    default:
      msg += "unknown error code";
      break;
  }

  throw new Error(msg);
}

this._mappingsPtr = mappingsPtr;
</figure>

The various query methods that call into WebAssembly have similar structure, just like the exported functions on the Rust side do. They validate query parameters, set up a temporary mappings callback closure with withMappingCallback that aggregates results, calls into WebAssembly, and then return the results.

Here is what allGeneratedPositionsFor looks like in JavaScript.

<figure class="highlight">
BasicSourceMapConsumer.prototype.allGeneratedPositionsFor = function ({
  source,
  line,
  column,
}) {
  const hasColumn = column === undefined;
  column = column || 0;

  source = this._findSourceIndex(source);
  if (source < 0) {
    return [];
  }

  if (originalLine < 1) {
    throw new Error("Line numbers must be >= 1");
  }

  if (originalColumn < 0) {
    throw new Error("Column numbers must be >= 0");
  }

  const results = [];

  this._wasm.withMappingCallback(
    m => {
      let lastColumn = m.lastGeneratedColumn;
      if (this._computedColumnSpans && lastColumn === null) {
        lastColumn = Infinity;
      }
      results.push({
        line: m.generatedLine,
        column: m.generatedColumn,
        lastColumn,
      });
    }, () => {
      this._wasm.exports.all_generated_locations_for(
        this._getMappingsPtr(),
        source,
        line,
        hasColumn,
        column
      );
    }
  );

  return results;
};
</figure>

When JavaScript is done querying the source map, the library consumer should call SourceMapConsumer.prototype.destroy which then calls into the exported free_mappings WebAssembly function.

<figure class="highlight">
BasicSourceMapConsumer.prototype.destroy = function () {
  if (this._mappingsPtr !== 0) {
    this._wasm.exports.free_mappings(this._mappingsPtr);
    this._mappingsPtr = 0;
  }
};
</figure>

Benchmarks

All tests were performed on a MacBook Pro from mid 2014 with a 2.8 GHz Intel Core i7 processor and 16 GB 1600 MHz DDR3 memory. The laptop was plugged into power for every test, and the benchmark Webpage was refreshed between tests. The tests were performed in Chrome Canary 65.0.3322.0, Firefox Nightly 59.0a1 (2018-01-15), and Safari 11.0.2 (11604.4.7.1.6)3. For each benchmark, to warm up the browser’s JIT compiler, we performed five iterations before collecting timings. After warm up, we recorded timings for 100 iterations.

We used a variety of input source maps with our benchmarks. We used three source maps found in the wild of varying sizes:

  1. The source map for the minified-to-unminified versions of the original JavaScript implementation of the source-map library. This source map is created by UglifyJS and its "mappings" string is 30,081 characters long.

  2. The latest Angular.JS’s minified-to-unminified source map. This source map’s "mappings" string is 391,473 characters long.

  3. The Scala.JS runtime’s Scala-to-JavaScript source map. This source map is the largest, and its "mappings" string is 14,964,446 characters long.

Additionally, we augmented the input set with two more artificially constructed source maps:

  1. The Angular.JS source map inflated to ten times its original size. This results in a "mappings" string of size 3,914,739.

  2. The Scala.JS source map inflated to twice its original size. This results in a "mappings" string of size 29,928,893. For this input source map, we only collected 40 iterations on each benchmark.

Astute readers will have noticed an extra 9 and 1 characters in the inflated source maps’ sizes respectively. These are from the ; separator characters between each copy of the original "mappings" string when gluing them together to create the inflated version.

We will pay particular attention to the Scala.JS source map. It is the largest non-artificial source map we tested. Additionally, it is the largest source map for which we have measurements for all combinations of browser and library implementation. There is no data for the largest input (the twice inflated Scala.JS source map) on Chrome. With the JavaScript implementation, we were unable to take any measurements with this combination because none of the benchmarks could complete without the tab’s content process crashing. With the WebAssembly implementation, Chrome would erroneously throw RuntimeError: memory access out of bounds. Using Chrome’s debugger, the supposed out-of-bounds access was happening in an instruction sequence that does not exist in the .wasm file. All other browser’s WebAssembly implementations successfully ran the benchmark with this input, so I am inclined to believe it is a bug in the Chrome implementation.

For all benchmarks, lower values are better.

Setting a Breakpoint for the First Time

The first benchmark simulates a stepping debugger setting a breakpoint on some line in the original source for the first time. This requires that the source map’s "mappings" string is parsed, and that the parsed mappings are sorted by their original source locations so we can binary search to the breakpoint’s line’s mappings. The query returns every generated JavaScript location that corresponds to any mapping on the original source line.

The WebAssembly implementation outperforms its JavaScript counterpart in all browsers. For the Scala.JS source map, the WebAssembly implementation takes 0.65x the amount of time its JavaScript counterpart takes in Chrome, 0.30x in Firefox, and 0.37x in Safari. The WebAssembly implementation is fastest in Safari, taking 702ms on average compared to Chrome’s 1140ms and Firefox’s 877ms.

Furthermore, the relative standard deviation of the WebAssembly implementation is more narrow than the JavaScript implementation, particularly in Firefox. For the Scala.JS source map with the JavaScript implementation, Chrome’s relative standard deviation is ±4.07%, Firefox’s is ±10.52%, and Safari’s is ±6.02%. In WebAssembly, the spreads shrink down to ±1.74% in Chrome, ±2.44% in Firefox, and ±1.58% in Safari.

Pausing at an Exception for the First Time

The second benchmark exercises the code path for the first time that a generated JavaScript location is used to look up its corresponding original source location. This happens when a stepping debugger pauses at an uncaught exception originating from within the generated JavaScript code, when a console message is logged from within the generated JavaScript code, or when stepping into the generated JavaScript code from some other JavaScript source.

To translate a generated JavaScript location into an original source location, the "mappings" string must be parsed. The parsed mappings must then be sorted by generated JavaScript location, so that we can binary search for the closest mapping, and finally that closest mapping’s original source location is returned.

Once again, the WebAssembly implementation outperforms the JavaScript implementation in all browsers — by an even larger lead this time. With the Scala.JS source map, in Chrome, the WebAssembly implementation takes 0.23x the amount time that the JavaScript implementation takes. In both Firefox and Safari, the WebAssembly takes 0.17x the time the JavaScript takes. Once more, Safari runs the WebAssembly fastest (305ms), followed by Firefox (397ms), and then Chrome (486ms).

The WebAssembly implementation is also less noisy than the JavaScript implementation again. The relative standard deviations fell from ±4.04% to 2.35±% in Chrome, from ±13.75% to ±2.03% in Firefox, and from ±6.65% to ±3.86% in Safari for the Scala.JS source map input.

Subsequent Breakpoints and Pauses at Exceptions

The third and fourth benchmarks observe the time it takes to set subsequent breakpoints after the first one, or to pause at subsequent uncaught exceptions, or to translate subsequent logged messages’ locations. Historically, these operations have never been a performance bottleneck: the expensive part is the initial "mappings" string parse and construction of queryable data structures (the sorted arrays).

Nevertheless, we want to make sure it stays that way: we don’t want these operations to suddenly become costly.

Both of these benchmarks are measuring the time it takes to binary search for the closest mapping to the target location and returning that mapping’s co-location.

These benchmark results should be seasoned with more salt than the other benchmarks’ results. Looking at both of the Scala.JS source map input plots, we see clear strata formations. This layering happens because the benchmarked operations run in such little time that the resolution of our timer becomes visible. We can see that Chrome exposes timers with resolution to tenths of a millisecond, Firefox exposes them with resolution to .02 milliseconds, and Safari exposes millisecond resolution.

All we can conclude, based on this data, is that subsequent queries largely remain sub-millisecond operations in both JavaScript and WebAssembly implementations. Subsequent queries have never been a performance bottleneck, and they do not become a bottleneck in the new WebAssembly implementation.

Iterating Over All Mappings

The final two benchmarks measure the time it takes to parse a source map and immediately iterate over its mappings, and to iterate over an already-parsed source map’s mappings. These are common operations performed by build tools that are composing or consuming source maps. They are also sometimes performed by stepping debuggers to highlight to the user which lines within an original source the user can set breakpoints on — it doesn’t make sense to set a breakpoint on a line that doesn’t translate into any location in the generated JavaScript.

This benchmark was the one we worried about: it involves the most JavaScript↔WebAssembly boundary crossing, an FFI call for every mapping in the source map. For all other benchmarks tested, our design minimized the number of such FFI calls.

In turns out our worrying was unfounded. The WebAssembly implementation doesn’t just meet the JavaScript implementation’s performance, even when the source map is already parsed, it surpasses the JavaScript implementation’s performance. For the parse-and-iterate and iterate-already-parsed benchmarks, the WebAssembly takes 0.61x and 0.71x the time of the JavaScript in Chrome. In Firefox, the WebAssembly takes 0.56x and 0.77x the time of the JavaScript. In Safari, the WebAssembly implementation takes 0.63x and 0.87x the amount of time that the JavaScript implementation takes. Once again, Safari runs the WebAssembly implementation the quickest, and Firefox and Chrome are essentially tied for second place. Safari deserves special acknowledgment for its JavaScript performance on the iterate-already-parsed benchmark: beyond just outperforming the other browsers’ JavaScript times, Safari runs the JavaScript faster than the other browsers run the WebAssembly!

True to the trend from earlier benchmarks, we also see reductions in the relative standard deviations in the WebAssembly compared to the JavaScript implementation. When both parsing and iterating, Chrome’s relative standard deviation fell from ±1.80% to ±0.33%, Firefox’s from ±11.63% to ±1.41%, and Safari’s from ±2.73% to ±1.51%. When iterating over an already-parsed source map’s mappings, Firefox’s relative standard deviation dropped from ±12.56% to ±1.40%, and Safari’s from ±1.97% to ±1.40%. Chrome’s relative standard deviation grew from ±0.61% to ±1.18%, making it the only browser to buck the trend, but only on this one benchmark.

Code Size

One of advantages of the wasm32-unknown-unknown target over the wasm32-unknown-emscripten target, is that it generates leaner WebAssembly code. The wasm32-unknown-emscripten target includes polyfills for most of libc and a file system built on top of IndexedDB, among other things, but wasm32-unknown-unknown does not. With the source-map library, we’ve only used wasm32-unknown-unknown.

We consider the code size of the JavaScript and WebAssembly that is delivered to the client. That is, we’re looking at the code size after bundling JavaScript modules together into a single .js file. We look at the effects of using wasm-gc, wasm-snip, and wasm-opt to shrink .wasm file size, and using gzip compression, which is ubiquitously supported on the Web.

In these measurements, the JavaScript size reported is always the size of minified JavaScript, created with the Google Closure Compiler at the “simple” optimization level. We used the Closure Compiler because UglifyJS does not support some newer ECMAScript forms that we introduced (for example let and arrow functions). We used the “simple” optimization level because the “advanced” optimization level is destructive for JavaScript that wasn’t authored with Closure Compiler in mind.

The bars labeled “JavaScript” are for variations of the original, pure-JavaScript source-map library implementation. The bars labeled “WebAssembly” are for variations of the new source-map library implementation that uses WebAssembly for parsing the "mappings" string and querying the parsed mappings. Note that the “WebAssembly” implementation still uses JavaScript for all other functionality! The source-map library has additional features, such as generating source maps, that are still implemented in JavaScript. For the “WebAssembly” implementation, we report the sizes of both the WebAssembly and JavaScript.

At their smallest, the new WebAssembly implementation has a larger total code size than the old JavaScript implementation: 20,996 bytes versus 8,365 bytes respectively. However, by using tools for shrinking .wasm size, we brought the size of the WebAssembly down to 0.16x its original size. Both implementations have similar amounts of JavaScript.

If we replaced JavaScript parsing and querying code with WebAssembly, why doesn’t the WebAssembly implementation contain less JavaScript? There are two factors contributing to the lack of JavaScript code size reductions. First, there is some small amount of new JavaScript introduced to load the .wasm file and interface with the WebAssembly. Second, and more importantly, some of the JavaScript routines that we “replaced” were previously shared with other parts of the source-map library. Now, although those routines are no longer shared, they are still in use by those other portions of the library.

Let us turn our focus towards what is contributing to the size of the pre-gziped .wasm file. Running wasm-objdump -h gives us the sizes of each section:

The Code and Data sections effectively account for the whole .wasm file’s size. The Code section contains the encoded WebAssembly instructions that make up function bodies. The Data section consists of static data to be loaded into the WebAssembly module’s linear memory space.

Using wasm-objdump to manually inspect the Data section’s contents shows that it mostly consists of string fragments used for constructing diagnostic messages if the Rust code were to panic. However, when targeting WebAssembly, Rust panics translate into WebAssembly traps, and traps do not carry extra diagnostic information. We consider it a bug in rustc that these string fragments are even emitted. Unfortunately, wasm-gc cannot currently remove unused Data segments either, so we are stuck with this bloat for the meantime. WebAssembly and its tooling is still relatively immature, and we expect the toolchain to improve in this respect with time.

Next, we post-process wasm-objdump’s disassembly output to compute the size of each function body inside the Code section, and group sizes by Rust crate:

The heaviest crate is dlmalloc which is used by the alloc crate, which implements Rust’s low-level allocation APIs. Together, dlmalloc and alloc clock in at 10,126 bytes, or 50.98% the total function size. In a sense, this is a relief: the code size of the allocator is a constant that will not grow as we port more JavaScript code to Rust.

The sum of code sizes from crates that we authored (vlq, source_map_mappings, and source_map_mappings_wasm_api) is 9,320 bytes, or 46.92% the total function size. That leaves only 417 bytes (2.10%) of space consumed by the other crates. This speaks to the efficacy of wasm-gc, wasm-snip, and wasm-opt: although crates like std are much larger than our crates, we only use a tiny fraction of its APIs and we only pay for what we use.

Conclusions and Future Work

Rewriting the most performance-sensitive portions of source map parsing and querying in Rust and WebAssembly has been a success. On our benchmarks, the WebAssembly implementation takes a fraction the time that the original JavaScript implementation takes — as little as 0.17x the time. We observe that, for all browsers, the WebAssembly implementation is faster than the JavaScript implementation. Furthermore, the WebAssembly implementation offers more consistent and reliable performance: relative standard deviation between iterations dropped significantly compared to the JavaScript implementation.

The JavaScript implementation has accumulated convoluted code in the name of performance, and we replaced it with idiomatic Rust. Rust does not force us to choose between clearly expressing intent and runtime performance.

That said, there is still more work to do.

The most pressing next step is investigating why the Rust standard library’s sorting is not as fast as our custom Quicksort when targeting WebAssembly. This is the sole unidiomatic bit of Rust code in the rewrite. This behavior is surprising since our Quicksort implementation is naive, and the standard library’s quick sort is pattern defeating and opportunistically uses insertion sort for small and mostly-sorted ranges. In fact, the standard library’s sorting routine is faster than ours when targeting native code. We speculate that inlining heuristics are changing across targets, and that our comparator functions aren’t being inlined into the standard library’s sorting routine when targeting WebAssembly. It requires further investigation.

We found size profiling WebAssembly was more difficult than necessary. To get useful information presented meaningfully, we were forced to write our own home-grown script to post-process wasm-objdump. The script constructs the call graph, and lets us query who the callers of some function were, helping us understand why the function was emitted in the .wasm file, even if we didn’t expect it to be. It is pretty hacky, and doesn’t expose information about inlined functions. A proper WebAssembly size profiler would have helped, and would be a benefit for anyone following in our tracks.

The relatively large code size footprint of the allocator suggests that writing or adapting an allocator that is focused on tiny code size could provide considerable utility for the WebAssembly ecosystem. At least for our use case, allocator performance is not a concern, and we only make a small handful of dynamic allocations. For an allocator, we would choose small code size over performance in a heartbeat.

The unused segments in our Data section highlight the need for wasm-gc, or another tool, to detect and remove static data that is never referenced.

There are still some JavaScript API improvements that we can make for the library’s downstream users. The introduction of WebAssembly in our current implementation requires the introduction of manually freeing the parsed mappings when the user is finished with it. This does not come naturally to most JavaScript programmers, who are used to relying on a garbage collector, and do not typically think about the lifetime of any particular object. We could introduce a SourceMapConsumer.with function that took a raw, un-parsed source map, and an async function. The with function would construct a SourceMapConsumer instance, invoke the async function with it, and then call destroy on the SourceMapConsumer instance once the async function call completes. This is like async RAII for JavaScript.

<figure class="highlight">
SourceMapConsumer.with = async function (rawSourceMap, f) {
  const consumer = await new SourceMapConsumer(rawSourceMap);
  try {
    await f(consumer);
  } finally {
    consumer.destroy();
  }
};
</figure>

Another alternative way to make the API easier to work with for JavaScript programmers would be to give every SourceMapConsumer its own instance of the WebAssembly module. Then, because the SourceMapConsumer instance would have the sole GC edge to its WebAssembly module instance, we could let the garbage collector manage all of the SourceMapConsumer instance, the WebAssembly module instance, and the module instance’s heap. With this strategy, we would have a single static mut MAPPINGS: Mappings in the Rust WebAssembly glue code, and the Mappings instance would be implicit in all exported query function calls. No more Box::new(mappings) in the parse_mappings function, and no more passing around *mut Mappings pointers. With some care, we might be able to remove all allocation from the Rust library, which would shrink the emitted WebAssembly to half its current size. Of course, this all depends on creating multiple instances of the same WebAssembly module being a relatively cheap operation, which requires further investigation.

The wasm-bindgen project aims to remove the need to write FFI glue code by hand, and automates interfacing between WebAssembly and JavaScript. Using it, we should be able to remove all the hand-written unsafe pointer manipulation code involved in exporting Rust APIs to JavaScript.

In this project, we ported source map parsing and querying to Rust and WebAssembly, but this is only half of the source-map library’s functionality. The other half is generating source maps, and it is also performance sensitive. We would like to rewrite the core of building and encoding source maps in Rust and WebAssembly sometime in the future as well. We expect to see similar speed ups to what we observed for parsing source maps.

The pull request adding the WebAssembly implementation to the mozilla/source-map library is in the progress of merging here. That pull request contains the benchmarking code, so that results can be reproduced, and we can continue to improve them.

Finally, I’d like to thank Tom Tromey for hacking on this project with me. I’d also like to thank Aaron Turon, Alex Crichton, Jeena Lee, Jim Blandy, Lin Clark, Luke Wagner, Michael Cooper, and Till Schneidereit for reading early drafts and providing valuable feedback. This document, our benchmarks, and the source-map library are all better thanks to them.


0 Or “transpiler” if you must insist.

1 SpiderMonkey now uses a self-hosted JavaScript implementation of Array.prototype.sort when there is a custom comparator function, and a C++ implementation when there is not.

2 The overhead of calls between WebAssembly and JavaScript should mostly disappear in Firefox once bug 1319203 lands. After that, calls between JavaScript and WebAssembly will have similar overheads to out-of-line calls between JavaScript functions. But that patch hasn’t landed yet, and other browsers haven’t landed equivalent improvements yet either.

3 For Firefox and Chrome, we tested with the latest nightly builds. We did not do the same with Safari because the latest Safari Technology Preview requires a newer macOS version than El Capitan, which this laptop was running.

Planet MozillaGiving and receiving help at Mozilla

This is going to sound corny, but helping people really is one of my favorite things at Mozilla, even with projects I have mostly moved on from. As someone who primarily works on internal tools, I love hearing about bugs in the software I maintain or questions on how to use it best.

Given this, you might think that getting in touch with me via irc or slack is the fastest and best way to get your issue addressed. We certainly have a culture of using these instant-messaging applications at Mozilla for everything and anything. Unfortunately, I have found that being “always on” to respond to everything hasn’t been positive for either my productivity or mental health. My personal situation aside, getting pinged on irc while I’m out of the office often results in stuff getting lost — the person who asked me the question is often gone by the time I return and am able to answer.

With that in mind, here’s some notes on my preferred conversation style when making initial contact about an issue:

  1. Please don’t send context-free pings on irc. It has been explained elsewhere why this doesn’t work that well, so I won’t repeat the argument here.
  2. If you are at all suspicious that your issue might be a bug in some software maintain, just file a bug and needinfo me. That puts us right on the path to documenting the problem and getting to a resolution — even if something turns out to not be a bug, if you’re seeing an unexpected error it points to a usability issue.
  3. For everything else, email is best. I do check it quite frequently between bursts of work (i.e. many times a day). I promise I won’t leave you hanging for days on end as long as I’m not on vacation.

These aren’t ironclad rules. If your question pertains to a project I’m actively working on, it might make sense to ping me on irc first (preferably on a channel where other people are around who might also be able to help). If it’s an actual emergency, then of course talk to me on irc straight away (or even call me on my phone) — if I don’t respond, then fall back to filing bug or sending email. Use common sense.

One of my new years resolutions is to also apply these rules to my communications with others at Mozilla as well, so if you see my violating it feel free to point me back at this post. Or just use this handy meme I created:

Planet MozillaGiving and receiving help at Mozilla

This is going to sound corny, but helping people really is one of my favorite things at Mozilla, even with projects I have mostly moved on from. As someone who primarily works on internal tools, I love hearing about bugs in the software I maintain or questions on how to use it best.

Given this, you might think that getting in touch with me via irc or slack is the fastest and best way to get your issue addressed. We certainly have a culture of using these instant-messaging applications at Mozilla for everything and anything. Unfortunately, I have found that being “always on” to respond to everything hasn’t been positive for either my productivity or mental health. My personal situation aside, getting pinged on irc while I’m out of the office often results in stuff getting lost — the person who asked me the question is often gone by the time I return and am able to answer.

With that in mind, here’s some notes on my preferred conversation style when making initial contact about an issue:

  1. Please don’t send context-free pings on irc. It has been explained elsewhere why this doesn’t work that well, so I won’t repeat the argument here.
  2. If you are at all suspicious that your issue might be a bug in some software maintain, just file a bug and needinfo me. That puts us right on the path to documenting the problem and getting to a resolution — even if something turns out to not be a bug, if you’re seeing an unexpected error it points to a usability issue.
  3. For everything else, email is best. I do check it quite frequently between bursts of work (i.e. many times a day). I promise I won’t leave you hanging for days on end as long as I’m not on vacation.

These aren’t ironclad rules. If your question pertains to a project I’m actively working on, it might make sense to ping me on irc first (preferably on a channel where other people are around who might also be able to help). If it’s an actual emergency, then of course talk to me on irc straight away (or even call me on my phone) — if I don’t respond, then fall back to filing bug or sending email. Use common sense.

One of my new years resolutions is to also apply these rules to my communications with others at Mozilla as well, so if you see my violating it feel free to point me back at this post. Or just use this handy meme I created:

Planet MozillaThe Joy of Coding - Episode 126

The Joy of Coding - Episode 126 mconley livehacks on real Firefox bugs while thinking aloud.

Planet MozillaThe Joy of Coding - Episode 126

The Joy of Coding - Episode 126 mconley livehacks on real Firefox bugs while thinking aloud.

Planet MozillaThe User Journey for Firefox Extensions Discovery

The ability to customize and extend Firefox are an important part of Firefox’s value to many users. Extensions are small tools that allow developers and users who install the extensions to modify, customize, and extend the functionality of Firefox. For example, during our workflows research in 2016, we interviewed a participant who was a graduate student in Milwaukee, Wisconsin. While she used Safari as her primary browser for common browsing, she used Firefox specifically for her academic work because of the extension Zotero was the best choice for keeping track of her academic work and citations within the browser. The features offered by Zotero aren’t built into Firefox, but added to Firefox by an independent developer.

Popular categories of extensions include ad blockers, password managers, and video downloaders. Given the variety of extensions and the benefits to customization they offer, why is it that only 40% of Firefox users have installed at least one extension? Certainly, some portion of Firefox users may be aware of extensions but have no need or desire to install one. However, some users could find value in some extensions but simply may not be aware of the existence of extensions in the first place.

Why not? How can Mozilla facilitate the extension discovery process?

A fundamental assumption about the extension discovery process is that users will learn about extensions through the browser, through word of mouth, or through searching to solve a specific problem. We were interested in setting aside this assumption and to observe the steps participants take and the decisions they make in their journey toward possibly discovering extensions. To this end, the Firefox user research team ran two small qualitative studies to understand better how participants solved a particular problem in the browser that could be solved by installing an extension. Our study helped us understand how participants do — or do not — discover a specific category of extension.

Our Study

Because ad blockers are easily the most popular type of extension in installation volume on Firefox (more generally, some kind of ad blocking is being used on 615 million devices worldwide in 2017), we chose them as an obvious candidate for this study. Their popularity and many users’ perception of some advertising as invasive and distracting is a good mix that we believed could pose a common solvable problem for participants to engage with. (Please do not take our choice of this category of extensions for this study as an official or unofficial endorsement or statement from Mozilla about ad blocking as a user tool.)

<figure><figcaption>Some ad blocker extensions currently available on AMO.</figcaption></figure>

In order to understand better how users might discover extensions (and why those chose a particular path), we conducted two small qualitative studies. The first study was conducted in-person in our Portland, Oregon offices with five participants. To gather more data, we conducted a remote, unmoderated study using usertesting.com with nine participants in the US, UK, and Canada. In total, we worked with fourteen participants. These participants used Firefox as their primary web browser and were screened to make certain that they had no previous experience with extensions in Firefox.

In both iterations of the study, we asked participants to complete the following task in Firefox:

Let’s imagine for a moment that you are tired of seeing advertising in Firefox while you are reading a news story online. You feel like ads have become too distracting and you are tired of ads following you around while you visit different sites. You want to figure out a way to make advertisements go away while you browse in Firefox. How would you go about doing that? Show us. Take as much time as you need to reach a solution that you are satisfied with.

Participants fell into roughly two categories: those who installed an ad blocking extension — such as Ad Block Plus or uBlock Origin — and those who did not. The participants who did not install an extension came up with a more diverse set of solutions.

First, among the fourteen participants across the two studies, only six completed the task by discovering an ad blocking extension (two of these did not install the extension for other reasons). The participants who completed the task in this manner all followed a similar path: they searched via a search engine to seek an answer. Most of these participants were not satisfied with accepting only the extensions that were surfaced from search results. Instead, these participants exhibited a high degree of information literacy and used independent reviews to assess and triangulate the quality, reputation, and reliability of the ad blocking extension they selected. More generally, they used the task as an opportunity to build knowledge about online advertising and ad blocking; they looked outside of their familiar tools and information sources.

<figure><figcaption>User journey for a participant who discovered an ad blocker to complete the task.</figcaption></figure>

One participant (who we’ll call “Andrew”) in our first study is a technically savvy user but does not frequently customize the software he uses. When prompted by our task, he said, “I’m going to do what I always do in situations where I don’t know the answer…I’m going to search around for information.” In this case, Andrew recalled that he had heard someone once mention something about blocking ads online and searched for “firefox ad block” in DuckDuckGo. Looking through the search results, he found a review on CNET.com that described various ad blockers, including uBlock Origin. While Andrew says that he “trusts CNET,” he wanted to find a second review source that would provide him with more information. After reading an additional review, he follows a link to the uBlock Firefox extension page, reads the reviews and ratings there (specifically, he is looking for a large number of positive reviews as a proxy for reliability and trustworthiness), and installs the extension. For his final step, P8 visits two sites (wired.com and huffingtonpost.com) that he believes use a large number of display ads and confirms that the ad blocker is suppressing their display.

The remaining participants did not install an ad blocking extension to complete the task. Unlike the participants who ultimately installed an ad blocking extension, these participants did not use a search engine or an outside information source in order to seek a solution to the task. Instead, they fell back on and looked inside tools and resources with which they were already familiar. Also, unlike the participants who were successful installing an ad blocking extension, these participants typically said they were dissatisfied with their solutions.

These participants generally followed two main routes to complete the task: they either looked within the browser in Preferences or other menus or they looked into the ads themselves. For the participants who looked in Preferences, many found privacy and security related options, but none related to blocking advertising. These participants did not seek out outside information sources via a search engine and eventually they gave up on the task.

A participant (“Marion”) in our first study recalled that she had seen a link once in an advertisement offering her “Ad Choices.” Ad Choices is a self-regulatory program in North America and the EU that is “aimed to give consumers enhanced transparency and control.” Marion navigated to a site she remembered had display advertising that offered a link to information on the program. She followed the link which took her to a long list of options for opting out of specific advertising themes and networks. Marion selected and deselected the choices she believed were relevant to her. Unfortunately, the confirmations from the various ad networks for her actions either did not work or did not provide much certainty into her actions. She navigated back to the site that she visited previously and could not discern any real difference in display advertising before and after enrolling in the Ad Choices program.

<figure><figcaption>Marion attempting to opt out of advertising online using Ad Choices.</figcaption></figure>

There were some limitations with this research. The task provided to participants is only a single situation that would cue them to seek out an extension to solve a problem. Rather, we imagine a cumulative sense of frustration might prompt users to seek out a solution. We would anticipate that they may hear about ad blocking from word of mouth or a news story (as participants in a similar study run at the same time with participants who were familiar with extensions demonstrated). Also, ad blocking is a specific domain of extension that has its own players and ecosystem. If we asked participants for example to find a solution to managing their passwords, we would expect a different set of solutions.

At a high level, those participants who displayed elements of traditionally defined digital information literacy were successful in discovering extensions to complete the task. This observation emerged through the analysis process in our second study. In future research, it would be useful to include some measurement of participants’ digital literacy or include related tasks to determine the strength of this relationship.

Nevertheless, this study provided us with insight into how users seek out information to discover solutions to problems in their browser. We gathered useful data on users’ mental model of the software discovery process, the information sources they looked to discover new functionality, and finally what kinds of information was important, reliable, and trustworthy. Those participants who did not seek out independent answers, fell back on the tools and information with which they were already familiar; these participants were also less satisfied with their solutions. These results demonstrate a need to provide cues to participants about Extensions within tools they are familiar with already such as Firefox’s Preferences/Options menu. Further, we need to surface cues in other channels (such as search engine results pages) that could lead Firefox users to relevant extensions.

Thanks to Philip Walmsley, Markus Jaritz, and Emanuela Damiani for their participation in this research project. Thanks to Jennifer Davidson and Heather McGaw for proofreading and suggestions.


The User Journey for Firefox Extensions Discovery was originally published in Firefox User Experience on Medium, where people are continuing the conversation by highlighting and responding to this story.

Planet MozillaFriend of Add-ons: Trishul Goel

Our newest Friend of Add-ons is Trishul Goel! Trishul first became involved with Mozilla five years when he was introduced to the Firefox OS smartphone. As a JavaScript developer with an interest in Mozilla’s mission, he looked for opportunities to get involved and began contributing to SUMO, L10n, and the Firefox OS Marketplace, where he contributed code and developed and reviewed apps.

After Firefox OS was discontinued as a commercial product, Trishul became interested in contributing to Mozilla’s add-ons projects. After landing his first code contributions to addons.mozilla.org (AMO), he set about learning how to develop extensions for Firefox using WebExtensions APIs. Soon, he began sharing his knowledge by leading and mentoring  workshops for extension developers as part of Mozilla’s “Build Your Own Extension” Activate campaign.

In the last year, Trishul has contributed more than 45 patches to Mozilla’s add-ons repositories, published 11 extensions to AMO, served as a member of the Featured Add-ons Advisory Board, and created a great set of WebExtensions APIs tutorials for new extension developers. Currently, he is an active Mozilla Rep and Tech Speaker, and is writing React code for AMO.

When asked what he has gained from his experience contributing to Mozilla, Trishul says, “I grew my skill set while coding for Mozilla. I’ve learned how to mentor people and how to work on a cross-culture remote team.”

In his free time, Trishul enjoys traveling and mountain biking.

Congratulations, Trishul, and thank you for your contributions to the add-ons community!

Are you a contributor to the add-ons community or know of someone who should be recognized? Please be sure to add them to our Recognition Wiki!

The post Friend of Add-ons: Trishul Goel appeared first on Mozilla Add-ons Blog.

Planet MozillaMaking WebAssembly even faster: Firefox’s new streaming and tiering compiler

People call WebAssembly a game changer because it makes it possible to run code on the web faster. Some of these speedups are already present, and some are yet to come.

One of these speedups is streaming compilation, where the browser compiles the code while the code is still being downloaded. Up until now, this was just a potential future speedup. But with the release of Firefox 58 next week, it becomes a reality.

Firefox 58 also includes a new 2-tiered compiler. The new baseline compiler compiles code 10–15 times faster than the optimizing compiler.

Combined, these two changes mean we compile code faster than it comes in from the network.

On a desktop, we compile 30-60 megabytes of WebAssembly code per second. That’s faster than the network delivers the packets.

If you use Firefox Nightly or Beta, you can give it a try on your own device. Even on a pretty average mobile device, we can compile at 8 megabytes per second —which is faster than the average download speed for pretty much any mobile network.

This means your code executes almost as soon as it finishes downloading.

Why is this important?

Web performance advocates get prickly when sites ship a lot of JavaScript. That’s because downloading lots of JavaScript makes pages load slower.

This is largely because of the parse and compile times. As Steve Souders points out, the old bottleneck for web performance used to be the network. But the new bottleneck for web performance is the CPU, and particularly the main thread.

Old bottleneck, the network, on the left. New bottleneck, work on the CPU such as compiling, on the right

So we want to move as much work off the main thread as possible. We also want to start it as early as possible so we’re making use of all of the CPU’s time. Even better, we can do less CPU work altogether.

With JavaScript, you can do some of this. You can parse files off of the main thread, as they stream in. But you’re still parsing them, which is a lot of work, and you have to wait until they are parsed before you can start compiling. And for compiling, you’re back on the main thread. This is because JS is usually compiled lazily, at runtime.

Timeline showing packets coming in on the main thread, then parsing happening simultaneously on another thread. Once parse is done, execution begins on main thread, interrupted occassionally by compiling

With WebAssembly, there’s less work to start with. Decoding WebAssembly is much simpler and faster than parsing JavaScript. And this decoding and the compilation can be split across multiple threads.

This means multiple threads will be doing the baseline compilation, which makes it faster. Once it’s done, the baseline compiled code can start executing on the main thread. It won’t have to pause for compilation, like the JS does.

Timeline showing packets coming in on the main thread, and decoding and baseline compiling happening across multiple threads simultaneously, resulting in execution starting faster and without compiling breaks.

While the baseline compiled code is running on the main thread, other threads work on making a more optimized version. When the more optimized version is done, it can be swapped in so the code runs even faster.

This changes the cost of loading WebAssembly to be more like decoding an image than loading JavaScript. And think about it… web performance advocates do get prickly about JS payloads of 150 kB, but an image payload of the same size doesn’t raise eyebrows.

Developer advocate on the left tsk tsk-ing about large JS file. Developer advocate on the right shrugging about large image.

That’s because load time is so much faster with images, as Addy Osmani explains in The Cost of JavaScript, and decoding an image doesn’t block the main thread, as Alex Russell discusses in Can You Afford It?: Real-world Web Performance Budgets.

This doesn’t mean that we expect WebAssembly files to be as large as image files. While early WebAssembly tools created large files because they included lots of runtime, there’s currently a lot of work to make these files smaller. For example, Emscripten has a “shrinking initiative”. In Rust, you can already get pretty small file sizes using the wasm32-unknown-unknown target, and there are tools like wasm-gc and wasm-snip which can optimize this even more.

What it does mean is that these WebAssembly files will load much faster than the equivalent JavaScript.

This is big. As Yehuda Katz points out, this is a game changer.

Tweet from Yehuda Katz saying it's possible to parse and compile wasm as fast as it comes over the network.

So let’s look at how the new compiler works.

Streaming compilation: start compiling earlier

If you start compiling the code earlier, you’ll finish compiling it earlier. That’s what streaming compilation does… makes it possible to start compiling the .wasm file as soon as possible.

When you download a file, it doesn’t come down in one piece. Instead, it comes down in a series of packets.

Before, as each packet in the .wasm file was being downloaded, the browser network layer would put it into an ArrayBuffer.

Packets coming in to network layer and being added to an ArrayBuffer

Then, once that was done, it would move that ArrayBuffer over to the Web VM (aka the JS engine). That’s when the WebAssembly compiler would start compiling.

Network layer pushing array buffer over to compiler

But there’s no good reason to keep the compiler waiting. It’s technically possible to compile WebAssembly line by line. This means you should be able to start as soon as the first chunk comes in.

So that’s what our new compiler does. It takes advantage of WebAssembly’s streaming API.

WebAssembly.instantiateStreaming call, which takes a response object with the source file. This has to be served using MIME type application/wasm.

If you give WebAssembly.instantiateStreaming a response object, the chunks will go right into the WebAssembly engine as soon as they arrive. Then the compiler can start working on the first chunk while the next one is still being downloaded.

Packets going directly to compiler

Besides being able to download and compile the code in parallel, there’s another advantage to this.

The code section of the .wasm module comes before any data (which will go in the module’s memory object). So by streaming, the compiler can compile the code while the module’s data is still being downloaded. If your module needs a lot of data, the data can be megabytes, so this can be significant.

File split between small code section at the top, and larger data section at the bottom

With streaming, we start compiling earlier. But we can also make compiling faster.

Tier 1 baseline compiler: compile code faster

If you want code to run fast, you need to optimize it. But performing these optimizations while you’re compiling takes time, which makes compiling the code slower. So there’s a tradeoff.

We can have the best of both of these worlds. If we use two compilers, we can have one that compiles quickly without too many optimizations, and another that compiles the code more slowly but creates more optimized code.

This is called a tiered compiler. When code first comes in, it’s compiled by the Tier 1 (or baseline) compiler. Then, after the baseline compiled code starts running, a Tier 2 compiler goes through the code again and compiles a more optimized version in the background.

Once it’s done, it hot-swaps the optimized code in for the previous baseline version. This makes the code execute faster.

Timeline showing optimizing compiling happening in the background.

JavaScript engines have been using tiered compilers for a long time. However, JS engines will only use the Tier 2 (or optimizing) compiler when a bit of code gets “warm”… when that part of the code gets called a lot.

In contrast, the WebAssembly Tier 2 compiler will eagerly do a full recompilation, optimizing all of the code in the module. In the future, we may add more options for developers to control how eagerly or lazily optimization is done.

This baseline compiler saves a lot of time at startup. It compiles code 10–15 times faster than the optimizing compiler. And the code it creates is, in our tests, only 2 times slower.

This means your code will be running pretty fast even in those first few moments, when it’s still running the baseline compiled code.

Parallelize: make it all even faster

In the article on Firefox Quantum, I explained coarse-grained and fine-grained parallelization. We use both for compiling WebAssembly.

I mentioned above that the optimizing compiler will do its compilation in the background. This means that it leaves the main thread available to execute the code. The baseline compiled version of the code can run while the optimizing compiler does its recompilation.

But on most computers that still leaves multiple cores unused. To make the best use of all of the cores, both of the compilers use fine-grained parallelization to split up the work.

The unit of parallelization is the function. Each function can be compiled independently, on a different core. This is so fine-grained, in fact, that we actually need to batch these functions up into larger groups of functions. These batches get sent to different cores.

… then skip all that work entirely by caching it implicitly (future work)

Currently, decoding and compiling are redone every time you reload the page. But if you have the same .wasm file, it should compile to the same machine code.

This means that most of the time, this work could be skipped. And in the future, this is what we’ll do. We’ll decode and compile on first page load, and then cache the resulting machine code in the HTTP cache. Then when you request that URL, it will pull out the precompiled machine code.

This makes load time disappear for subsequent page loads.

Timeline showing all work disappearing with caching.

The groundwork is already laid for this feature. We’re caching JavaScript byte code like this in the Firefox 58 release. We just need to extend this support to caching the machine code for .wasm files.

Planet MozillaFirefox Telemetry Use Counters: Over-estimating usage, now fixed

Firefox Telemetry records the usage of certain web features via a mechanism called Use Counters. Essentially, for every document that Firefox loads, we record a “false” if the document didn’t use a counted feature, and a “true” if the document did use that counted feature.

(( We technically count it when the documents are destroyed, not loaded, since a document could use a feature at any time during its lifetime. We also count top-level documents (pages) separately from the count of all documents (including iframes), so we can see if it is the pages that users load that are using a feature or if it’s the subdocuments that the page author loads on the user’s behalf that are contributing the counts. ))

To save space, we decided to count the number of documents once, and the number of “true” values in each use counter. This saved users from having to tell us they didn’t use any of Feature 1, Feature 2, Feature 5, Feature 7, … the “no-use” use counters. They could just tell us which features they did see used, and we could work out the rest.

Only, we got it wrong.

The server-side adjustment of the counts took every use counter we were told about, and filled in the “false” values. A simple fix.

But it didn’t add in the “no-use” use counters. Users who didn’t see a feature used at all weren’t having their “false” values counted.

This led us to under-count the number of “false” values (since we only counted “falses” from users who had at least one “true”), which led us to overestimate the usage of features.

Of all the errors to have, this one was probably the more benign. In failing in the “overestimate” direction we didn’t incorrectly remove features that were being used more than measured… but we may have kept some features that we could have removed, costing mozilla time and energy for their maintenance.

Once we detected the fault, we started addressing it. First, we started educating people whenever the topic came up in email and bugzilla. Second, :gfritzsche added a fancy Use Counter Dashboard that did a client-side adjustment using the correct “true” and “false” values for a given population.

Third, and finally, we fixed the server-side aggregator service to serve the correct values for all data, current and historical.

And that brings us to today: Use Counters are fixed! Please use them, they’re kind of cool.

:chutten

<figure class="wp-caption alignnone" id="attachment_5668">bfcbd97c-80cf-483b-8707-def6057474e6<figcaption class="wp-caption-text">Before</figcaption></figure> <figure class="wp-caption alignnone" id="attachment_5669">beb4afee-f937-4729-b210-f4e212da7504<figcaption class="wp-caption-text">After (4B more samples)</figcaption></figure>

Planet MozillaFirefox 58 new contributors

With the upcoming release of Firefox 58, we are pleased to welcome the 53 developers who contributed their first code change to Firefox in this release, 49 of whom were brand new volunteers! Please join us in thanking each of these diligent and enthusiastic individuals, and take a look at their contributions:

Planet MozillaLong-Term Consequences Of Spectre And Its Mitigations

The dust is settling on the initial wave of responses to Spectre and Meltdown. Meltdown was relatively simple to deal with; we can consider it fixed. Spectre is much more difficult and has far-reaching consequences for the software ecosystem.

The community is treating Spectre as two different issues, "variant 1" involving code speculatively executed after a conditional branch, and "variant 2" involving code speculatively executed via an indirect branch whose predicted destination is attacker-controlled. I wish these had better names, but c'est la vie.

Spectre variant 1 mitigations

Proposals for mitigating variant 1 have emerged from Webkit, the Linux kernel, and Microsoft. The former two propose similar ideas: masking array indices so that even speculative array loads can't load out-of-bounds. MSVC takes a different approach, introducing LFENCE instructions to block speculative execution when the load address appears to be guarded by a range check. Unfortunately Microsoft says

It is important to note that there are limits to the analysis that MSVC and compilers in general can perform when attempting to identify instances of variant 1. As such, there is no guarantee that all possible instances of variant 1 will be instrumented under /Qspectre.
This seems to be a great weakness, as developers won't know whether this mitigation is actually effective on their code.

The Webkit and Linux kernel approaches have the virtue of being predictable, but at the cost of requiring manual code changes. The fundamental problem is that in C/C++ the compiler generally does not know with certainty the array length associated with an array lookup, thus the masking code must be introduced manually. Webkit goes further and adds protection against speculative loads guarded by dynamic type checks, but again this must be done manually in many cases since C/C++ have no built-in tagged union type.

I think "safe" languages like Rust should generalize the idea behind Webkit's mitigations: require that speculatively executed code adhere to the memory safety constraints imposed by the type system. This would make Spectre variant 1 a lot harder to exploit. It would subsume every variant 1 mitigation I've seen so far, and could be automatic for safe code. Unsafe Rust code would need to be updated.

Having said that, there could be variant-1 attacks that don't circumvent the type system, that none of these mitigations would block. Consider a browser running JS code:

let x = bigArray[iframeElem.contentWindow.someProperty];
Conceivably that could get compiled to some mix of JIT code and C++ that does
  if (iframeElemOrigin == selfDocumentOrigin) {
index = ... get someProperty ...
x = bigArray[index];
} else {
... error ...
}
The speculatively executed code violates no type system invariants, but could leak the value of the property across origins. This example suggests that complete protection against Spectre variant 1 will require draconian mitigations, either pervasive and expensive code instrumentation or deep (and probably error-prone) analysis.

Spectre variant 2 mitigations

There are two approaches here. One is microcode and silicon changes to CPUs to enable flushing and/or disabling of indirect branch predictors. The other is "retpolines" — replace indirect branches with an instruction sequence that doesn't trigger the indirect branch predictor. (More precisely, that doesn't use the BTB; the RSB prediction is used instead, but its prediction is directed to a safe destination address.) Apparently the Linux community is advising all compilers and assembly writers to avoid all indirect branches on Intel even in user-space. This means, for example, that we should update rr's handwritten assembly to avoid indirect branches. On the other hand, Microsoft is not giving such advice and apparently is not planning to introduce retpoline support in MSVC. I don't know why this difference is occurring, but it seems like a problem.

Assuming the Linux community advice is followed, things get even more complicated. Future CPUs can be secure against variant 2 without requiring retpolines. We will want to avoid retpolines on those CPUs for performance reasons. Also, Intel's future CET control-flow-integrity hardware will not work with retpolines, so we'll want to turn retpolines off for security! So software will need to determine at run-time whether retpolines should be used. JITs and handwritten assembly will need to add code to do that. This is going to be a burden on lots of software developers for a very long time.

Security/performance tradeoffs

There is now a significant performance penalty for running untrusted code. If you know for sure there is no malicious code running in your (virtual) machine you can turn off these mitigations and get significant performance wins. This wasn't really true before. (Unikernels reaped some performance benefits but created too many other problems to be generally useful.) Inventorying the entire collection of software running in your VM to verify that it's all trusted may be difficult in practice and reduces defense-in-depth ... but no doubt people will be tempted to do it.

We could see increased interest in source-based distributions like Gentoo. Recompiling your software stack to include just the mitigations that you need could bring performance benefits.

Javascript implications

The isolation boundary between Javascript and a browser content process' native code is not visible to the CPU, which makes hardware mitigations difficult to use for JS — and any other system running code in the same process with different levels of trust. It's hard to say what the immediate implications of this are, but I think it makes "one site per process" policies in browsers more appealing in the long term, at least as an option to deploy in case some future difficult-to-mitigate vulnerability hits. Right now browsers are trying to keep the problem manageable by making it difficult for JS to extract information from the timing channel (by limiting timer resolution and disabling features like SharedArrayBuffer that can be used to implement high-resolution timers), but this unfortunately limits the power of Web applications compared to native applications. For example, as long as it lasts we can't run idiomatic parallel Rust code in browsers via WebAssembly :-(. Also I suspect in the medium term attackers will find other ways to read the timing channel that will be less feasible to disable.

I think it would be a grave mistake to simply give up on mixing code with different trust labels in the same address space. Apart from having to redesign lot of software, that would set a hard lower bound on the cost of transitioning between trust zones. It would be much better if hardware mitigations can be designed to be usable within a single address space.

Other attacks

Perhaps the biggest question is whether we are seeing just the start of a flood of serious attacks based on Spectre-like ideas. I think it's entirely possible, and if so, then dealing with those attacks piecemeal as they surface is going to be incredibly expensive and painful. There is even a possibility that the cost of mitigations will compound as mitigations interfere with one another and fewer and fewer people are capable of understanding what's going on. Therefore I hope and pray that people in positions of power — CPU vendors, big software vendors, etc — work together to come up with comprehensive, preventative fixes that simply rule out these classes of attacks, and don't let themselves be entirely consumed by demands for immediate responses to zero-day vulnerabilities. I applaud the sentiment of RISC-V's statement to this end, self-serving as it is.

Planet MozillaMy trip in Cuba

I spent 3 weeks in Cuba with my family after the Mozilla All-Hands we had in December in Austin, and had the opportunity to meet a local user group (Cuban Tech Group) and do a hackaton with them.

The #cubantech group during the December hackaton on #micropython

Une publication partagée par Tarek Ziadé (@tarek.ziade) le <time datetime="2018-01-17T08:13:06+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">17 Janv. 2018 à 12 :13 PST</time>

Olemis Lang is one of the founders and very active in promoting open source in Cuba. We’ve had some similar experiences in running user groups (I founded the Python french one a decade ago), and were excited about sharing our experience.

Olemis in Havana Lighthouse

Une publication partagée par Tarek Ziadé (@tarek.ziade) le <time datetime="2018-01-17T08:10:04+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">17 Janv. 2018 à 12 :10 PST</time>

One annoying thing in Cuba is the lack of internet access. There’s basically no internet ISP in the country besides the stated-owned company (etesca) that runs wifi hotspots in parks. If you want to access internet, you have to buy 1h or 2h cards for a few euros and sit down in a park as close as possible from the hotspot source. It’s easy to find those hotspots: you will see group of teens with their smartphones. If you recall the craziness around Pokemon Go last summer, with people gathering around Pokemon Arenas, that’s basically it.

Stunning sight from El Mirador near Soroa, Cuba

Une publication partagée par Tarek Ziadé (@tarek.ziade) le <time datetime="2018-01-08T20:40:45+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">8 Janv. 2018 à 12 :40 PST</time>

In most countries I’ve visited, we take internet for granted, and that’s been true for many years. We’ve dumped a big chunk of our memory back into to google/duckduckgo/qwant/xxx because it’s easier to look back for a piece of information rather than remembering it. We all have stories of finding back our own blog posts on the internet when looking for an answer.

Case in point: when we decided to do a hackaton around MicroPython, Olemis and myself took some of time to make sure we had all the required material on local disks. But we spent most of the evening trying to make things work. One person missed a tiny debian package, or we missed one GitHub repo with a specific file, etc.

NodeMCU boards flashed with #Micropython #cubantech

Une publication partagée par Tarek Ziadé (@tarek.ziade) le <time datetime="2018-01-17T08:11:35+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">17 Janv. 2018 à 12 :11 PST</time>

Although I was quite impressed by Olemis’ setup to mitigate the lack of internet. A few raspberry pis here and there, a SAN, a local mirror of stack overflow, of ubuntu packages, a local git server with clones etc, and a proxy cache to visit a few websites.

We had a lot of fun (the “YEAAAAAH” when the Micropython board finally lits the LED is always cool), but everyone was frustrated by the lack of connection. Even with my crappy DSL back at home in France, I felt like that rich spoiled kid with all the toys, compared to the other kids.

Olemis introduces Micropython #cubantech

Une publication partagée par Tarek Ziadé (@tarek.ziade) le <time datetime="2018-01-17T08:12:09+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">17 Janv. 2018 à 12 :12 PST</time>

With the current state of internet in Cuba (I’ve heard it should improve), how can we expect Cuban devs to participate to open source projects as much as they would like ?

A patch for Firefox ? forget it, look at the size of the mercurial repo, it would take them weeks to get it and days to update it (and $$$). A PR in that GitHub repo? that’s possible, but with all the back and forth before it’s pushed, it’s going to costs a few internet cards.

Would you pay 5 euros to fix a bug in a project ?

There’s no way to fix this, they will have to wait for the Cuban government to allow private internet access, or drastically improve the public one, maybe by creating open spaces with better internet access.

But in the interim (=in the next 5 years), I think there’s something we can do to help them. We can send them data.

It’s easy for people with a good connection to fill an USB disk with :

  • a PyPI mirror
  • a Stackoverflow mirror
  • the latest Git or Mercurial clone for project x, y and z
  • etc.

I want to try to do something about this - not only for Cuba, but for any other place with the same issue.

I am working on defining how we could build an online service to send USB sticks of public content on a regular basis, containing the data developers needs to play with OSS projects.

Maybe this could fit Mozilla’s MOSS - https://www.mozilla.org/en-US/moss/ , or maybe something like that already exists. Digging…

Planet MozillaMozilla Files Suit Against FCC to Protect Net Neutrality

Today, Mozilla filed a petition in federal court in Washington, DC against the Federal Communications Commission for its recent decision to overturn the 2015 Open Internet Order.

Why did we do this?

The internet is a global, public resource. It relies on the core principle of net neutrality (that all internet traffic be treated equally) to exist. If that principle is removed — with only some content and services available or with roadblocks inserted by ISPs to throttle or control certain services — the value and impact of that resource can be impaired or destroyed.

Ending net neutrality could end the internet as we know it. That’s why we are committed to fighting the order. In particular, we filed our petition today because we believe the recent FCC decision violates both federal law as well as harms internet users and innovators. In fact, it really only benefits large Internet Service Providers.

What is next?

As we have said many times over the years, we’ll keep fighting for the open internet to ensure everyone has access to the entire internet and do everything in our power to protect net neutrality. In addition to our court challenge, we are also taking steps to ask Congress and the courts to fix the broken policies.

As a process note, the FCC decision made it clear that suits should be filed 10 days after it is published in the Federal Register, which has not yet occurred. However, federal law is more ambiguous. Due to the importance of this issue, even though we believe the filing date should be later, we filed in the event a court determines the appropriate date is today. The FCC or a court may accept this order or require us and others to refile at a later date. In fact, we’re urging them to use the later date. In either instance, we will continue to challenge the order in the courts.

What can you do?

It is imperative that all internet traffic be treated equally, without discrimination against content or type of traffic — that’s the how the internet was built and what has made it one of the greatest inventions of all time.

You can help by calling your elected officials and urge them to support an open internet. Net neutrality is not a partisan or U.S. issue and the decision to remove protections for net neutrality is the result of broken processes, broken politics, and broken policies. We need politicians to decide to protect users and innovation online rather than increase the power of a few large ISPs.

The post Mozilla Files Suit Against FCC to Protect Net Neutrality appeared first on The Mozilla Blog.

Planet MozillaWebRender newsletter #12

Hi there, your 12th WebRender newsletter has arrived. As I mentioned in the previous newsletter, the biggest theme in the last two weeks has been fixing correctness bugs and adding missing features. I keep learning new and scary things about text while Lee digs up more and more features that we need to add support for.
If you are testing on Windows and WebRender makes rendering flicker horribly, worry not Kvark has landed a fix in WebRender that will make it in nightly soon.

Notable WebRender changes

  • Glenn is making progress (1),(2) in the conversion to segmented primitives which lets us move more content to the opaque pass and improves batching.
  • Glenn removed the need to re-build the scene when a dynamic property changes. This saves a lot of CPU time during scrolling and animations.
  • Glenn added support for caching render tasks across frames and display lists, and added support for it in box shadows. This nice because it will let us avoid re-doing some of the same potentially expensive things every frame (like blurs).
  • Morris fixed a drop-shadow rendering issues (#2243 and #2261).
  • Lee fixed/implemented various text rendering features (1) (2).
  • Kvark fixed edge mask decoding in the shaders.
  • Martin fixed/improved the clipping logic (1) (2) (3).
  • Nical added the concept of transactions to the API (allows us to express certain constraints important for correctness and avoid redundant work).
  • Ethan fixed some glitches when box-shadow parameters are close to zero.
  • Glenn switched travis off for mac builds, we are now using taskcluster. Travis is great but it couldn’t sustain the load on Mac. Testing turnaround times are going to be much better now!
  • Kats resurrected support for non-premultiplied images (We need it for canvas).
  • Glenn ported line primitives to the brush infrastructure.
  • Martin implemented a more expressive API for clipping.
  • Ethan fixed a glitch when mixing edges with and without borders.
  • Lee implemented support for glyph transforms for writing modes.
  • Markus fixed a bug in the calculation of corner radius overlap.
  • Ethan fixed a bug in with inset box-shadow offsets.
  • Glenn moved alpha targets from using the A8 format to R8 on all platforms which avoids slow paths on ANGLE and some drivers.

Notable Gecko changes

  • Lee implemented the plumbing for synthetic italic.
  • Nical updated the gecko integration to use the transaction API and take advantage of it.
  • Nical implemented a simple shared memory recycling scheme for blob images and fonts (improves the performance of display list building).
  • Gankro fixed sub-pixel aa for some bullet display items.
  • Andrew fixed a bug causing images to disappear after dragging a tab to a different window.
  • Kats sped up display list building by removing duplicated work for adjacent items with equivalent clip chains.
  • Ethan fixed a text rendering issue.
  • Milan added the “gfx.webrender.all” pref to simplify enabling the most up to date WebRender prefs on all platforms.
  • Sotaro fixed a memory leak and a lot of smaller things (too many for me to link them here).
  • Vincent and Jerry fixed a bug with video on unfocused tabs freezing.

Enabling WebRender in Firefox Nightly

In about:config:
– just set “gfx.webrender.all” to true (no other prefs needed anymore \o/)

Note that WebRender can only be enabled in Firefox Nightly.

Planet MozillaMozilla and Sundance Film Festival Present: VR the People

On Monday January 22, Mozilla is bringing together a panel of the top VR industry insiders in the world to the Sundance Film Festival in Park City, Utah, to explain how VR storytelling is revolutionizing the film and entertainment industry.

“We want the storyteller’s vision to exceed the capacity of existing technology, to push boundaries, because then the technologist is inspired to engineer new mechanisms that enable things initially thought impossible” says Kamal Sinclair, Director of New Frontier Lab Programs at Sundance Institute.  “However, this is not about creating something that appeals to people simply because of its novel technical achievements; rather it is something that has real meaning, and where that meaning can be realized by engineering the technologies to deliver the best experience possible.”

Mozilla selected the Sundance Film Festival as a destination because the Sundance Institute is a nonprofit organization that promotes artists in film, theatre, and new media to create and thrive. This is a perfect fit for Mozilla because both organizations are aligned with keeping creators first, especially within the Mixed Reality (VR & AR) space.

“We want to empower artists to create immersive, impactful storytelling with ease and accessible tools. It’s a part of our mission to ensure the internet is a global public resource, open and accessible to all,” said Sean White, Mozilla Senior Vice President of Emerging Technologies.

The all-star lineup includes Mozilla Senior Vice President of Emerging Technologies Sean White, founder and CEO of Emblematic Group Nonny de la Peña, Reggie Watts, and immersive director Chris Milk, CEO of WITHIN.

The panel will be moderated by Kamal Sinclair, Director of New Frontier Lab Programs at the Sundance Institute.

Attendees will also get to step inside an exclusive preview of an immersive volumtetric demonstration from immersive journalist Nonny de la Peña, as well as a special opportunity to deliver a powerful message about women in technology.

At Mozilla, our vision is an internet that truly puts people first, where individuals can shape their own experience and are empowered, safe and independent.

Our commitment to VR has been in the works for a while. Mozilla, the maker of Firefox, was the first browser to bring WebVR to the web browser in August of 2017. This created a fast lane for developers and artists to create web based VR experiences to browse with Firefox.

Please join Mozilla as we help creators use the web to the maximum of its potential to reach the world.

 

Mozilla and Sundance Film Festival Present: VR the People

Technology is often viewed as a barrier to immersive storytelling. Instead, it can be used to enhance, amplify, and simplify the creative process.

Join Mozilla at the Sundance Film Festival ClaimJumper building 3rd Floor as we explore how technology can be used to push the limits of storytelling

Our stellar lineup of speakers includes:

Mozilla Senior Vice President of Emerging Technologies Sean White,
Founder and CEO of Emblematic Group & Immersive Journalist Nonny de la Peña,
Reggie Watts,
CEO of WITHIN & Immersive Director Chris Milk,
and Moderator and Director, Kamal Sinclair New Frontier Lab Programs at Sundance Institute

Mon, January 22, 2018
Venue: Claim Jumper Court, 3rd Floor
12:00 PM – 1:30 PM MST
Park City, UT 84060

 

About Mozilla

Mozilla is the not-for-profit behind the popular web browser, Firefox. We believe the Internet is a global public resource, open and accessible to all. We work to ensure it stays open by building products, technologies and programs that put people in control of their online lives, and contribute to a healthier Internet.

About Sundance Film Festival

Founded in 1981 by Robert Redford, Sundance Institute is a nonprofit organization that provides and preserves the space for artists in film, theatre, and new media to create and thrive. The Institute’s signature Labs, granting, and mentorship programs, dedicated to developing new work, take place throughout the year in the U.S. and internationally. The Sundance Film Festival and other public programs connect audiences to artists in igniting new ideas, discovering original voices, and building a community dedicated to independent storytelling. Sundance Institute has supported such projects as Boyhood, Swiss Army Man, Manchester By the Sea, Brooklyn, Little Miss Sunshine, Life, Animated, Sonita, 20 Feet From Stardom, Beasts of the Southern Wild, Fruitvale Station, Sin Nombre, The Invisible War, The Square, Dirty Wars, Spring Awakening, A Gentleman’s Guide to Love and Murder and Fun Home. Join Sundance Institute on Facebook, Instagram, Twitter and YouTube.

 

The post Mozilla and Sundance Film Festival Present: VR the People appeared first on The Mozilla Blog.

Planet MozillaHost an Open Internet Activist

The Ford-Mozilla Open Web Fellowship program is seeking host organizations: like-minded groups working on the front lines of internet health

Today, we’re launching the Ford-Mozilla Open Web Fellowship call for host organizations. If your organization is devoted to a healthy internet for all users, we encourage you to apply.

Now entering its fourth year, the Open Web Fellows program is part of a growing movement to build global leadership around public interest technology and open web advocacy. Backed by the Ford Foundation and Mozilla, the program partners emerging technologists (fellows) with civil society organizations (hosts) for a 10-month period of collaboration and creative work to support public awareness around online policy, to defend digital rights, and to champion internet freedom.

In the past four years, the fellowship has partnered with 21 host organizations around the globe, including the American Civil Liberties Union, Amnesty International, Derechos Digitales, European Digital Rights, Freedom of the Press Foundation and many more, across sectors and specialties, united by a shared goal to protect public interaction on the open web.

Says Ford Foundation’s Michael Brennan, who helps manage the fellowship program: “Technology suffuses every aspect of our lives, and the challenges and opportunities it presents for civil society and social justice are greater than ever. It’s very important that civil society organizations understand the technical landscape, and how developing technologies can serve the public interest.”

WHY TO APPLY

Organizations defending an open and accessible web are the backbone of our fellowship program. They socialize fellows to a global assortment of issues, policies, and technologies and inspire them with meaningful work that impacts how the internet grows and supports community globally.

The Fellowship sponsors fellows with a competitive stipend and suite of benefits such that they can comfortably contribute to and learn from their host organizations for their 10 month fellowship tenure. In turn, the host organizations mentor their fellows to tackle challenges to the open web: digital inclusion, online privacy and security, transparent policy, and human rights advocacy. You can read more about the fellows’ interests and skills via the 2015-2017 cohorts’ bios, or check out this video series featuring their profiles and passions.

A CALL FOR HOST ORGANIZATIONS

Effective immediately, any organization is welcome to apply to be a Ford-Mozilla Open Web Fellowship host. We are seeking applications from organizations that are passionate about preserving the open web as a global resource for all. Selected organizations have a reputation for influencing public policies that impact the internet and a strong interest in onboarding talented emerging technologists to the issues they advocate for and defend on a daily basis.

The application deadline is Friday, February 16th, 2018.

Have questions or concerns? Check this guide to applying, read through the program overview, or reach out by email to fellowships@mozillafoundation.org.

Apply today, and spread the word!

The post Host an Open Internet Activist appeared first on Open Policy & Advocacy.

Planet MozillaUsing Hardware Token-based 2FA with the WebAuthn API

To provide higher security for logins, websites are deploying two-factor authentication (2FA), often using a smartphone application or text messages. Those mechanisms make phishing harder but fail to prevent it entirely — users can still be tricked into passing along codes, and SMS messages can be intercepted in various ways.

Firefox 60 will ship with the WebAuthn API enabled by default, providing two-factor authentication built on public-key cryptography immune to phishing as we know it today. Read on for an introduction and learn how to secure millions of users already in possession of FIDO U2F USB tokens.

Creating a new credential

Let’s start with a simple example: this requests a new credential compatible with a standard USB-connected FIDO U2F device; there are many of these compliant tokens sold with names like Yubikey, U2F Zero, and others:

const cose_alg_ECDSA_w_SHA256 = -7;

/* The challenge must be produced by the server */
let challenge = new Uint8Array([21,31,105 /* 29 more random bytes generated by the server */]);
let pubKeyCredParams = [{
  type: "public-key",
  alg: cose_alg_ECDSA_w_SHA256
}];
let rp = {
  name: "Test Website"
};
let user = {
  name: "Firefox User <firefox@example.com>",
  displayName: "Firefox User",
  id: new TextEncoder("utf-8").encode("firefox@example.com")
};

let publicKey = {challenge, pubKeyCredParams, rp, user};
navigator.credentials.create({publicKey})
  .then(decodeCredential);

In the case of USB U2F tokens, this will make all compatible tokens connected to the user’s system wait for user interaction. As soon as the user touches any of the devices, it generates a new credential and the Promise resolves.

The user-defined function decodeCredential() will decode the response to receive a key handle, either a handle to the ECDSA key pair stored on the device or the ECDSA key pair itself, encrypted with a secret, device-specific key. The public key belonging to said pair is sent in the clear.

The key handle, the public key, and a signature must be verified by the backend using the random challenge. As a credential is cryptographically tied to the web site that requested it, this step would fail if the origins don’t match. This prevents reuse of credentials generated for other websites.

The key handle and public key will from now on be associated with the current user. The WebAuthn API mandates no browser UI, which means it’s the sole responsibility of the website to signal to users they should now connect and register a token.

Getting an assertion for an existing credential

The next time the user logs into the website they will be required to prove possession of the second factor that created the credential in the previous section. The backend will retrieve the key handle and send it with a new challenge to the user. As allowCredentials is an array, it allows sending more than one token, if multiple tokens are registered with a single user account.

/* The challenge must be produced by the server */
let challenge = new Uint8Array([42,42,33 /* 29 more random bytes generated by the server */]);
let key = new Uint8Array(/* … retrieve key handle … */);

let allowCredentials = [{
  type: "public-key",
  id: key,
  transports: ["usb"]
}];

let publicKey = {challenge, allowCredentials};

navigator.credentials.get({publicKey})
  .then(decodeAssertion);

Again, all connected USB U2F tokens will wait for user interaction. When the user touches a token it will try to either find the stored key handle with the given ID, or try to decrypt it with the internal secret key. On success, it will return a signature. Otherwise the authentication flow will abort and will need to be retried by the website.

After decoding, the signature and the key handle that were used to sign are sent to the backend. If the public key stored with the key handle is able to verify the given signature over the provided challenge, the assertion is considered valid and the user will be logged in.

First Factor Authentication

Web Authentication also defines the mechanisms to log in without a username and password at all using a secure token - such as the trusted execution environment on your smartphone. In this mode, your token would attest that you not only have possession of it, but also that you, as a person, unlocked the token using a passcode (something you know) and/or a biometric (something you are).

In this world, websites could let you enroll to perform seamless authentication to a web application on your desktop by answering a prompt which appears on your smartphone.

The FIDO U2F tokens deployed today aren’t sophisticated enough to make this happen yet, but the next generation of tokens will be, and web developers will interact with those FIDO 2.0 tokens using Web Authentication.

WebAuthn, coming to a Firefox near you

This was a very short introduction to the world of Web Authentication and it intentionally omits a lot of nitty-gritty details such as CBOR encoding and COSE_Key formats, as well as further parameters that can be passed to the .create() and .get() functions.

We would like to encourage developers to start experimenting with WebAuthn, and allow users to secure their logins with second factors, as the API becomes available. We are not aware of any WebAuthn-U2F polyfill libraries at the time of writing, but hope that these will be available soon. If you have seen something promising, please let us know in the comments.

It is very exciting to bring standardized two-factor authentication to the web; public-key cryptography already protects our data as it travels the Internet via the TLS protocol, and now we can use it to make phishing a lot, lot harder. Give WebAuthn a try in Firefox Nightly!

USB WebAuthn tokens lit up asking for user interaction

A final note about testing

Web Authentication is a powerful feature. As such, it can only be used in Secure Contexts, and if used in a frame, only when all of the frames are from the same origin as the parent document. This means that you are likely to encounter security errors when experimenting with it on some popular testing websites (such as jsfiddle.net).

Planet MozillaMae Jemison as Luke Skywalker and other stories

My husband gave me the Lego women in STEM and Lego Women of NASA sets for Christmas.  He is knows me well!

IMG_5585

Our son was quite fascinated by the women of NASA kit and we assembled it together.    I have an Lego X-Wing fighter from a long time ago aka my childhood.  Our son was playing with it one day, and I noticed that Mae Jemison was sitting in the pilot’s seat instead of Luke Skywalker.

IMG_5783

Me to my son: “Huh, Mae Jemison is flying the X-Wing now” He: “She’s an astronaut Mommy, she can fly anything.”

IMG_5784

A few days later, I noticed she was flying a small Millennium Falcon.  A very talented astronaut indeed!

Last week I spoke at CUSEC (Canadian Undergraduate Software Engineering Conference) in Montreal. I wrote about it here.  After my talk, a couple of women mentioned that they really appreciated that I featured the work of women in my slides.  I said to them that I did this in purpose.  If you work in a field where people always reference the work of people who don’t look like you, you can begin to think you don’t belong. If you look at your company org chart and see the people above you all look the same but not like you, you can wonder if you how difficult it will be for you to advance.   Underrepresented folks in tech do amazing work but often aren’t referenced as experts due to bias. You can change this by highlighting their work and making them more visible.

<figure class="wp-caption alignnone" id="attachment_1889">Screen Shot 2018-01-15 at 3.42.03 PM<figcaption class="wp-caption-text">https://container-solutions.com/what-is-a-distributed-system/</figcaption></figure> <figure class="wp-caption alignnone" id="attachment_1890">Screen Shot 2018-01-15 at 3.49.32 PM<figcaption class="wp-caption-text">Slide on applying the concepts of the code to your development + speaker notes</figcaption></figure>

I’ve been taking a course at work on leadership.  One of topics is being an authentic leader. As part of the that course, we talked about how people cover or hide parts of their identities at work.  For instance, some parents don’t talk the childcare responsibilities they have because they don’t want to seem less dedicated to their work and be offered less challenging assignments.  People who grew up in families with less financial resources might hide that fact from coworkers who grew up in more privileged circumstances.  People who are members of communities that are disproportionately subject to police violence might not want to discuss the latest news with their coworkers who don’t have a reason to fear the police.  People who identify as LGBTQIA might hide their identify for fear of job discrimination or dismissal, because in some jurisdictions this is still (effectively) legal.

The course talks about ways that as a leader you should be try to show more of your authentic self if this is safe for you to do.  Which is easier to do with the people that are your peers, but it more difficult to do for the people who report at a higher level than you in the org chart.  For instance, I’ve a manager in the past who blocked off part of his calendar everyday to pick up his kids from school.  This is a signal to others that this is acceptable behaviour within the group.

Screen Shot 2018-01-15 at 4.31.56 PM

So I tried to be a little more authentic in my talk.

“Outside of work,  I like baking and running long distances. I have an amazing family too! I put these pictures up here to show you that as a developer you can have a life outside of work.  Our industry tends to glamourize long hours at work at the expense of everything else but it doesn’t have to be that way.”

It’s just a very small thing but I wanted to let people know that you all belong here.  You are welcome. And you’ll be amazing. Representation matters.

Planet MozillaNVDA and Firefox 58 – The team is regaining strength

A week before the Firefox 57 “Quantum” release in November, I published an Article detailing some bits to be aware of when using Firefox and the NVDA screen reader together. In Firefox 58, due on January 23, 2018, the reliable team is regaining strength in playing well together and offering you good and fast web accessibility.

After the Firefox 57 release, due to many changes under the hood, NVDA and Firefox temporarily lapsed in performance. Statistics quickly showed that about two thirds of the NVDA user base stayed with us despite of this. So to all of you who stuck with us on this difficult release: Thank you! Many of the others moved to the extended support release of Firefox 52. Thank you to those of you as well, you decided to stick with Firefox! Also, statistics show that barely any of those of you who stuck with 57 decided to turn off multi-process Firefox, but instead used the new technology, and some of you even reported problems to us.

Since then, the accessibility team at Mozilla have worked hard to improve the situation. And I am glad to report that in Firefox 58, most of the bottlenecks and slowdowns could be fixed. We’re now delivering most pages fast enough again so that NVDA doesn’t feel the need to notify you that it is loading a document. In addition, you will start feeling the actual effects of the Quantum technologies, like faster page load times and other more smooth experiences now that the accessibility bottlenecks are out of the way. Feedback from users who have been using the Firefox 58 beta releases has been good, so we feel confident that those of you who upgrade from 57 to 58 upon release, will immediately feel more comfortable with your screen reader and browser combination again. And I am hoping that this encourages many of those who switched to the 52 ESR, to come back to Firefox 58 and give it a try. If you still don’t like it, you can go back to 52 ESR as before, but I sincerely hope you’ll stick with 58 once you notice the improvements.

Moreover, Firefox 58 introduces a new feature that NVDA will take advantage of starting in the 2018.1 release, due in February. Those of you NVDA users on the Next or Master development snapshots, already have that particular improvement. That improvement will speed up the cooperation between NVDA and Firefox even more, causing web content to render in the virtual buffer faster.

What’s coming beyond 58?

Of course, more bug fixes! 🙂 We’re continuing to make improvements which bring us closer to parity with Firefox 56, or even beyond that. On many small and medium size pages, that is already the case now, so Firefox 59 deals with stuff that mostly hits on big pages with lots of hyperlinks or many chunks of differently formatted text.

We’ll also continue to work hard to iron out any problems that may cause JAWS and Firefox to slow down so badly together. While some of the above mentioned improvements also improve the interaction between Firefox and JAWS somewhat, JAWS’s interactions with Firefox are different enough that there is still something causing bad slowness that we don’t see with NVDA at all. And while we’ll do everything to improve the situation on our end, there will also need to come some updates to JAWS from Freedom Scientific, the makers of JAWS. So for JAWS users, the recommendation still stands to remain on 52 ESR, which will receive regular security updates until way into the third quarter of 2018, or keep multi-process tabs turned off, as many JAWS users who remained with current Firefox releases have done. Note, however, that turning off multi-process tabs is something you do at your own risk. This is officially an unsupported configuration, so if anything breaks there, it’s tough luck.

I will have more updates in a separate article once we have them and there is significant progress to report.

In conclusion

As we continue to find bottlenecks, debug hangs, and look into sites reported by users, the situation will return to normal, if it hasn’t for you already. All of the above mentioned improvements we have made in Firefox 58 should impact more assistive technologies than just NVDA, except where noted.

I would like to repeat my thanks to all of you who decided to stick with the release, or even try out the beta and give us feedback on the sites you use day to day. This helped a lot over the past few weeks to make more improvements, and moreover, get agreement from release management to push these out to 58 even when they were initially already in the 59 timeframe. Through that, we’ve been able to give you even more improvements in Firefox 58 than I initially anticipated.

The post NVDA and Firefox 58 – The team is regaining strength appeared first on Marco's Accessibility Blog.

Planet MozillaThis Week in Rust 217

Hello and welcome to another issue of This Week in Rust! Rust is a systems language pursuing the trifecta: safety, concurrency, and speed. This is a weekly summary of its progress and community. Want something mentioned? Tweet us at @ThisWeekInRust or send us a pull request. Want to get involved? We love contributions.

This Week in Rust is openly developed on GitHub. If you find any errors in this week's issue, please submit a PR.

Updates from Rust Community

News & Blog Posts

#Rust2018

Find all #Rust2018 posts at Read Rust.

Crate of the Week

This week's crate is cargo-bloat, a cargo subcommand to find out how much space crates/functions take up in an executable. Thanks to Vikrant for the suggestion!

Submit your suggestions and votes for next week!

Call for Participation

Always wanted to contribute to open-source projects but didn't know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!

Some of these tasks may also have mentors available, visit the task page for more information.

If you are a Rust project owner and are looking for contributors, please submit tasks here.

Updates from Rust Core

158 pull requests were merged in the last week

New Contributors

  • Alexander Regueiro
  • Alexis Hunt
  • Bulat Musin
  • Dan Robertson
  • Fenrir
  • Kagamihime
  • muvlon
  • Neil Shen
  • O01eg
  • ritiek
  • Ryan Cumming

Approved RFCs

Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:

No RFCs were approved this week.

Final Comment Period

Every week the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now. This week's FCPs are:

New RFCs

Upcoming Events

If you are running a Rust event please add it to the calendar to get it mentioned here. Email the Rust Community Team for access.

Rust Jobs

No jobs listed for this week.

Quote of the Week

Anything that will make wasm nicer will be awesome, but honestly, I’m thrilled with what we’ve got. It feels absolutely insane that I can just compile this language that’s basically the opposite of JavaScript and it’s running in the browser.

Tomas Sedovic in a #Rust2018 post.

Thanks to ErichDonGubler and CAD97 for the suggestion!

Submit your quotes for next week!

This Week in Rust is edited by: nasa42 and llogiq.

Planet MozillaFirefox 60 Product Integrity Requests Report

Late last year I was putting out weekly reports on the number of requests Mozilla’s Product Integrity group was receiving and how well we were tracking toward our self-imposed service-level agreement (respond to 90% within 48 hours).

The initial system we set up was only ever intended to be minimally viable and has not scaled well, although that’s probably to be expected. There’s been quite a lot of growing pains so I’ve been tasked with taking it to the next level.

What does that have to do with the weekly reports?

Going forward I have decided to stop giving reports on a weekly basis and instead try giving milestone-based reports. Unfortunately the data in the current system is not easily tracked against milestones and so the data presented in these reports is not 100% accurate.

In general I am tracking two categories of requests for each milestone, based very loosely on the dates:

After Deadline” are requests filed after the deadline for the current milestone but before the “PI request deadline” email goes out for the next milestone.

Within Deadline” are requests filed in the week long window between the “PI request deadline” email going out and the deadline for the current milestone.

Total” is quite simply an aggregate of the two.

There will be some inconsistency in this data with regards to the milestone as not every request filed within a milestone window is not necessarily tied to that exact milestone. However this is the closest approximation we can make with the data given at this time; a failing that I hope to address in the next iteration of the PI requests system.

Firefox 58, 59, 60

The deadline to submit PI requests for Firefox 60 passed on January 7, 2018.

By and large we’re getting most of our requests after the deadline. It is possible that some of the “after deadline” number includes requests for the next milestone, particularly if requested toward the end of the current milestone. Unfortunately there is no concrete way for me to know this based on the information we currently record. Regardless I would like to see a trend over time of getting the majority of our requests prior to the deadline. Time will tell.

In terms of our SLA performance we’re tracking better with requests made after the deadline than those filed within the week-long deadline window. Granted the lower volume of requests within the deadline window could be an impact. Currently we’re tracking within 10% of our SLA which I’d like to see improved but is actually better than I thought we’d be doing at this point, considering the number of requests we receive and the number of resources we have available.

That’s all for today. I’ll be back toward the end of the Firefox 60 cycle to report on how that milestone shook out and to look forward to Firefox 61.

Have questions or feedback about this report or the PI Requests system in general? Please get in touch.

Planet WebKitSpeedometer 2.0: A Benchmark for Modern Web App Responsiveness

In 2014, the WebKit team at Apple released Speedometer 1.0, a benchmark for web app responsiveness. It simulates user interactions in web applications, using TodoMVC to orchestrate adding, completing, and removing todo items. Speedometer repeats these actions using DOM APIs that were extensively used in real-world applications. The performance of these kinds of operations depends on the speed of the JavaScript engine, DOM APIs, layout, CSS style resolution and other parts of the browser engine.

Browser engineers have been optimizing their engines using Speedometer as a proxy for real-world use of popular frameworks for a number of years. Originally, Speedometer included implementations of todo apps in six popular JavaScript frameworks and libraries in heavy use: Ember, Backbone, AngularJS, jQuery, Flight, and an early version of React. It also included vanilla JavaScript.

The web developer ecosystem has evolved significantly since Speedometer 1.0 was first released, as have the trends in what libraries, frameworks, and programming paradigms are used. Developers now commonly use transpilers, module bundlers, and recently-introduced frameworks when creating new sites. This is why, for the last year, engineers from WebKit and Chromium have been collaborating on a new version of Speedometer that better reflects the frameworks, tools, and patterns in wide use today.

Today, we are pleased to announce the Speedometer 2.0 benchmark. We hope this new version of Speedometer helps browser vendors optimize their browser engines for the modern Web.

Support for modern JavaScript frameworks and libraries

Over the last three years, a growing number of real-world sites have been written using React — a JavaScript library for authoring user interfaces. Derivatives such as Preact and Inferno have also gained popularity. Speedometer 2.0 includes web apps implemented using these libraries. It also includes an entry using React and Redux — a popular state management library.

Webpack and Rollup are popular JavaScript module bundlers frequently used with these libraries and Speedometer 2.0 includes output generated by these tools.

Ember.js, which featured in the original Speedometer, now has a dedicated tool to create new projects, and provides a more streamlined deployment process for authors. In addition, there were large changes to the core Ember framework over the years. To incorporate these changes in Ember.js itself and the way developers use Ember.js today, Speedometer 2.0 includes an implementation using the latest Ember, built using Ember CLI.

Another framework we observed gaining traction is Vue.js — a progressive solution aimed at being incrementally adoptable. Similar to Ember, Vue.js has prescriptive tooling for getting started and Speedometer 2.0 includes a Vue.js implementation built using the Vue CLI.

It’s of course true that not all real-world sites are being built using these solutions. Many are still deployed using libraries that were popular when Speedometer 1.0 was authored, which is one reason Speedometer 2.0 also includes updates to implementations written in AngularJS, Backbone.js, and Flight.

ES2015 JavaScript and Babel support

Speedometer 1.0 included a version of the todo app implemented with vanilla JavaScript — i.e. without using any libraries or frameworks. At the time, web developers primarily wrote their applications in the version of JavaScript known as ES5. Today, modern browsers have excellent support of ES2015 (also known as ES6), a more evolved version of JavaScript. Speedometer 2.0 now includes a todo app implemented using ES2015 features like classes, const, let, arrow functions, and template literals.

Although measuring vanilla JavaScript performance has high value, a growing number of developers today also use transpilers like Babel to transpile the latest versions of JavaScript code back to a version supporting all browsers they care about. To reflect this workflow, Speedometer 2.0 includes an ES2015 implementation that uses ES Modules and has ES5 output generated by Babel. In the future, as browsers gain full support for native ES Modules, we hope to evolve the benchmark to also track an implementation that isn’t bundled or translated.

TypeScript support

TypeScript is a typed superset of JavaScript that has been gaining traction in the web developer community. It offers types as a first-class syntax, generally fast compilation, and rich tooling for type-aware auto-completion and error highlighting during iteration.

Today, one of the largest users of TypeScript is Angular. To enable browsers to measure the kinds of output a TypeScript app might generate, Speedometer 2.0 includes an Angular implementation written in TypeScript, transpiled to ES5. We’re hopeful that browsers optimizing for this implementation will be able to offer the same wins as more frameworks introduce TypeScript support.

Future-facing: functional programming

The front-end developer community has been shifting in the direction of borrowing more patterns from functional programming. This has been demonstrated with the growth of interest in technologies like Elm and PureScript, both of which transpile down to JavaScript. To enable browsers to optimize for these patterns, Speedometer 2.0 includes implementations for both of these technologies.

Updates in score calculation

Speedometer 1.0 calculated a final score for Web App Responsiveness using the arithmetic mean of the run time needed to add, mark completed, and remove 100 todo items in each implementation in order to incentivize browser vendors to optimize the slowest framework or library. Unfortunately, this resulted in some implementation of a todo app getting 25× more weight compared to another as we added more libraries and frameworks to Speedometer 2.0. It became particularly problematic when we added back a debug build of Ember — it was more than 4× slower than the Ember production build. However, only a small fraction of websites deployed with Ember use debug builds.

In Speedometer 2.0, we’ve changed the score to be computed as the geometric mean against different implementations of the todo app. The final score is computed as the arithmetic mean of the geometric means computed for each iteration of the benchmark.

Future

Speedometer 2.0 has been an exciting collaboration between browser vendors. We would like to build on this collaboration in future iterations of the benchmark by working more closely with framework authors and the developer community to identify broadly-used patterns, frameworks, and tools for which browser engines could be optimized.

Planet MozillaThank you CUSEC!

Last week, I spoke at CUSEC (Canadian Undergraduate Software Engineering Conference) in Montreal.   I really enjoy speaking with students and learning what they are working on.  They are the future of our industry!  I was so impressed by the level of organization and the kindness and thoughtfulness of the CUSEC organizing committee who were all students from various universities across Canada. I hope that you all are enjoying some much needed rest after your tremendous work in the months approaching the conference and last week.

<figure class="wp-caption alignnone" id="attachment_1885">IMG_5768<figcaption class="wp-caption-text">Gracey Hlywa Maytan and Kenny Hong opening CUSEC 2018 in Montreal</figcaption></figure>

Some of of their thoughtful gestures included  handwritten thank you notes to speakers.  They had organized speakers lunches and dinners. They arranged times for AV checks hours before the your talk.   They gave speakers a gift bag of things that might be useful during the conference – lip balm, kleenex, hand sanitizer, snacks, a tuque, a t-shirt. I felt so welcome and will be recommending this conference to others. Neither the organizing committee nor the speakers were all pale and/or male. I think a lot of organizers of other conferences could learn a lot from these students because a lot of industry conferences fail at this metric.   I really enjoyed the other speaker’s talks as well and wish I could have stayed for the rest of the conference.  I’ll have to watch them online as I had to leave Thursday night because I had an appointment I had to attend in Ottawa on Friday.

My talk was about one of my favourite things to do, replacing parts of running distributed systems.  Thank you to Sébastien Roy for inviting me to speak and to Anushka Paliwal for introducing me before my talk.

I really enjoyed preparing and delivering this talk.  (Sidebar: I took the picture on the title slide while snowshoeing in Gatineau Parc near Wakefield, which is spectacular). When I’m preparing for a talk I really spend a lot of time thinking about the background of the audience and what they can get out of a talk. The audience is mostly students, and they are studying different areas of software engineering so I tried to make the talk pretty general. Understanding existing code bases, how to read them and how they evolve is a useful skill to share.  I had a lot of positive feedback from attendees about the talk – how talking about the release process is great because this is a often an overlooked part of software development.

The talk was 20 minutes in length exactly.  When I was writing the talk, I had about 10 other slides that I  removed.  Perhaps some day I could give this talk in an expanded format. There is certainly a lot of material to cover in this subject area.

After my talk, I talked to a lot of students and gave away a lot of fox stickers. Here are some of the questions that came up that I thought I could answer here as well

Screen Shot 2018-01-15 at 10.26.16 AM

  1. How many lines of code changed during the Firefox Quantum release? See https://blog.mozilla.org/firefox/the-new-firefox-by-the-numbers/
  2. What is the process to I apply for an internship at Mozilla?  The internship process at Mozilla starts in the fall for summer internships.  The interview process usually starts in October/November and continues until each position is filled.  There are still internship positions listed on the web page, so if you are interested, now is the time to apply.
  3. What do you look for when interviewing intern candidates? I interviewed many candidates for our Python internships this past fall. At Mozilla, we have a online technical screening test for applicants.  If you proceed past that, you will have some more interviews that dig deeper into technical questions and your approach to problem solving as well as your communication skills.  Personally, I like to focus on past work as a predictor of future performance. So we would have conversations about past work and school projects you worked on.  I wrote a blog post a couple of years ago about preparing for interviews, with the caveat that it is more geared toward hiring for fulltime positions. Also, I have a very strong no asshole rule when interviewing candidates and have some questions to weed that out.
  4. Are you considered a team player if you go home on time?  You should do your work during the work day and then go home and do non-work stuff.  Otherwise you could burn out or get RSI.  Being a team player is getting your work done and helping your coworkers succeed, not being at work all the time so that the rest of your life suffers.
  5. What Beyonce song are the lyrics from?  On slide 24 I include some Beyonce lyrics about saying goodbye as a way to illustrate the how you may feel about about deleting code you’ve worked on for many years.  The lyrics are from “It’s Hard to Say Goodbye” from the Dreamgirls soundtrack.
  6. How do deal with people leaving rude comments on your PRs in open source?  Well, we’ll have to meet for coffee for that because that’s a long conversation.

IMG_5792

If you are ever invited to speak at CUSEC, I highly recommend saying yes.  The organization is impeccable, the organizers were kind and thoughtful, and the talks were interesting and came from diverse perspectives.  Huge kudos to everyone involved.

Planet MozillaSecure Contexts Everywhere

Since Let’s Encrypt launched, secure contexts have become much more mature. We have witnessed the successful restriction of existing, as well as new features to secure contexts. The W3C TAG is about to drastically raise the bar to ship features on insecure contexts. All the building blocks are now in place to quicken the adoption of HTTPS and secure contexts, and follow through on our intent to deprecate non-secure HTTP.

Requiring secure contexts for all new features

Effective immediately, all new features that are web-exposed are to be restricted to secure contexts. Web-exposed means that the feature is observable from a web page or server, whether through JavaScript, CSS, HTTP, media formats, etc. A feature can be anything from an extension of an existing IDL-defined object, a new CSS property, a new HTTP response header, to bigger features such as WebVR. In contrast, a new CSS color keyword would likely not be restricted to secure contexts.

Requiring secure contexts in standards development

Everyone involved in standards development is strongly encouraged to advocate requiring secure contexts for all new features on behalf of Mozilla. Any resulting complication should be raised directly against the Secure Contexts specification.

Exceptions to requiring secure contexts

There is room for exceptions, provided justification is given to the dev.platform mailing list. This can either be inside the “Intent to Implement/Ship” email or a separate dedicated thread. It is up to Mozilla’s Distinguished Engineers to judge the outcome of that thread and ensure the dev.platform mailing list is notified. Expect to be granted an exception if:

  • other browsers already ship the feature insecurely
  • it can be demonstrated that requiring secure contexts results in undue implementation complexity.

Secure contexts and legacy features

Features that have already shipped in insecure contexts, but are deemed more problematic than others from a security, privacy, or UX perspective, will be considered on a case-by-case basis. Making those features available exclusively to secure contexts should follow the guidelines for removing features as appropriate.

Developer tools and support

To determine whether features are available developers can rely on feature detection. E.g., by using the @supports at-rule in CSS. This is recommend over the self.isSecureContext API as it is a more widely applicable pattern.

Mozilla will provide developer tools to ease the transition to secure contexts and enable testing without an HTTPS server.

The post Secure Contexts Everywhere appeared first on Mozilla Security Blog.

Planet MozillaInspect curl’s TLS traffic

Since a long time back, the venerable network analyzer tool Wireshark (screenshot above) has provided a way to decrypt and inspect TLS traffic when sent and received by Firefox and Chrome.

You do this by making the browser tell Wireshark the SSL secrets:

  1. set the environment variable named SSLKEYLOGFILE to a file name of your choice before you start the browser
  2. Setting the same file name path in the Master-secret field in Wireshark. Go to Preferences->Protocols->SSL and edit the path as shown in the screenshot below.

Having done this simple operation, you can now inspect your browser’s HTTPS traffic in Wireshark. Just super handy and awesome.

Just remember that if you record TLS traffic and want to save it for analyzing later, you need to also save the file with the secrets so that you can decrypt that traffic capture at a later time as well.

curl

Adding curl to the mix. curl can be built using a dozen different TLS libraries and not just a single one as the browsers do. It complicates matters a bit.

In the NSS library for example, which is the TLS library curl is typically built with on Redhat and Centos, handles the SSLKEYLOGFILE magic all by itself so by extension you have been able to do this trick with curl for a long time – as long as you use curl built with NSS. A pretty good argument to use that build really.

Since curl version 7.57.0 the SSLKEYLOGFILE feature can also be enabled when built with GnuTLS, BoringSSL or OpenSSL. In the latter two libs, the feature is powered by new APIs in those libraries and in GnuTLS the library’s own logic similar to how NSS does it. Since OpenSSL is the by far most popular TLS backend for curl, this feature is now brought to users more widely.

In curl 7.58.0 (due to ship on Janurary 24, 2018), this feature is built by default also for curl with OpenSSL and in 7.57.0 you need to define ENABLE_SSLKEYLOGFILE to enable it for OpenSSL and BoringSSL.

And what’s even cooler? This feature is at the same time also brought to every single application out there that is built against this or later versions of libcurl. In one single blow. now suddenly a whole world opens to make it easier for you to debug, diagnose and analyze your applications’ TLS traffic when powered by libcurl!

Like the description above for browsers, you

  1. set the environment variable SSLKEYLOGFILE to a file name to store the secrets in
  2. tell Wireshark to use that same file to find the TLS secrets (Preferences->Protocols->SSL), as the screenshot showed above
  3. run the libcurl-using application (such as curl) and Wireshark will be able to inspect TLS-based protocols just fine!

trace options

Of course, as a light weight alternative: you may opt to use the –trace or –trace-ascii options with the curl tool and be fully satisfied with that. Using those command line options, curl will log everything sent and received in the protocol layer without the TLS applied. With HTTPS you’ll see all the HTTP traffic for example.

Credits

Most of the curl work to enable this feature was done by Peter Wu and Ray Satiro.

Planet MozillaRHL'18 in Saint-Cergue, Switzerland

RHL'18 was held at the centre du Vallon à St-Cergue, the building in the very center of this photo, at the bottom of the piste:

People from various free software communities in the region attended for a series of presentations, demonstrations, socializing and ski. This event is a lot of fun and I would highly recommend that people look out for the next edition. (subscribe to rhl-annonces on lists.swisslinux.org for a reminder email)

Ham radio demonstration

I previously wrote about building a simple antenna for shortwave (HF) reception with software defined radio. That article includes links to purchase all the necessary parts from various sources. Everything described in that article, together with some USB sticks running Debian Hams Live (bootable ham radio operating system), some rolls of string and my FT-60 transceiver, fits comfortably into an OSCAL tote bag like this:

It is really easy to take this kit to an event anywhere, set it up in 10 minutes and begin exploring the radio spectrum. Whether it is a technical event or a village fair, radio awakens curiosity in people of all ages and provides a starting point for many other discussions about technological freedom, distributing stickers and inviting people to future events. My previous blog contains photos of what is in the bag and a video demo.

Open Agriculture Food Computer discussion

We had a discussion about progress building an Open Agriculture (OpenAg) food computer in Switzerland. The next meeting in Zurich will be held on 30 January 2018, please subscribe to the forum topic to receive further details.

Preparing for Google Summer of Code 2018

In between eating fondue and skiing, I found time to resurrect some of my previous project ideas for Google Summer of Code. Most of them are not specific to Debian, several of them need co-mentors, please contact me if you are interested.

Planet MozillaThe Big SUMO Report: 2017

Hey there, SUMO Nation!
sumo_logo

We are happy to see you reading these words at the beginning of 2018 – and curious to see what it brings… But before we dive deep into the intriguing future, let’s take a look back at 2017. It has been a year full of changes big and small, but we were all in it together and we could not have reached the end of the year as a team without you. There is quite a lot to talk about, so let’s get rolling with the numbers and details!

SUMO 2017 – the major facts

  • We had a fair share of “platform adventures”, switching the whole site over to a completely new front- and back-end experience for admins, contributors, and users – and then coming back to our previous (and current) setup – both for good reasons. It has been a rocky road, full of big challenges and unexpected plot twists. Thank you for staying with us through all the good winds and bad turbulence.
  • More on the platform side – Giorgos and Ben have been quietly moving things around to ensure a stable future for the site – the works are finishing only now, but the prep work happened all in 2017.
  • We proudly and loudly took part in the biggest event of 2017 for everyone at Mozilla – the release of Firefox Quantum! Remember – even if you never contributed a single line of code to Firefox, your contributions and participation in what Mozilla does is crucial to the future of an open web.
  • The “big one” aside, we also welcomed Firefox Rocket and Firefox for Fire TV to our family of “all the software for your burning browsing needs” ;-).
  • We met in San Francisco and Austin to talk about all things Mozilla and SUMO – and celebrate our diversity and achievements.
  • We also met in Berlin to talk about our community plans for 2018.
  • We were invited to join the kicking off of the Non-Coding Volunteers project and are really looking forward to more of it in 2018.
  • This humble blog took a dip in general activity and traffic (15 new posts vs 68 in 2016 and over 7,200 page views vs 9,200 in 2016). Still, some of the most read posts in its history appeared last year – and were written by you. We’re talking about community event reports, All Hands tales, and stories about bugs – quality over quantity!

SUMO 2017 in numbers – the highlights

Just like the year before, our activity and its results could be analysed from many perspectives, with dozens of data sources. Putting everything together, especially given our platform changes in 2018, is quite impossible – and the numbers have been affected by interruptions in tracking, as expected.

Capturing the potential, reach, and power of SUMO as a source of knowledge and help and as an example of community collaboration is something we definitely want to improve upon in 2018.

No amount of simple digits can tell the complete story of our joint voyage, but let’s try with the ones below.

General site stats
  • Number of total page views: 855,406,690 vs 806,225,837 (2016) – over 49 million more!
  • Number of users with at least one recorded session (= visit to the site): 296,322,215 vs 259,584,893 (2016) – over 36 million more!
  • Number of sessions (= periods of active user engagement on the site): 513,443,121 vs 446,537,566 (2016) – over 66 million more!
  • Percentage of people returning to the site: 44.1% vs 45% (2016)
  • Average time spent on site per session: 01:26 vs 01:39 (2016)
  • Average number of pages visited per session: 1.67 vs 1.81 (2016)
  • Percentage of single page visits: 39.7% vs 25% (2016)
  • 89.65% of users saw the website on desktop or laptop computers (90.9% in 2016). The remaining 10.35% visited us from a mobile device (9.1% in 2016).
  • Percentage of people who visited SUMO using Firefox: 85% vs 84% (2016)
  • Percentage of people who visited SUMO using Chrome: 5.8% / Safari: 5.8% / Internet Explorer: 1.6% vs Chrome: 7% / Safari: 4% / Internet Explorer: 3% (2016)
  • Number of people who contributed for the first time: 1,174 vs 1,153 (2016)
Language & Knowledge Base stats
  • Top visitor 10 languages (by percentage of sessions):
    1. English (no change)
    2. German (no change)
    3. Spanish (from 4th in 2016)
    4. French (from 3rd)
    5. Russian (from 6th)
    6. Japanese (from 5th)
    7. Portuguese (Brazil) (no change)
    8. Chinese (simplified) (from 10th)
    9. Polish (from 8th)
    10. Italian (from 9th)
  • Top 10 visitor countries (by percentage of sessions):
    1. United States
    2. Germany
    3. Japan
    4. France
    5. Brazil
    6. Russia
    7. India
    8. United Kingdom
    9. Poland
    10. Indonesia

(No change for the top 10 visitor countries from last year)

  • Number of all submitted article revisions in 2017 (important note: the numbers include only data from Kitsune – we do not have data from Lithium):
    • for English only: 1,588 (over 4 a day on average)
    • for the top 20 locales: 12,087 (over 33 a day on average)
    • for all locales: 16,707 (over 45 a day on average)
  • Here’s an alphabetical list of the most active contributors to the Knowledge Base across all 12 months of 2017. If your username is below, you have gone above and beyond in making Mozilla and SUMO useful to users around the world – we can’t thank you enough for your participation!

albertdecastro
AliceWyman
Anticisco
Artist
avelper
Banban
Bogdancev
buluma_michael
bychek.ru
caitmuenster
cecilebertin
Channy
cloney1301
cynthiapereira
Daniel2099
dibery
dskmori
dwchiang
f1m15
FabioBeneditto
Goofy_BZ
Goudron
graba
haard37
harrierr
Hezorg
irahal
J2m06
JCPlus
jhonymoz
JimSp472
Joergen
Jotunheim
jsavage
kaneren
kazem82
kelimutu
KimLudvigsen
kkemenczy
Klofutar
kusavica
mansil
manxmensch
marcel11
marcelo.ghelman
marsf
maxcoder
merikes
meskobalazs
michro
Mikk
milupo
MozGianluc
Mozinet
naddo
OmTi
ouesten
petercpg
philipp
pierreneter
Pokno_Royal
pollti
rmcguigan
rtanglao
seulgi
slxxx
SonicSusi
soucet
T.Ukegawa
thiagopolicena
Tonnes
TyDraniu
underpass
Unghost
upwinxp
–V–
vesper
wxie2016
zerdo
zilmar
zint

(For more general data please check this document)

Support Forum

Adelaa
aeons0000
Airmail
alan_r
alex_mayorga
AliceWyman
Ansamb
arejfour
Avengement
Beerbaron23
BelFox
brookej
bygabyga
CarolSaile
cezart44
Chaosrider
christ1
cor-el
databaseben
ddtudoran
dehe25
DRulz
Enigma-2U
Estevaomd
FabioBeneditto
feer56
ffw62
FireFoxFan1
Fizix
fredmcd-hotmail
gnospen
Happy112
herbert-wagner
Hv60T
iampowerslave
jagan605
jam70
James
jayelbe
jbacinti
JeremyRossJunior
John99
johnoo
Jonathan1
jorgk
jscher2000
kbrosnan
Lanselot
LasseL
linuxmodder
MagicFab
MaikelSantos
MajorMike
MarjaE
MattAuSupport
mattcamp
maxcoder
meskobalazs
Mikk
Mkll
moses
Noah_SUMO
oeekker
OmTi
ouesten
pcp04
philipp
phubert28
Pkshadow
poljos-moz
RevertSearch
Richardinengland
rmcguigan
rtanglao
safemanusa
Scribe_uk
Seburo
sfhowes
Slowpoke47
sludge7051-x
smorele
stellawrites
SueWitch
the-edmeister
Toad-Hall
Tolkien
tomatoshadow2
Tonnes
TyDraniu
Tylerdowner
user1121639
vesper
waynec444
wsmwk
Zachary_
Zenos
zilmar

Social Support

  • The Social Support kept going strong thanks to 20 core contributors and around 40 casual helpers.
  • We are still using Reply as the main tool and have added 4 new languages this year – we now support in Czech, Turkish, French, German, English, Portuguese, Spanish, Dutch, Bengali, and Nepali.
  • We are still looking for more help in German, French, Spanish, Italian and Japanese – so there’s a lot of opportunity to have your name listed below ;-)
  • Quantum was the highest participation we had in social all year, thank you everyone!
  • The Army of Awesome is still being sunsetted… We are just waiting for a few more pieces to fall into place.
  • Whew, now it’s finally time to meet the social superheroes! Each and every one of the great folks listed below is out there, making sure that no cry for help goes unanswered. Take a look here and also below:

3DIndian
74274265j
Aashish0506
Abbackar Diomande
adamclouder
Aditya113
Alex Mayorga
AlexOrdun
amitroy2779
Andrei Petcu
andresdeapia
Andrew Truong _feer56_
arunsathiya
ascasoft
asynchronous_javascriptor
awasthi_nayan
beffsstories
Benny Chandra
bilgicib98
canusupportme
Cynthia Pereira
Daniela Albarrán
danilovbarbosa
DGuarch
Dynisha Faust
emAditya_
Emailemail01
emeka_nwaoma
ethicaladitya
Fabio Beneditto
FxHelp
Gebruiker00
GuardianMajor
HarshSh05339652
herbertmoreno18
iamjayakumars
iamsooryanb
IvnRuvalcaba7
jdc20181
Jhonatas Rodrigues
joaoesteves10RS
jon20120
Josh Riley
João Paulo Polles
JPaulLucien
KL_Kunai
KonstantinMetz
Kumaresan C S
L3i1_
MajorMike
Michal Stanke
MikeMozmicky
MukeshPathak
Nayan_Awasthi
Nildëala Dorffer
Noah Y
otherCelo
ouesten
packetguardian
pavangudiwda
PavanKiran27
ptirth421
RafsanHashemi
Robert Sayles
Ronuad
ronuad
rtsayles
ryanolsonx
shaun6342
smorele
smorele
Sr3lliot
st3109ar
Stefan Costen
sumaiya_tasnim9
Swarnava Sengupta
syam
Valentin Angeli
ViniciusLifem11
Wim B
wizofe
Wolter_Miller
Zach Kingston
Zilmar de Souza Junior

SUMO 2017 in your words

We’ve shared our notes and insights from the last year with you… Now it’s time to hear from you! Please use the comment section to share your best and not so good SUMO (or Mozilla) moments from the last calendar year.

We look forward to hearing from you – both now and later in 2018!

 

Planet MozillaNews flash: encrypted.google.com is not special in any way

Once upon a time, Google dared to experiment with HTTPS encryption for their search instead of allowing all search data to go unencrypted through the wire. For this experiment, they created a new subdomain: encrypted.google.com was the address where your could get some extra privacy. What some people apparently didn’t notice: the experiment was successful, and Google rolled out HTTPS encryption to all of their domains. I don’t know why encrypted.google.com is still around, but there doesn’t seem to be anything special about it any more. Which doesn’t stop some people from imagining that there is.

Myth #1: Your data is extra private, thanks to the extra encryption

The “encrypted” in “encrypted.google.com” refers to HTTPS encryption. You get it on any Google domain, and pretty much every other search engine switched to HTTPS-only as well. There is no indication that Google experiments with any other kind of encryption on this domain.

Myth #2: encrypted.google.com doesn’t send your search query to websites you click on

That myth seems to be based on this answer by a Google employee noting differences in referrer handling. And maybe in 2013 it was even accurate. But these days anybody running a website will confirm that they don’t see your search query, no matter what Google domain you are on. Until recently the reason was Google’s Instant Search feature, the search terms simply weren’t part of the address. Now Instant Search is gone, but Google uses Referrer Policy to prevent giving away your search terms to other websites.

Actual differences?

I did notice a few actual differences on that domain. For example, encrypted.google.com won’t redirect me to google.de because of my geographical location — but it will still show me results that are specific to Germany, same as google.de. It also doesn’t provide links to some other Google apps such as Maps. Not really a reason to prefer it over regular Google domains, but maybe somebody knows about more substantial differences and can list those in the comments.

Planet MozillaUser Style for bugzilla.mozilla.org

Yesterday, I was talking with Kohei Yoshino (the person behind the Bugzilla Quantum effort that recently landed significant UX improvements to the header strip) about some visual issues I have on bugzilla.mozilla.org which basically boil down to our default view being a bit too noisy for my taste and not emphasizing enough on the key elements I want to glance at immediately when I visit a bug (bug Status, description, comments).

Given that I spend a significant amount of time on Bugzilla and that I also spend some time on Github issues, I decided to see if I could improve our default theme on Bugzilla with a user style to make it easier on the eyes and also closer visually to Github, which I think is good when you use both on a daily basis.

After an evening spent on it, I am happy with the result so I decided to share it via UserStyle.org. To install this style on Firefox, you need an extension such as Stylish or Styl-us (I use the former), go to the user style page above and install it. Load a bug (ex: Bug 1406825) and you should see the result. Note that this CSS patches the default theme on bugzilla.mozilla.org, not the old one. You need to have the option "Use modal user interface" set to ON in your bugzilla preferences (that's the default value).

Here are a few Before/After screenshots:

<figure style="margin: 0 1em 1em 0; text-align: center;">Screenshot-2018-1-10 1406825 - Enable Custom elements v1 on Nightly by default.png <figcaption>Overview and header before</figcaption> </figure> <figure style="margin: 0 1em 1em 0; text-align: center;">

Screenshot-2018-1-10 1406825 - Enable Custom elements v1 on Nightly by default(1).png

Overview and header after

</figure> <figure style="margin: 0 1em 1em 0; text-align: center;">Screenshot-2018-1-10 1408044 - Stop shipping about (2).png <figcaption>Comment before</figcaption> </figure> <figure style="margin: 0 1em 1em 0; text-align: center;"> <figure>[object Object] <figcaption> </figcaption> <figcaption>Comment after</figcaption> </figure> </figure>

That's a v1 (a v1.1 actually), it uses sans-serif fonts instead of monospace, styles replies and comments in a more modern way, removes some visual elements and emphasizes on readibility of comments. Cheers

Planet MozillaMicrosoft curls too

On December 19 2017, Microsoft announced that since insider build 17063 of Windows 10, curl is now a default component. I’ve been away from home since then so I haven’t really had time to sit down and write and explain to you all what this means, so while I’m a bit late, here it comes!

I see this as a pretty huge step in curl’s road to conquer the world.

curl was already existing on Windows

Ever since we started shipping curl, it has been possible to build curl for Windows and run it on Windows. It has been working fine on all Windows versions since at least Windows 95. Running curl on Windows is not new to us. Users with a little bit of interest and knowledge have been able to run curl on Windows for almost 20 years already.

Then we had the known debacle with Microsoft introducing a curl alias to PowerShell that has put some obstacles in the way for users of curl.

Default makes a huge difference

Having curl shipped by default by the manufacturer of an operating system of course makes a huge difference. Once this goes out to the general public, all of a sudden several hundred million users will get a curl command line tool install for them without having to do anything. Installing curl yourself on Windows still requires some skill and knowledge and on places like stackoverflow, there are many questions and users showing how it can be problematic.

I expect this to accelerate the curl command line use in the world. I expect this to increase the number of questions on how to do things with curl.

Lots of people mentioned how curl is a “good” new tool to use for malicious downloads of files to windows machines if you manage to run code on someone’s Windows computer. curl is quite a capable thing that you truly do not want to get invoked involuntarily. But sure, any powerful and capable tool can of course be abused.

About the installed curl

This is what it looks when you check out the curl version on this windows build:

(screenshot from Steve Holme)

I don’t think this means that this is necessarily exactly what curl will look like once this reaches the general windows 10 installation, and I also expect Microsoft to update and upgrade curl as we go along.

Some observations from this simple screenshot, and if you work for Microsoft you may feel free to see this as some subtle hints on what you could work on improving in future builds:

  1. They ship 7.55.1, while 7.57.0 was the latest version at the time. That’s just three releases away so I consider that pretty good. Lots of distros and others ship (much) older releases. It’ll be interesting to see how they will keep this up in the future.
  2. Unsurprisingly, they use a build that uses the WinSSL backend for TLS.
  3. They did not build it with IDN support.
  4. They’ve explicitly disabled support a whole range of protocols that curl supports natively by default (gopher, smb, rtsp etc), but they still have a few rare protocols enabled (like dict).
  5. curl supports LDAP using the windows native API, but that’s not used.
  6. The Release-Date line shows they built curl from unreleased sources (most likely directly from a git clone).
  7. No HTTP/2 support is provided.
  8. There’s no automatic decompression support for gzip or brotli content.
  9. The build doesn’t support metalink and no PSL (public suffix list).

(curl gif from the original Microsoft curl announcement blog post)

Independent

Finally, I’d like to add that like all operating system distributions that ship curl (macOS, Linux distros, the BSDs, AIX, etc) Microsoft builds, packages and ships the curl binary completely independently from the actual curl project.

Sure I’ve been in contact with the good people working on this from their end, but they are working totally independently of us in the curl project. They mostly get our code, build it and ship it.

I of course hope that we will get bug fixes and improvement from their end going forward when they find problems or things to polish.

The future looks as great as ever before!

Planet MozillaGitHub Knows

I was reflecting the other day how useful it would be if GitHub, in addition to the lists it has now like Trending and Explore, could also provide me a better view into which projects a) need help; and more, b) can accept that help when it arrives. Lots of people responded, and I don't think I'm alone in wanting better ways to find things in GitHub.

Lots of GitHub users might not care about this, since you work on what you work on already, and finding even more work to do is the last thing on your mind. For me, my interest stems from the fact that I constantly need to find good projects, bugs, and communities for undergrads wanting to learn how to do open source, since this is what I teach. Doing it well is an unsolved problem, since what works for one set of students automatically disqualifies the next set: you can't repeat your success, since closed bugs (hopefully!) don't re-open.

And because I write about this stuff, I hear from lots of students that I don't teach, students from all over the world who, like my own, are struggling to find a way in, a foothold, a path to get started. It's a hard problem, made harder by the size of the group we're discussing. GitHub's published numbers from 2017 indicate that there are over 500K students using its services, and those are just the ones who have self-identified as such--I'm sure it's much higher.

The usual response I get from people is to use existing queries for labels with some variation of "good first bug". This can work, especially if you get in quickly when a project, or group of projects, does a triage through their issues. For example, this fall I was able to leverage the Hacktoberfest efforts, since many projects took the time to go and label bugs they felt were a good fit for new people (side note: students love this, and I had quite a few get shirts and a sense that they'd become part of the community).

But static labeling of issues doesn't work over time. For example, I could show you thousands of "good first bugs" sitting patiently in projects that have long ago stopped being relevant, developed, or cared about by the developers. It's like finding a "Sale!" sign on goods in an abandoned store, and more of a historical curiosity than a signal to customers. Unless these labels auto-expire, or are mercilessly triaged by the dev team, I don't think they solve the problem.

So what could we do instead? Well, one thing we could do is make better use of the fact that we all now work in a monorepo called github.com. Everyone's favourite distributed version control system has evolved to (hilariously) become the most centralized system we've ever had. As such, GitHub knows about you, your team, and your code and could help us navigate through everything it contains. What I'm going to describe is already starting to happen. For example, if you have any node.js projects on GitHub, you've probably received emails about npm packages being out of date and vulnerable to security issues:

We found a potential security vulnerability in a repository which you have been granted security alert access. Known high severity security vulnerability detected in package-name < X.Y.Z defined in package-lock.json. package-lock.json update suggested: package-name ~> X.Y.Z

Now imagine we take this further. What sorts of things could we do?

  • GitHub knows how likely your project is to accept work from new contributors, based on that fact that it knows whether any given user has commits in your repo. If a project doesn't accept contributions from new people, that would be great to know. Even better, show me which ones routinely take commits from new people.
  • GitHub knows that your project has died, that no one is making new commits, or that the rapidity of updates has slowed. It therefore knows that your "good first bugs" (probably) aren't any more, that they've expired. It would be great if they could be de-priortized in some search I might do.
  • GitHub knows how busy a project's developers are. I might be contributing to a dozen different repos across GitHub, and this might be a particularly busy time for me. I might also be the person who usually interacts with new people in issues. However, right now, I'm pushing branches, doing reviews, etc. and am less likely to be able to help people, even if I usually will. It would be great if there was some signal to help passers-by to know whether there are rooms available if they decide to stay.

This kind of signalling could also help all GitHub users, not just new contributors to a project. For example, I bet GitHub knows when you're approaching burnout. In sport we see coaches and team physios/physicians making data-driven decisions about injury potential for athletes: it's imperative to periodize training and balance volume and recovery so that you don't push beyond what an individual can effectively manage. Why don't we do this with tech? It's easy with programming to "lose yourself" in a problem or piece of code. I've had lots of periods where I'm pushing myself really hard in order to find a solution to some problem. I've also had lots of periods where I'm mentally dead and can't bring myself to write anything. I often don't have good insight into when I'm veering into one or the other of these two extremes.

But GitHub does. If I'm committing code day after day, I'm working day after day. If I'm writing novellas in issues, filing bugs, reviewing code, or even just scrolling my mouse without clicking on this website, I'm working. I don't know what the threshold is for burnout, but I'm sure there are psychologists or other behavioral scientist who do, and could give some guidelines that could be used to offer a signal.

What if, in addition to "critical" security warnings for our code modules, we also got helpful info about the state of our project and practices. GitHub could let people know when it sees them cross some threshold of work, to give them some idea how much they've been doing, to help them realize they need some rest. Same thing for a project overall: when your top few developers are going all out for long stretches, the project as a whole is going to collapse eventually. If everyone is @ mentioning a person in bugs across a bunch of repos, asking for reviews, asking for help, looking for feedback, eventually that person is not going to be able to keep up. What if GitHub showed me a bit of info when I requested a review or @ mentioned you:

"This person is totally swamped right now. Is there someone else that could help?"

Actually, GitHub probably knows that too, since it knows the other devs who commit to the same repos/files on a regular basis. Why not suggest some alternatives to balance the load?

Eventually this could go even further. I can't tell you how many times the solution to a bug I have is sitting quietly over in another repo, unknown to me or the rest of my team. Wouldn't it be nice if GitHub could point me at bugs that are like the one I'm looking at now? Doing this perfectly would be hard, I realize. But imagine if GitHub could signal the presence of other similar bugs, code, or people. GitHub knows.

GitHub knows and now it needs to tell us. None of the above can be done immediately, but we need to get there. We are moving into a period of data, machine learning, artificial intelligence, and automation. In 2018 we need our systems to move beyond hosting our data, and our data needs to start working for us.

Planet MozillaNot every bit of code you write needs to be optimal

It's easy to fall into the trap of obsessing about performance and try to micro-optimize every little detail in the code you're writing. Or reviewing for that matter. Most of the time, this just adds complexity and is a waste of effort.

If a piece of code only runs a few (or even a few hundred) times a second, a few nanoseconds per invocation won't make a significant difference. Chances are the performance wins you'll gain by micro optimizing such code won't show up on a profile.

Given that, what should you do instead? Code is read and edited much more than it is written, so optimize for readability, and maintainability.

If you find yourself wondering whether a piece of code is making your program slow, one of the first things you should do is fire up a profiler, and measure it. Or add telemetry to report how long your function takes in the wild. Then you can stop guessing, and start doing science.

If data shows that your code is slow, by all means optimize it. But if not, you can get more impact out of your time by directing your efforts elsewhere.


Planet WebKitFrédéric Wang: Review of Igalia's Web Platform activities (H2 2017)

Last september, I published a first blog post to let people know a bit more about Igalia’s activities around the Web platform, with a plan to repeat such a review each semester. The present blog post focuses on the activity of the second semester of 2017.

Accessibility

As part of Igalia’s commitment to diversity and inclusion, we continue our effort to standardize and implement accessibility technologies. More specifically, Igalian Joanmarie Diggs continues to serve as chair of the W3C’s ARIA working group and as an editor of Accessible Rich Internet Applications (WAI-ARIA) 1.1, Core Accessibility API Mappings 1.1, Digital Publishing WAI-ARIA Module 1.0, Digital Publishing Accessibility API Mappings 1.0 all of which became W3C Recommandations in December! Work on versions 1.2 of ARIA and the Core AAM will begin in January. Stay tuned for the First Public Working Drafts.

We also contributed patches to fix several issues in the ARIA implementations of WebKit and Gecko and implemented support for the new DPub ARIA roles. We expect to continue this collaboration with Apple and Mozilla next year as well as to resume more active maintenance of Orca, the screen reader used to access graphical desktop environments in GNU/Linux.

Last but not least, progress continues on switching to Web Platform Tests for ARIA and “Accessibility API Mappings” tests. This task is challenging because, unlike other aspects of the Web Platform, testing accessibility mappings cannot be done by solely examining what is rendered by the user agent. Instead, an additional tool, an “Accessible Technology Test Adapter” (ATTA) must be also be run. ATTAs work in a similar fashion to assistive technologies such as screen readers, using the implemented platform accessibility API to query information about elements and reporting what it obtains back to WPT which in turn determines if a test passed or failed. As a result, the tests are currently officially manual while the platform ATTAs continue to be developed and refined. We hope to make sufficient progress during 2018 that ATTA integration into WPT can begin.

CSS

This semester, we were glad to receive Bloomberg’s support again to pursue our activities around CSS. After a long commitment to CSS and a lot of feedback to Editors, several of our members finally joined the Working Group! Incidentally and as mentioned in a previous blog post, during the CSS Working Group face-to-face meeting in Paris we got the opportunity to answer Microsoft’s questions regarding The Story of CSS Grid, from Its Creators (see also the video). You might want to take a look at our own videos for CSS Grid Layout, regarding alignment and placement and easy design.

On the development side, we maintained and fixed bugs in Grid Layout implementation for Blink and WebKit. We also implemented alignment of positioned items in Blink and WebKit. We have several improvements and bug fixes for editing/selection from Bloomberg’s downstream branch that we’ve already upstreamed or plan to upstream. Finally, it’s worth mentioning that the work done on display: contents by our former coding experience student Emilio Cobos was taken over and completed by antiik (for WebKit) and rune (for Blink) and is now enabled by default! We plan to pursue these developments next year and have various ideas. One of them is improving the way grids are stored in memory to allow huge grids (e.g. spreadsheet).

Web Platform Predictability

One of the area where we would like to increase our activity is Web Platform Predictability. This is obviously essential for our users but is also instrumental for a company like Igalia making developments on all the open source Javascript and Web engines, to ensure that our work is implemented consistently across all platforms. This semester, we were able to put more effort on this thanks to financial support from Bloomberg and Google AMP.

We have implemented more frame sandboxing attributes WebKit to improve user safety and make control of sandboxed documents more flexible. We improved the sandboxed navigation browser context flag and implemented the new allow-popup-to-escape-sandbox, allow-top-navigation-without-user-activation, and allow-modals values for the sandbox attribute.

Currently, HTML frame scrolling is not implemented in WebKit/iOS. As a consequence, one has to use the non-standard -webkit-overflow-scrolling: touch property on overflow nodes to emulate scrollable elements. In parallel to the progresses toward using more standard HTML frame scrolling we have also worked on annoying issues related to overflow nodes, including flickering/jittering of “position: fixed” nodes or broken Find UI or a regression causing content to disappear.

Another important task as part of our CSS effort was to address compatibility issues between the different browsers. For example we fixed editing bugs related to HTML List items: WebKit’s Bug 174593/Chromium’s Issue 744936 and WebKit’s Bug 173148/Chromium’s Issue 731621. Inconsistencies in web engines regarding selection with floats have also been detected and we submitted the first patches for WebKit and Blink. Finally, we are currently improving line-breaking behavior in Blink and WebKit, which implies the implementation of new CSS values and properties defined in the last draft of the CSS Text 3 specification.

We expect to continue this effort on Web Platform Predictability next year and we are discussing more ideas e.g. WebPackage or flexbox compatibility issues. For sure, Web Platform Tests are an important aspect to ensure cross-platform inter-operability and we would like to help improving synchronization with the conformance tests of browser repositories. This includes the accessibility tests mentioned above.

MathML

Last November, we launched a fundraising Campaign to implement MathML in Chromium and presented it during Frankfurt Book Fair and TPAC. We have gotten very positive feedback so far with encouragement from people excited about this project. We strongly believe the native MathML implementation in the browsers will bring about a huge impact to STEM education across the globe and all the incumbent industries will benefit from the technology. As pointed out by Rick Byers, the web platform is a commons and we believe that a more collective commitment and contribution are essential for making this world a better place!

While waiting for progress on Chromium’s side, we have provided minimal maintenance for MathML in WebKit:

  • We fixed all the debug ASSERTs reported on Bugzilla.
  • We did follow-up code clean up and refactoring.
  • We imported Web Platform tests in WebKit.
  • We performed review of MathML patches.

Regarding the last point, we would like to thank Minsheng Liu, a new volunteer who has started to contribute patches to WebKit to fix issues with MathML operators. He is willing to continue to work on MathML development in 2018 as well so stay tuned for more improvements!

Javascript

During the second semester of 2017, we worked on the design, standardization and implementation of several JavaScript features thanks to sponsorship from Bloomberg and Mozilla.

One of the new features we focused on recently is BigInt. We are working on an implementation of BigInt in SpiderMonkey, which is currently feature-complete but requires more optimization and cleanup. We wrote corresponding test262 conformance tests, which are mostly complete and upstreamed. Next semester, we intend to finish that work while our coding experience student Caio Lima continues work on a BigInt implementation on JSC, which has already started to land. Google also decided to implement that feature in V8 based on the specification we wrote. The BigInt specification that we wrote reached Stage 3 of TC39 standardization. We plan to keep working on these two BigInt implementations, making specification tweaks as needed, with an aim towards reaching Stage 4 at TC39 for the BigInt proposal in 2018.

Igalia is also proposing class fields and private methods for JavaScript. Similarly to BigInt, we were able to move them to Stage 3 and we are working to move them to stage 4. Our plan is to write test262 tests for private methods and work on an implementation in a JavaScript engine early next year.

Igalia implemented and shipped async iterators and generators in Chrome 63, providing a convenient syntax for exposing and using asynchronous data streams, e.g., HTML streams. Additionally, we shipped a major performance optimization for Promises and async functions in V8.

We implemented and shipped two internationalization features in Chrome, Intl.PluralRules and Intl.NumberFormat.prototype.formatToParts. To push the specifications of internationalization features forwards, we have been editing various other internationalization-related specifications such as Intl.RelativeTimeFormat, Intl.Locale and Intl.ListFormat; we also convened and led the first of what will be a monthly meeting of internationalization experts to propose and refine further API details.

Finally, Igalia has also been formalizing WebAssembly’s JavaScript API specification, which reached the W3C first public working draft stage, and plans to go on to improve testing of that specification as the next step once further editorial issues are fixed.

Miscellaneous

Thanks to sponsorship from Mozilla we have continued our involvement in the Quantum Render project with the goal of using Servo’s WebRender in Firefox.

Support from Metrological has also given us the opportunity to implement more web standards from some Linux ports of WebKit (GTK and WPE, including:

  • WebRTC
  • WebM
  • WebVR
  • Web Crypto
  • Web Driver
  • WebP animations support
  • HTML interactive form validation
  • MSE

Conclusion

Thanks for reading and we look forward to more work on the web platform in 2018. Onwards and upwards!

Planet MozillaMaking tab switching faster in Firefox with tab warming

Making tab operations fast

Since working on the Electrolysis team (and having transitioned to working on various performance initiatives), I’ve been working on making tab operations feel faster in Firefox. For example, I wrote a few months back about a technique we used to make tab closing faster.

Today, I’m writing to talk about how we’re trying to make tab switching feel faster in some cases.

What is “tab warming”?

When you switch a tab in multi-process Firefox, traditionally we’d send a message to the content process to tell it to paint its layers, and then we’d wait for the compositor to tell us that it had received those layers before finally doing the tab switch.

With the exception of some degenerate cases, this mechanism has worked pretty well since we introduced it, but I think we can do slightly better.

“Tab warming” is what we’re calling the process of pre-emptively rendering the layers for a tab, and pre-emptively uploading them to the compositor, when we’re pretty sure you’re likely to switch to that tab.1

Maybe this is my Canadian-ness showing, but I like to think of it almost like coming in from shoveling snow off of the driveway, and somebody inside has already made hot chocolate for you, because they knew you’d probably be cold.

For many cases, I don’t actually think tab warming will be very noticeable; in my experience, we’re able to render and upload the layers2 for most sites quickly enough for the difference to be negligible.

There are certain sites, however, that we can’t render and upload layers for as quickly. These are the sites that I think warming will help with.

Here’s an example of such a site

The above link is using SVGs and CSS to do an animation. Unfortunately, on my MBP, if I have this open in a background tab in Firefox right now, and switch to it, there’s an appreciable delay between clicking that tab and it finally being presented to me.3

With tab warming enabled, when you hover over the tab with your mouse cursor, the rendering of that sophisticated SVG will occur while your finger is still on its way to click on the mouse button to actually choose the tab. Those precious milliseconds are used to do the rendering and uploading, so that when the click event finally comes, the SVG is ready and waiting for you.

Assuming a sufficiently long delay between hover and click, the tab switch should be perceived as instantaneous. If the delay was non-zero but still not long enough, we will have nonetheless shaved that time off in eventually presenting the tab to you.

And in the event that we were wrong, and you weren’t interested in seeing the tab, we eventually throw the uploaded layers away.

On my own machine, this makes a significant difference in the perceived tab switch performance with the above site.

Trying it out in Nightly

Tab warming is currently controlled via this preference:

browser.tabs.remote.warmup.enabled

and is currently off by default while we test it and work out more kinks. If you’re interested in helping us test, flip that preference in Firefox Nightly, and file bugs if you see it introducing strange behaviour.

Hopefully we’ll be able to flip it on by default soon. Stay tuned!


  1. Right now, we simply detect whether you’re hovering a tab with a mouse to predict that you’re likely going to choose that, but there are certain more opportunities to introduce warming based on other user behaviours. 

  2. We can even interrupt JavaScript to do this, thankfully! 

  3. I suspect WebRender will eventually help with the raw rendering performance, but that’s still a little ways off from being shipped to users. 

Planet MozillaAnnouncing ESR60 with policy engine

The Firefox ESR (extended support release) is based on an official release of Firefox desktop for use by organizations including schools, universities, businesses and others who need extended support for mass deployments. Since Firefox 10, ESR has grown in popularity and many large organisations rely on it to let their employees browse the Internet securely.

We want to make customization of Firefox deployments simpler for system administrators and we’re pleased to announce that our next ESR version, Firefox 60, will include a policy engine that increases customization possibilities and integration into existing management systems.

What is the policy engine?

The Policy Engine is a project to build a Firefox desktop configuration and customization feature for enterprise users. The policy engine will work with any tool that wants to set policies and we intend to bring Windows Group Policy support as well. We’ll be initially supporting a limited set of policies but this will be evolving through user feedback.

More details on the policy engine can be found here.

Bug reference

What’s the plan?

In order to accommodate the group policy implementation, we are making Firefox 60 our next ESR version and will be following this plan:

  • May 8th – ESR 60.0 released (we’d love feedback from early adopters at that point and will be sharing a feedback form through the enterprise mailing list)
  • July 3rd – ESR 60.1 released
  • August 28th – End of life for ESR 52 and release of ESR 60.2.0. No further updates will be offered for ESR52 and an update to ESR 60.2.0 will be provided through the application update service

Also please keep in mind that Firefox 57, released last month, supports only add-ons built with the WebExtensions API. This means that Firefox 52 ESR is the last release that will support legacy add-ons.  If you developed an add-on that has not been updated to the WebExtensions API, there is still time to do so. Documentation is available, and you can ask questions by emailing dev-addons@mozilla.org or joining the #webextensions channel at irc.mozilla.org.

If you are supporting users who use add-ons, now is a good time to encourage them to check if their add-on works with Firefox 57+.

The post Announcing ESR60 with policy engine appeared first on Future Releases.

Planet MozillaReps Council at Austin

TL;DR

The All Hands is a special time of the year where Mozilla employees along with core volunteers gather for a week of many meetings and brainstorming. The All Hands Wiki page has more information about the general setting. During the All Hands, the Reps Council participated in the Open Innovation meetings as well as had meetings about what 2018 planning. One of our main topics was about the Mission Driven Mozillians proposal.

Hot Topics

As you already know, we asked in the past for your input on discourse (if you’re not already subscribed to the Reps category, maybe now it’s the right moment)

Before we start with the recap of the week, we would like to say thank you to the Reps that left feedbacks on that thread (Felipe, Edoardo and André) but also all to the Mozillians on https://discourse.mozilla.org/t/mission-driven-mozillians-volunteer-leadership-principles-practices/22386/ that left their feedbacks too.

These suggestions were very important and on shaping the document that will be available soon (we had more discussions during the All Hands so the new revised version is not yet ready).

Mission Driven Mozillians was the major topic on the Reps Council meetings. After consulting the results of Communities and Contributors Qualitative Survey we shaped the following questions. https://docs.google.com/document/d/1whJ6cyR-zozeMdEjtuvQTitSpz5T6bsy44AP0I7f6Y4/edit):

  • Is the Reps program really a representative of all the mozillian communities?
  • How does the program need to evolve to engage more with our members?
  • How can we get new Reps to join the new Coach and Resources initiatives?
  • How can we improve the use of the report tool by our Reps?

Plans for 2018

 

A photo with: Reps Peers, Reps Council (missing Prathamesh and Elio), Kiki (the awesome girl that manage all our budget/swag requests) and Manel (an unexpected guest during the photo shoot)

 

Moving forward, our plan is to deep dive into those because and also start acting on them.

The council also highly engaged with the Leadership proposal (that had a high priority) since it will be the base on how Mozilla communities will be shaped in the near future and will of course influence our program.

Open by Design was the motto of this All Hands during all the meetings inside Open Innovation, but also across the organization. You see Mozilla has a strong value that the other IT companies don’t have: the community.

Mozilla is more than a Silicon Valley IT company

One of the most curious and interesting, for the purpose of openness, thing is the ability to access  official Mozilla Slack channels for all the mozillians with NDA to. This support enables the volunteers to be able to communicate directly with the employees. And Yes, as Reps you have already signed the NDA so you can access without problems the various channels!

Planet MozillaFirefox Release Management at FOSDEM 2018

Fosdem logo

Like every year, Brussels will host on February 3 & 4 the largest European Open Source event gathering thousands of developers, namely FOSDEM.

Sylvestre, head of the Firefox Release Management team, will explain on Saturday how we ship Firefox to hundreds of millions of users every 6 weeks and how our test suites, pre-release channels, static analyzers, data mining on uplifts, code coverage, fuzzers and community feedback are part of the tools and processes Mozilla uses to ship Firefox every 6 weeks.

Here are the details of the conference:

Firefox: How to ship quality software
6000 new patches, a release every 6 weeks, how Mozilla does it?
Speaker Sylvestre Ledru
Track Mozilla devroom
Room UA2.118 (Henriot)
Day Saturday
Start 14:00
End 14:30


Several members of the Release Management team as well as other Mozilla employees from other departments and Mozilla volunteers will be at Fosdem so don’t hesitate to come and talk to us at the Mozilla Developper Room or at our booth.

In addition to the official schedule for our devroom, our Mozilla Wiki page for Fosdem 2018 contains all the information you may need regarding our participation to this event.

Last but not least, we would like to thank our Belgian Mozilla community, particularly Anthony Maton and Ziggy Maes for organizing our participation at Fosdem!

Planet MozillaWeb. Period.

Well. I have published something on Medium. #epub #web #future #eprdctn

Planet WebKitManuel Rego: "display: contents" is coming

Yes, display: contents is enabled by default in Blink and WebKit and it will be probably shipped in Chrome 65 and Safari 11.1. These browsers will join Firefox that is shipping it since version 37, which makes Edge the only one missing the feature (you can vote for it!).

Regarding this I’d like to highlight that the work to support it in Chromium was started by Emilio Cobos during his Igalia Coding Experience that took place from fall 2016 to summer 2017.

You might (or not) remember a blog post from early 2016 where I was talking about the Igalia Coding Experience program and some ideas of tasks to be done as part of the Web Platform team. One of the them was display: contents which is finally happening.

What is display: contents?

This new value for the display property allows you to somehow remove an element from the box tree but still keep its contents. The proper definition from the spec:

The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes and text runs as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents (including both its source-document children and its pseudo-elements, such as ::before and ::after pseudo-elements, which are generated before/after the element’s children as normal).

A simple example will help to understand it properly:

<div style="display: contents;
            background: magenta; border: solid thick black; padding: 20px;
            color: cyan; font: 30px/1 Monospace;">
  <span style="background: black;">foobar</span>
</div>

display: contents makes that the div doesn’t generate any box, so its background, border and padding are not renderer. However the inherited properties like color and font have effect on the child (span element) as expected.

For this example, the final result would be something like:

<span style="background: black; color: cyan; font: 30px/1 Monospace;">foobar</span>

Unsupported

foobar

Actual

foobar

Supported

foobar

Unsupported vs actual (in your browser) vs supported output for the previous example

If you want more details Rachel Andrew has a nice blog post about this topic.

CSS Grid Layout & display: contents

As you could expect from a post from myself this is somehow related to CSS Grid Layout. 😎 display: contents can be used as a replacement of subgrids (which are not supported by any browser at this point) in some use cases. However subgrids are still needed for other scenarios.

The canonical example for Grid Layout auto-placement is a simple form like:

<style>
  form   { display:     grid;   }
  label  { grid-column: 1;      }
  input  { grid-column: 2;      }
  button { grid-column: span 2; }
</style>
<form>
  <label>Name</label><input />
  <label>Mail</label><input />
  <button>Send</button>
</form>

A simple form formatted with CSS Grid Layout A simple form formatted with CSS Grid Layout

However this is not the typical HTML of a form, as you usually want to use a list inside, so people using screen readers will know how many fields they have to fill in your form beforehand. So the HTML looks more like this:

<form>
  <ul>
    <li><label>Name</label><input /></li>
    <li><label>Mail</label><input /></li>
    <li><button>Send</button></li>
  </ul>
</form>

With display: contents you’ll be able to have the same layout than in the first case with a similar CSS:

ul     { display: grid;       }
li     { display: contents;   }
label  { grid-column: 1;      }
input  { grid-column: 2;      }
button { grid-column: span 2; }

So this is really nice, now when you convert your website to start using CSS Grid Layout, you would need less changes on your HTML and you won’t need to remove some HTML that is really useful, like the list in the previous example.

Chromium implementation

As I said in the introduction, Firefox shipped display: contents three years ago, however Chromium didn’t have any implementation for it. Igalia as CSS Grid Layout implementor was interested in having support for the feature as it’s a handy solution for several Grid Layout use cases.

The proposal for the Igalia Coding Experience was the implementation of display: contents on Blink as the main task. Emilio did an awesome job and managed to implement most of it, reporting issues to CSS Working Group and other browsers as needed, and writing tests for the web-platform-tests repository to ensure interoperability between the implementations.

Once the Coding Experience was over there were still a few missing things to be able to enable display: contents by default. Rune Lillesveen (Google and previously Opera) who was helping during the whole process with the reviews, finished the work and shipped it past week.

WebKit implementation

WebKit already had an initial support for display: contents that was only used internally by Shadow DOM implementation and not exposed to the end users, neither supported by the rest of the code.

We reactivated the work there too, he didn’t have time to finish the whole thing but later Antti Koivisto (Apple) completed the work and enabled it by default on trunk by November 2017.

Conclusions

Igalia is one of the top external contributors on the open web platform projects. This put us on a position that allows us to implement new features in the different open source projects, thanks to our community involvement and internal knowledge after several years of experience on the field. Regarding display: contents implementation, this feature probably wouldn’t be available today in Chromium and WebKit without Igalia’s support, in this particular case through a Coding Experience.

We’re really happy about the results of the Coding Experience and we’re looking forward to repeat the success story in the future.

Of course, all the merit goes to Emilio, who is an impressive engineer and did a great job during the Coding Experience. As part of this process he got committer privileges in both Chromium and WebKit projects. Kudos!

Last, but not least, thanks to Antti and Rune for finishing the work and making display: contents available to WebKit and Chromium users.

Planet Mozillahappy additional push day!

release tag

the following changes have been pushed to bugzilla.mozilla.org:

  • [1429398] Scrolling down with keyboard no longer works correctly in at least Firefox
  • [1429449] Dropdown menu shouldn’t handle non-primary button click
  • [1429290] Bugs now display too wide to fit in my window; reading bugs is much harder

discuss these changes on mozilla.tools.bmo.

Planet WebKitRelease Notes for Safari Technology Preview 47

Safari Technology Preview Release 47 is now available for download for macOS Sierra and macOS High Sierra. If you already have Safari Technology Preview installed, you can update from the Mac App Store’s Updates tab. This release covers WebKit revisions 225841-226358.

This release contains the Spectre mitigations released in iOS 11.2.2, the High Sierra 10.13.2 supplemental update, and Safari 11.0.2 reissue released on Monday, January 8. For more information about Spectre, see What Spectre and Meltdown Mean For WebKit.

Storage Access API

This is a newly proposed API (WhatWG issue) that can be enabled in the Develop menu under Experimental Features. Cookies partitioned by Safari’s Intelligent Tracking Prevention can be accessed in an iframe loaded from the cookie’s domain by calling document.requestStorageAccess() in a click event handler. When the returned promise resolves, the first-party cookies in that iframe will be accessible. There is also the convenience function document.hasStorageAccess() available that doesn’t require a click.

  • Enabled allowing requests from non-sandboxed <iframes> (r226244)
  • Implemented frame-specific access in the document.cookie layer (r225934)
  • Made document.hasStorageAccess() retrieve the current status from the network process (r226016)
  • Refactored XPC for access removal to go straight from the web process to the network process (r226389)

Service Workers

  • Added support for response blob given to fetch events (r226066)
  • Changed extracting a body of type Blob to set the Content-Type to null instead of an empty string (r226162)
  • Changed to use “error” redirect mode for fetching service worker scripts (r226087)
  • Changed the Service Worker script fetch request to set the Service-Worker header (r225996)
  • Changed Service Worker to not clean HTTP headers added by the application or by Fetch specification before Service Worker interception (r226126)
  • Fixed the default scope used when registering a service worker (r226096)
  • Fixed the Service Worker Registration promise sometimes not getting rejected when the script load fails (r225975)
  • Fixed Service Worker served response tainting to keep its tainting (r226090)
  • Fixed scopeURL to start with the provided scriptURL (r226141)
  • Fixed self.importScripts() to obey updateViaCache inside service workers (r225940)
  • Fixed Fetch handling to wait for the Service Worker’s state to become activated (r226136)
  • Fixed SameOrigin and CORS fetch to fail on opaque responses served from a Service Worker (r226084)
  • Fixed memory cache to not reuse resources with a different credential fetch option (r226333)
  • Prevented searching for service worker registration for non-HTTP navigation loads (r226185, r226200)
  • Supported Service Worker interception of a request with blob body (r226191)

Media

  • Enabled picture-in-picture from an inline element on suspend (r226217)
  • Fixed playing media elements which call “pause(); play()” getting the play promise rejected (r226059, r226150)
  • Implemented <iframe allow="camera; microphone"> (r225963)

Rendering

  • Fixed elements animated on-screen that are sometimes missing (r225983)
  • Fixed setting the fePointLights color (r226317)
  • Fixed the color of the bottom right pixel of feDiffuseLighting (r226316)
  • Fixed SVG lighting colors to be converted into linearSRGB (r226315)
  • Updated the SVG use element’s shadow trees explicitly before the style recall (r225868)

Web Inspector

  • Enabled the Canvas Tab by default (r225979)
  • Improved open time performance when enumerating system fonts (r226352)
  • Fixed Command-Option-R (⌘⌥R) in the docked inspector causing Web Inspector to reload instead of the inspected page (r225907)
  • Fixed the URL filter in the Network Tab to be case-insensitive like filter bars in other tabs (r225939)
  • Fixed mis-sized waterfall graphs in the Network Tab after closing the detail view (r226070)
  • Redesigned the waterfall popover showing timing data in the Network Tab table (r226158)
  • Updated the Time column in the Network Tab table to include the total duration not just the download duration (r226151)
  • Added an inline swatch for CSS variables in the Styles sidebar (r226079)
  • Added support for typing a semicolon at the end of a value to move to the next property in the Styles sidebar (r226149)
  • Enabled Command-S (⌘S) to save changes in the matching CSS resource in the Styles sidebar (r226082)
  • Fixed selecting text in the Styles sidebar to not add new properties (r225945)
  • Implemented clicking above the selector to prepend a new property in the Styles sidebar (r225968)

Clipboard API

  • Fixed isSafari runtime check to enable custom clipboard types and clipboard data sanitization in Safari Technology Preview (r226215)
  • Fixed not being able to paste images on Gmail (r226173)
  • Reverted blob URL conversions in pasted contents for LegacyWebKit clients (r226156)

Bug Fix

  • Avoided waking plugin process up unnecessarily (r225984)

Storage Access API: Refactor XPC for access removal to go straight from the web process to the network process (r226389)

Planet MozillaThe Joy of Coding - Episode 125

The Joy of Coding - Episode 125 mconley livehacks on real Firefox bugs while thinking aloud.

Planet MozillaThe Joy of Coding - Episode 125

The Joy of Coding - Episode 125 mconley livehacks on real Firefox bugs while thinking aloud.

Planet MozillaTurning a Corner in the New Year

I’m going to start the year off with a blog post, mostly to procrastinate on replying to the many e-mails that my very productive colleagues have sent my way 🙂

2017 was quite a year beyond the socio-economic, geo-political, and bizarre. I, and many of my colleagues did what we could: find solace in work. I’ve often found that in uncertain times, making forward progress on difficult technical projects provides just enough incentive to continue for a bit longer. With the successful release of Firefox 57, I’m again optimistic about the future for the technical work. The Firefox Layout Engine team has a lot to be proud of in the 57 version. The winning combination was shipping big-ticket investments, and grinding down on many very difficult bugs. Plan “A” all the way!

Of course, it’s easy to see this in retrospect. I recall many late nights wondering “is this even going to work?” My friend and former colleague Robert O’Callahan recently revealed that he had similar doubts from before my time on the project. I wonder how much of that is inherent in the work. Is the Mozilla mission also the same narrative that fills me and other leaders with the sense that we’re always in rescue mode? Is that a sustainable lifestyle? In any case, it does feel like we’ve turned a corner.

For the first time in a long time, I feel like the wind’s at our backs and about to go into 2018 with that momentum. It would be hubris on my part to say that we’ve figured it all out. 2018’s uncertainties (e.g, Spectre/Meltdown) promise more late nights ahead. We’ve got lots of things in flight, and more ideas to investigate. We’ll need to make changes to deal with it all, but that’s par for the coming year’s course.

Happy New Year!

Planet Mozillahappy bmo push day

release tag

the following changes have been pushed to bugzilla.mozilla.org:

  • [1429110] Update Revision.pm type checking to treat bug id as a simple string
  • [1429075] Encoding issue in notification area (mojibake)
  • [1429076] Add lock icon for requests on security-sensitive bugs in notification area
  • [1429194] Clicking requests icon gives “Loading… ” but never actually completes
  • [1429220] Issues with the new fixed header implementation

discuss these changes on mozilla.tools.bmo.

Planet MozillaWhat Are Tokio and Async IO All About?

The Rust community lately has been focusing a lot on “async I/O” through the tokio project. This is pretty great!

But for many in the community who haven’t worked with web servers and related things it’s pretty confusing as to what we’re trying to achieve there. When this stuff was being discussed around 1.0, I was pretty lost as well, having never worked with this stuff before.

What’s all this Async I/O business about? What are coroutines? Lightweight threads? Futures? How does this all fit together?

What problem are we trying to solve?

One of Rust’s key features is “fearless concurrency”. But the kind of concurrency required for handling a large amount of I/O bound tasks – the kind of concurrency found in Go, Elixir, Erlang – is absent from Rust.

Let’s say you want to build something like a web service. It’s going to be handling thousands of requests at any point in time (known as the “c10k problem”). In general, the problem we’re considering is having a huge number of I/O bound (usually network I/O) tasks.

“Handling N things at once” is best done by using threads. But … thousands of threads? That sounds a bit much. Threads can be pretty expensive: Each thread needs to allocate a large stack, setting up a thread involves a bunch of syscalls, and context switching is expensive.

Of course, thousands of threads all doing work at once is not going to work anyway. You only have a fixed number of cores, and at any one time only one thread will be running on a core.

But for cases like web servers, most of these threads won’t be doing work. They’ll be waiting on the network. Most of these threads will either be listening for a request, or waiting for their response to get sent.

With regular threads, when you perform a blocking I/O operation, the syscall returns control to the kernel, which won’t yield control back, because the I/O operation is probably not finished. Instead, it will use this as an opportunity to swap in a different thread, and will swap the original thread back when its I/O operation is finished (i.e. it’s “unblocked”). Without Tokio and friends, this is how you would handle such things in Rust. Spawn a million threads; let the OS deal with scheduling based on I/O.

But, as we already discovered, threads don’t scale well for things like this1.

We need “lighter” threads.

Lightweight threading

I think the best way to understand lightweight threading is to forget about Rust for a moment and look at a language that does this well, Go.

So instead, Go has lightweight threads, called “goroutines”. You spawn these with the go keyword. A web server might do something like this:

listener, err = net.Listen(...)
// handle err
for {
    conn, err := listener.Accept()
    // handle err

    // spawn goroutine:
    go handler(conn)
}

This is a loop which waits for new TCP connections, and spawns a goroutine with the connection and the function handler. Each connection will be a new goroutine, and the goroutine will shut down when handler finishes. In the meantime, the main loop continues executing, because it’s running in a different goroutine.

So if these aren’t “real” (operating system) threads, what’s going on?

A goroutine is an example of a “lightweight” thread. The operating system doesn’t know about these, it sees N threads owned by the Go runtime, and the Go runtime maps M goroutines onto them2, swapping goroutines in and out much like the operating system scheduler. It’s able to do this because Go code is already interruptible for the GC to be able to run, so the scheduler can always ask goroutines to stop. The scheduler is also aware of I/O, so when a goroutine is waiting on I/O it yields to the scheduler.

Essentialy, a compiled Go function will have a bunch of points scattered throughout it where it tells the scheduler and GC “take over if you want” (and also “I’m waiting on stuff, please take over”).

When a goroutine is swapped on an OS thread, some registers will be saved, and the program counter will switch to the new goroutine.

But what about its stack? OS threads have a large stack with them, and you kinda need a stack for functions and stuff to work.

What Go used to do was segmented stacks. The reason a thread needs a large stack is that most programming languages, including C, expect the stack to be contiguous, and stacks can’t just be “reallocated” like we do with growable buffers since we expect stack data to stay put so that pointers to stack data to continue to work. So we reserve all the stack we think we’ll ever need (~8MB), and hope we don’t need more.

But the expectation of stacks being contiguous isn’t strictly necessary. In Go, stacks are made of tiny chunks. When a function is called, it checks if there’s enough space on the stack for it to run, and if not, allocates a new chunk of stack and runs on it. So if you have thousands of threads doing a small amount of work, they’ll all get thousands of tiny stacks and it will be fine.

These days, Go actually does something different; it copies stacks. I mentioned that stacks can’t just be “reallocated” we expect stack data to stay put. But that’s not necessarily true — because Go has a GC it knows what all the pointers are anyway, and it can rewrite pointers to stack data on demand.

Either way, Go’s rich runtime lets it handle this stuff well. Goroutines are super cheap, and you can spawn thousands without your computer having problems.

Rust used to support lightweight/“green” threads (I believe it used segmented stacks). However, Rust cares a lot about not paying for things you don’t use, and this imposes a penalty on all your code even if you aren’t using green threads, and it was removed pre-1.0.

Async I/O

A core building block of this is Async I/O. As mentioned in the previous section, with regular blocking I/O, the moment you request I/O your thread will not be allowed to run (“blocked”) until the operation is done. This is perfect when working with OS threads (the OS scheduler does all the work for you!), but if you have lightweight threads you instead want to replace the lightweight thread running on the OS thread with a different one.

Instead, you use non-blocking I/O, where the thread queues a request for I/O with the OS and continues execution. The I/O request is executed at some later point by the kernel. The thread then needs to ask the OS “Is this I/O request ready yet?” before looking at the result of the I/O.

Of course, repeatedly asking the OS if it’s done can be tedious and consume resources. This is why there are system calls like epoll. Here, you can bundle together a bunch of unfinished I/O requests, and then ask the OS to wake up your thread when any of these completes. So you can have a scheduler thread (a real thread) that swaps out lightweight threads that are waiting on I/O, and when there’s nothing else happening it can itself go to sleep with an epoll call until the OS wakes it up (when one of the I/O requests completes).

(The exact mechanism involved here is probably more complex)

So, bringing this to Rust, Rust has the mio library, which is a platform-agnostic wrapper around non-blocking I/O and tools like epoll/kqueue/etc. It’s a building block; and while those used to directly using epoll in C may find it helpful, it doesn’t provide a nice programming model like Go does. But we can get there.

Futures

These are another building block. A Future is the promise of eventually having a value (in fact, in Javascript these are called Promises).

So for example, you can ask to listen on a network socket, and get a Future back (actually, a Stream, which is like a future but for a sequence of values). This Future won’t contain the response yet, but will know when it’s ready. You can wait() on a Future, which will block until you have a result, and you can also poll() it, asking it if it’s done yet (it will give you the result if it is).

Futures can also be chained, so you can do stuff like future.then(|result| process(result)). The closure passed to then itself can produce another future, so you can chain together things like I/O operations. With chained futures, poll() is how you make progress; each time you call it it will move on to the next future provided the existing one is ready.

This is a pretty good abstraction over things like non-blocking I/O.

Chaining futures works much like chaining iterators. Each and_then (or whatever combinator) call returns a struct wrapping around the inner future, which may contain an additional closure. Closures themselves carry their references and data with them, so this really ends up being very similar to a tiny stack!

🗼 Tokio 🗼

Tokio’s essentially a nice wrapper around mio that uses futures. Tokio has a core event loop, and you feed it closures that return futures. What it will do is run all the closures you feed it, use mio to efficiently figure out which futures are ready to make a step3, and make progress on them (by calling poll()).

This actually is already pretty similar to what Go was doing, at a conceptual level. You have to manually set up the Tokio event loop (the “scheduler”), but once you do you can feed it tasks which intermittently do I/O, and the event loop takes care of swapping over to a new task when one is blocked on I/O. A crucial difference is that Tokio is single threaded, whereas the Go scheduler can use multiple OS threads for execution. However, you can offload CPU-critical tasks onto other OS threads and use channels to coordinate so this isn’t that big a deal.

While at a conceptual level this is beginning to shape up to be similar to what we had for Go, code- wise this doesn’t look so pretty. For the following Go code:

// error handling ignored for simplicity

func foo(...) ReturnType {
    data := doIo()
    result := compute(data)
    moreData = doMoreIo(result)
    moreResult := moreCompute(data)
    // ...
    return someFinalResult
}

The Rust code will look something like

// error handling ignored for simplicity

fn foo(...) -> Future<ReturnType, ErrorType> {
    do_io().and_then(|data| do_more_io(compute(data)))
          .and_then(|more_data| do_even_more_io(more_compute(more_data)))
    // ......
}

Not pretty. The code gets worse if you introduce branches and loops. The problem is that in Go we got the interruption points for free, but in Rust we have to encode this by chaining up combinators into a kind of state machine. Ew.

Generators and async/await

This is where generators (also called coroutines) come in.

Generators are an experimental feature in Rust. Here’s an example:

let mut generator = || {
    let i = 0;
    loop {
        yield i;
        i += 1;
    }
};
assert_eq!(generator.resume(), GeneratorState::Yielded(0));
assert_eq!(generator.resume(), GeneratorState::Yielded(1));
assert_eq!(generator.resume(), GeneratorState::Yielded(2));

Functions are things which execute a task and return once. On the other hand, generators return multiple times; they pause execution to “yield” some data, and can be resumed at which point they will run until the next yield. While my example doesn’t show this, generators can also finish executing like regular functions.

Closures in Rust are sugar for a struct containing captured data, plus an implementation of one of the Fn traits to make it callable.

Generators are similar, except they implement the Generator trait4, and usually store an enum representing various states.

The unstable book has some examples on what the generator state machine enum will look like.

This is much closer to what we were looking for! Now our code can look like this:

fn foo(...) -> Future<ReturnType, ErrorType> {
    let generator = || {
        let mut future = do_io();
        let data;
        loop {
            // poll the future, yielding each time it fails,
            // but if it succeeds then move on
            match future.poll() {
                Ok(Async::Ready(d)) => { data = d; break },
                Ok(Async::NotReady(d)) => (),
                Err(..) => ...
            };
            yield future.polling_info();
        }
        let result = compute(data);
        // do the same thing for `doMoreIo()`, etc
    }

    futurify(generator)
}

where futurify is a function that takes a generator and returns a future which on each poll call will resume() the generator, and return NotReady until the generator finishes executing.

But wait, this is even more ugly! What was the point of converting our relatively clean callback-chaining code into this mess?

Well, if you look at it, this code now looks linear. We’ve converted our callback code to the same linear flow as the Go code, however it has this weird loop-yield boilerplate and the futurify function and is overall not very neat.

And that’s where futures-await comes in. futures-await is a procedural macro that does the last-mile work of packaging away this boilerplate. It essentially lets you write the above function as

#[async]
fn foo(...) -> Result<ReturnType, ErrorType> {
    let data = await!(do_io());
    let result = compute(data);
    let more_data = await!(do_more_io());
    // ....

Nice and clean. Almost as clean as the Go code, just that we have explicit await!() calls. These await calls are basically providing the same function as the interruption points that Go code gets implicitly.

And, of course, since it’s using a generator under the hood, you can loop and branch and do whatever else you want as normal, and the code will still be clean.

Tying it together

So, in Rust, futures can be chained together to provide a lightweight stack-like system. With async/await, you can neatly write these future chains, and await provides explicit interruption points on each I/O operation. Tokio provides an event loop “scheduler” abstraction, which you can feed async functions to, and under the hood it uses mio to abstract over low level non-blocking I/O primitives.

These are components which can be used independently — you can use tokio with futures without using async/await. You can use async/await without using Tokio. For example, I think this would be useful for Servo’s networking stack. It doesn’t need to do much parallel I/O (not at the order of thousands of threads), so it can just use multiplexed OS threads. However, we’d still want to pool threads and pipeline data well, and async/await would help here.

Put together, all these components get something almost as clean as the Go stuff, with a little more explicit boilerplate. Because generators (and thus async/await) play nice with the borrow checker (they’re just enum state machines under the hood), Rust’s safety guarantees are all still in play, and we get to have “fearless concurrency” for programs having a huge quantity of I/O bound tasks!

Thanks to Arshia Mufti, Steve Klabnik, Zaki Manian, and Kyle Huey for reviewing drafts of this post


  1. Note that this isn’t necessarily true for all network server applications. For example, Apache uses OS threads. OS threads are often the best tool for the job.

  2. Lightweight threading is also often called M:N threading (also “green threading”)

  3. In general future combinators aren’t really aware of tokio or even I/O, so there’s no easy way to ask a combinator “hey, what I/O operation are you waiting for?”. Instead, with Tokio you use special I/O primitives that still provide futures but also register themselves with the scheduler in thread local state. This way when a future is waiting for I/O, Tokio can check what the recentmost I/O operation was, and associate it with that future so that it can wake up that future again when epoll tells it that that I/O operation is ready.

  4. The Generator trait has a resume() function which you can call multiple times, and each time it will return any yielded data or tell you that the generator has finished running.

Footnotes

Updated: .  Michael(tm) Smith <mike@w3.org>