Bug 1401244 - Update webrender to commit 2a005f156b9f25862a2dc8443b57be37168233f2. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 22 Sep 2017 08:52:50 -0400
changeset 669047 eb07592ecc0b4ba3ae490cca3c2a2f3661e9812b
parent 669023 2cd3752963fc8f24f7c202687eab55e83222f608
child 669048 6823b8836bd9705fd762c744579bfe00d95f4bb7
push id81202
push userkgupta@mozilla.com
push dateFri, 22 Sep 2017 12:56:52 +0000
reviewersjrmuizel
bugs1401244
milestone58.0a1
Bug 1401244 - Update webrender to commit 2a005f156b9f25862a2dc8443b57be37168233f2. r?jrmuizel MozReview-Commit-ID: IJ9TXtRw8j3
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/examples/basic.rs
gfx/webrender/examples/common/boilerplate.rs
gfx/webrender/examples/scrolling.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/freelist.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/color.rs
gfx/webrender_api/src/font.rs
gfx/webrender_bindings/Cargo.toml
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 68e2e7cd0e39d216bb6c35dfb353dc0700bb6948
+Latest Commit: 2a005f156b9f25862a2dc8443b57be37168233f2
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.50.0"
+version = "0.51.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
@@ -41,11 +41,12 @@ servo-glutin = "0.11"     # for the exam
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.3", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 gamma-lut = "0.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
+core-foundation = "0.3"
 core-graphics = "0.8.0"
 core-text = { version = "6.1", default-features = false }
 gamma-lut = "0.2"
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -251,17 +251,17 @@ impl Example for App {
 
         if false {
             // draw text?
             let font_key = api.generate_font_key();
             let font_bytes = load_file("res/FreeSans.ttf");
             resources.add_raw_font(font_key, font_bytes, 0);
 
             let font_instance_key = api.generate_font_instance_key();
-            resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None);
+            resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
 
             let text_bounds = (100, 200).by(700, 300);
             let glyphs = vec![
                 GlyphInstance {
                     index: 48,
                     point: LayoutPoint::new(100.0, 100.0),
                 },
                 GlyphInstance {
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -211,16 +211,38 @@ pub fn main_wrapper(example: &mut Exampl
                 ) => {
                     let mut flags = renderer.get_debug_flags();
                     flags.toggle(webrender::ALPHA_PRIM_DBG);
                     renderer.set_debug_flags(flags);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
+                    Some(glutin::VirtualKeyCode::Key1),
+                ) => {
+                    api.set_window_parameters(document_id,
+                        size,
+                        DeviceUintRect::new(DeviceUintPoint::zero(), size),
+                        1.0
+                    );
+                }
+                glutin::Event::KeyboardInput(
+                    glutin::ElementState::Pressed,
+                    _,
+                    Some(glutin::VirtualKeyCode::Key2),
+                ) => {
+                    api.set_window_parameters(document_id,
+                        size,
+                        DeviceUintRect::new(DeviceUintPoint::zero(), size),
+                        2.0
+                    );
+                }
+                glutin::Event::KeyboardInput(
+                    glutin::ElementState::Pressed,
+                    _,
                     Some(glutin::VirtualKeyCode::M),
                 ) => {
                     api.notify_memory_pressure();
                 }
                 _ => if example.on_event(event, &api, document_id) {
                     let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
                     let mut resources = ResourceUpdates::new();
 
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -77,17 +77,17 @@ impl Example for App {
                 LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
             builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
 
             // Below the above rectangles, set up a nested scrollbox. It's still in
             // the same stacking context, so note that the rects passed in need to
             // be relative to the stacking context.
             let nested_clip_id = builder.define_scroll_frame(
                 None,
-                (0, 100).to(300, 400),
+                (0, 100).to(300, 1000),
                 (0, 100).to(200, 300),
                 vec![],
                 None,
                 ScrollSensitivity::ScriptAndInputEvents,
             );
             builder.push_clip_id(nested_clip_id);
 
             // give it a giant gray background just to distinguish it and to easily
@@ -95,41 +95,43 @@ impl Example for App {
             let info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
             builder.push_rect(&info, ColorF::new(0.5, 0.5, 0.5, 1.0));
 
             // add a teal square to visualize the scrolling/clipping behaviour
             // as you scroll the nested scrollbox
             let info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
             builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
 
-            // Add a sticky frame. It will "stick" at a margin of 10px from the top, until
-            // the scrollframe scrolls another 60px, at which point it will "unstick". This lines
-            // it up with the above teal square as it scrolls out of the visible area of the
-            // scrollframe
+            // Add a sticky frame. It will "stick" twice while scrolling, once
+            // at a margin of 10px from the bottom, for 40 pixels of scrolling,
+            // and once at a margin of 10px from the top, for 60 pixels of
+            // scrolling.
             let sticky_id = builder.define_sticky_frame(
                 None,
-                (50, 140).to(100, 190),
+                (50, 350).by(50, 50),
                 StickyFrameInfo::new(
                     Some(StickySideConstraint {
                         margin: 10.0,
                         max_offset: 60.0,
                     }),
                     None,
-                    None,
+                    Some(StickySideConstraint {
+                        margin: 10.0,
+                        max_offset: -40.0,
+                    }),
                     None,
                 ),
             );
             builder.push_clip_id(sticky_id);
-            let info = LayoutPrimitiveInfo::new((50, 140).to(100, 190));
+            let info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
             builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
             builder.pop_clip_id(); // sticky_id
 
-            // just for good measure add another teal square in the bottom-right
-            // corner of the nested scrollframe content, which can be scrolled into
-            // view by the user
+            // just for good measure add another teal square further down and to
+            // the right, which can be scrolled into view by the user
             let info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
             builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
 
             builder.pop_clip_id(); // nested_clip_id
 
             builder.pop_clip_id(); // clip_id
             builder.pop_stacking_context();
         }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1059,16 +1059,17 @@ impl FrameBuilder {
 
         let prim_font = FontInstance::new(
             font.font_key,
             font.size,
             *color,
             normal_render_mode,
             subpx_dir,
             font.platform_options,
+            font.variations.clone(),
         );
         let prim = TextRunPrimitiveCpu {
             font: prim_font,
             glyph_range,
             glyph_count,
             glyph_gpu_blocks: Vec::new(),
             glyph_keys: Vec::new(),
             shadow_render_mode,
@@ -2181,19 +2182,17 @@ impl<'a> LayerRectCalculationAndCullingP
                     Some(rect) => rect,
                     None => return None,
                 }
             }
 
             //TODO-LCCR: bake a single LCCR instead of all aligned rects?
             self.current_clip_stack.push(ClipWorkItem {
                 layer_index: clip.packed_layer_index,
-                clip_sources: self.frame_builder
-                    .clip_store
-                    .create_weak_handle(&clip.clip_sources),
+                clip_sources: clip.clip_sources.weak(),
                 apply_rectangles: next_node_needs_region_mask,
             });
             next_node_needs_region_mask = false;
         }
 
         self.current_clip_stack.reverse();
         self.current_clip_info = Some((clip_id, Some(bounding_rect)));
         Some(bounding_rect)
@@ -2312,19 +2311,17 @@ impl<'a> LayerRectCalculationAndCullingP
                         Some(rect) => rect,
                         None => continue,
                     },
                     _ => prim_screen_rect,
                 };
 
                 let extra = ClipWorkItem {
                     layer_index: packed_layer_index,
-                    clip_sources: self.frame_builder
-                        .clip_store
-                        .create_weak_handle(&prim_metadata.clip_sources),
+                    clip_sources: prim_metadata.clip_sources.weak(),
                     apply_rectangles: false,
                 };
 
                 RenderTask::new_mask(
                     None,
                     mask_rect,
                     &self.current_clip_stack,
                     Some(extra),
--- a/gfx/webrender/src/freelist.rs
+++ b/gfx/webrender/src/freelist.rs
@@ -10,19 +10,30 @@ use util::recycle_vec;
 //           retain() style functionality.
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 struct Epoch(u32);
 
 #[derive(Debug)]
 pub struct FreeListHandle<T> {
     index: u32,
+    epoch: Epoch,
     _marker: PhantomData<T>,
 }
 
+impl<T> FreeListHandle<T> {
+    pub fn weak(&self) -> WeakFreeListHandle<T> {
+        WeakFreeListHandle {
+            index: self.index,
+            epoch: self.epoch,
+            _marker: PhantomData,
+        }
+    }
+}
+
 impl<T> Clone for WeakFreeListHandle<T> {
     fn clone(&self) -> WeakFreeListHandle<T> {
         WeakFreeListHandle {
             index: self.index,
             epoch: self.epoch,
             _marker: PhantomData,
         }
     }
@@ -89,25 +100,16 @@ impl<T> FreeList<T> {
         let slot = &mut self.slots[id.index as usize];
         if slot.epoch == id.epoch {
             slot.value.as_mut()
         } else {
             None
         }
     }
 
-    pub fn create_weak_handle(&self, id: &FreeListHandle<T>) -> WeakFreeListHandle<T> {
-        let slot = &self.slots[id.index as usize];
-        WeakFreeListHandle {
-            index: id.index,
-            epoch: slot.epoch,
-            _marker: PhantomData,
-        }
-    }
-
     // Perform a database style UPSERT operation. If the provided
     // handle is a valid entry, update the value and return the
     // previous data. If the provided handle is invalid, then
     // insert the data into a new slot and return the new handle.
     pub fn upsert(&mut self, id: &WeakFreeListHandle<T>, data: T) -> UpsertResult<T> {
         if self.slots[id.index as usize].epoch == id.epoch {
             let slot = &mut self.slots[id.index as usize];
             let result = UpsertResult::Updated(slot.value.take().unwrap());
@@ -125,30 +127,33 @@ impl<T> FreeList<T> {
 
                 // Remove from free list.
                 self.free_list_head = slot.next;
                 slot.next = None;
                 slot.value = Some(item);
 
                 FreeListHandle {
                     index: free_index,
+                    epoch: slot.epoch,
                     _marker: PhantomData,
                 }
             }
             None => {
                 let index = self.slots.len() as u32;
+                let epoch = Epoch(0);
 
                 self.slots.push(Slot {
                     next: None,
-                    epoch: Epoch(0),
+                    epoch,
                     value: Some(item),
                 });
 
                 FreeListHandle {
                     index,
+                    epoch,
                     _marker: PhantomData,
                 }
             }
         }
     }
 
     pub fn free(&mut self, id: FreeListHandle<T>) -> T {
         let slot = &mut self.slots[id.index as usize];
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -405,16 +405,17 @@ fn raterize_200_glyphs() {
 
     let font = FontInstance::new(
         font_key,
         Au::from_px(32),
         ColorF::new(0.0, 0.0, 0.0, 1.0),
         FontRenderMode::Subpixel,
         SubpixelDirection::Horizontal,
         None,
+        Vec::new(),
     );
 
     let mut glyph_keys = Vec::with_capacity(200);
     for i in 0 .. 200 {
         glyph_keys.push(GlyphKey::new(
             i,
             LayoutPoint::zero(),
             font.render_mode,
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -108,16 +108,18 @@ mod platform {
     }
     #[cfg(target_os = "windows")]
     pub mod windows {
         pub mod font;
     }
 }
 
 #[cfg(target_os = "macos")]
+extern crate core_foundation;
+#[cfg(target_os = "macos")]
 extern crate core_graphics;
 #[cfg(target_os = "macos")]
 extern crate core_text;
 
 #[cfg(all(unix, not(target_os = "macos")))]
 extern crate freetype;
 
 #[cfg(target_os = "windows")]
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -1,35 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
-use api::{FontInstance, NativeFontHandle};
+use api::{FontInstance, FontVariation, NativeFontHandle};
 use api::GlyphKey;
 use app_units::Au;
+use core_foundation::array::{CFArray, CFArrayRef};
+use core_foundation::base::TCFType;
+use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
+use core_foundation::number::{CFNumber, CFNumberRef};
+use core_foundation::string::{CFString, CFStringRef};
 use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedLast};
 use core_graphics::base::kCGBitmapByteOrder32Little;
 use core_graphics::color_space::CGColorSpace;
 use core_graphics::context::{CGContext, CGTextDrawingMode};
 use core_graphics::data_provider::CGDataProvider;
-use core_graphics::font::{CGFont, CGGlyph};
+use core_graphics::font::{CGFont, CGFontRef, CGGlyph};
 use core_graphics::geometry::{CGPoint, CGRect, CGSize};
 use core_text;
-use core_text::font::CTFont;
+use core_text::font::{CTFont, CTFontRef};
 use core_text::font_descriptor::kCTFontDefaultOrientation;
 use gamma_lut::{Color as ColorLut, GammaLut};
 use internal_types::FastHashMap;
 use std::collections::hash_map::Entry;
 use std::ptr;
 use std::sync::Arc;
 
 pub struct FontContext {
     cg_fonts: FastHashMap<FontKey, CGFont>,
-    ct_fonts: FastHashMap<(FontKey, Au), CTFont>,
+    ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
     gamma_lut: GammaLut,
 }
 
 // core text is safe to use on multiple threads and non-shareable resources are
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
 pub struct RasterizedGlyph {
@@ -142,16 +147,127 @@ fn get_glyph_metrics(
         rasterized_ascent: top,
         rasterized_descent: -bottom,
         advance: advance as f32,
     };
 
     metrics
 }
 
+#[link(name = "ApplicationServices", kind = "framework")]
+extern {
+    static kCTFontVariationAxisIdentifierKey: CFStringRef;
+    static kCTFontVariationAxisNameKey: CFStringRef;
+    static kCTFontVariationAxisMinimumValueKey: CFStringRef;
+    static kCTFontVariationAxisMaximumValueKey: CFStringRef;
+    static kCTFontVariationAxisDefaultValueKey: CFStringRef;
+
+    fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef;
+
+    fn CGFontCreateCopyWithVariations(font: CGFontRef, vars: CFDictionaryRef) -> CGFontRef;
+}
+
+fn new_ct_font_with_variations(cg_font: &CGFont, size: Au, variations: &[FontVariation]) -> CTFont {
+    unsafe {
+        let ct_font = core_text::font::new_from_CGFont(cg_font, size.to_f64_px());
+        if variations.is_empty() {
+            return ct_font;
+        }
+        let axes_ref = CTFontCopyVariationAxes(ct_font.as_concrete_TypeRef());
+        if axes_ref.is_null() {
+            return ct_font;
+        }
+        let axes: CFArray = TCFType::wrap_under_create_rule(axes_ref);
+        let mut vals: Vec<(CFString, CFNumber)> = Vec::with_capacity(variations.len() as usize);
+        for axis_ptr in axes.iter() {
+            let axis: CFDictionary = TCFType::wrap_under_get_rule(axis_ptr as CFDictionaryRef);
+            if !axis.instance_of::<CFDictionaryRef, CFDictionary>() {
+                return ct_font;
+            }
+            let tag_val = match axis.find(kCTFontVariationAxisIdentifierKey as *const _) {
+                Some(tag_ptr) => {
+                    let tag: CFNumber = TCFType::wrap_under_get_rule(tag_ptr as CFNumberRef);
+                    if !tag.instance_of::<CFNumberRef, CFNumber>() {
+                        return ct_font;
+                    }
+                    match tag.to_i64() {
+                        Some(val) => val,
+                        None => return ct_font,
+                    }
+                }
+                None => return ct_font,
+            };
+            let mut val = match variations.iter().find(|variation| (variation.tag as i64) == tag_val) {
+                Some(variation) => variation.value as f64,
+                None => continue,
+            };
+
+            let name: CFString = match axis.find(kCTFontVariationAxisNameKey as *const _) {
+                Some(name_ptr) => TCFType::wrap_under_get_rule(name_ptr as CFStringRef),
+                None => return ct_font,
+            };
+            if !name.instance_of::<CFStringRef, CFString>() {
+                return ct_font;
+            }
+
+            let min_val = match axis.find(kCTFontVariationAxisMinimumValueKey as *const _) {
+                Some(min_ptr) => {
+                    let min: CFNumber = TCFType::wrap_under_get_rule(min_ptr as CFNumberRef);
+                    if !min.instance_of::<CFNumberRef, CFNumber>() {
+                        return ct_font;
+                    }
+                    match min.to_f64() {
+                        Some(val) => val,
+                        None => return ct_font,
+                    }
+                }
+                None => return ct_font,
+            };
+            let max_val = match axis.find(kCTFontVariationAxisMaximumValueKey as *const _) {
+                Some(max_ptr) => {
+                    let max: CFNumber = TCFType::wrap_under_get_rule(max_ptr as CFNumberRef);
+                    if !max.instance_of::<CFNumberRef, CFNumber>() {
+                        return ct_font;
+                    }
+                    match max.to_f64() {
+                        Some(val) => val,
+                        None => return ct_font,
+                    }
+                }
+                None => return ct_font,
+            };
+            let def_val = match axis.find(kCTFontVariationAxisDefaultValueKey as *const _) {
+                Some(def_ptr) => {
+                    let def: CFNumber = TCFType::wrap_under_get_rule(def_ptr as CFNumberRef);
+                    if !def.instance_of::<CFNumberRef, CFNumber>() {
+                        return ct_font;
+                    }
+                    match def.to_f64() {
+                        Some(val) => val,
+                        None => return ct_font,
+                    }
+                }
+                None => return ct_font,
+            };
+
+            val = val.max(min_val).min(max_val);
+            if val != def_val {
+                vals.push((name, CFNumber::from_f64(val)));
+            }
+        }
+        if vals.is_empty() {
+            return ct_font;
+        }
+        let vals_dict = CFDictionary::from_CFType_pairs(&vals);
+        let cg_var_font_ref = CGFontCreateCopyWithVariations(cg_font.as_concrete_TypeRef(), vals_dict.as_concrete_TypeRef());
+        let cg_var_font: CGFont = TCFType::wrap_under_create_rule(cg_var_font_ref);
+        core_text::font::new_from_CGFont(&cg_var_font, size.to_f64_px())
+    }
+}
+
 impl FontContext {
     pub fn new() -> FontContext {
         debug!("Test for subpixel AA support: {}", supports_subpixel_aa());
 
         // Force CG to use sRGB color space to gamma correct.
         let contrast = 0.0;
         let gamma = 0.0;
 
@@ -199,53 +315,58 @@ impl FontContext {
                 .cloned()
                 .collect::<Vec<_>>();
             for ct_font_key in ct_font_keys {
                 self.ct_fonts.remove(&ct_font_key);
             }
         }
     }
 
-    fn get_ct_font(&mut self, font_key: FontKey, size: Au) -> Option<CTFont> {
-        match self.ct_fonts.entry(((font_key).clone(), size)) {
+    fn get_ct_font(
+        &mut self,
+        font_key: FontKey,
+        size: Au,
+        variations: &[FontVariation],
+    ) -> Option<CTFont> {
+        match self.ct_fonts.entry((font_key, size, variations.to_vec())) {
             Entry::Occupied(entry) => Some((*entry.get()).clone()),
             Entry::Vacant(entry) => {
                 let cg_font = match self.cg_fonts.get(&font_key) {
                     None => return None,
                     Some(cg_font) => cg_font,
                 };
-                let ct_font = core_text::font::new_from_CGFont(cg_font, size.to_f64_px());
+                let ct_font = new_ct_font_with_variations(cg_font, size, variations);
                 entry.insert(ct_font.clone());
                 Some(ct_font)
             }
         }
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         let character = ch as u16;
         let mut glyph = 0;
 
-        self.get_ct_font(font_key, Au(16 * 60))
+        self.get_ct_font(font_key, Au(16 * 60), &[])
             .and_then(|ref ct_font| {
                 let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
 
                 if result {
                     Some(glyph as u32)
                 } else {
                     None
                 }
             })
     }
 
     pub fn get_glyph_dimensions(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<GlyphDimensions> {
-        self.get_ct_font(font.font_key, font.size)
+        self.get_ct_font(font.font_key, font.size, &font.variations)
             .and_then(|ref ct_font| {
                 let glyph = key.index as CGGlyph;
                 let (x_offset, y_offset) = font.get_subpx_offset(key);
                 let metrics = get_glyph_metrics(ct_font, glyph, x_offset, y_offset);
                 if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
                     None
                 } else {
                     Some(GlyphDimensions {
@@ -301,17 +422,17 @@ impl FontContext {
         }
     }
 
     pub fn rasterize_glyph(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<RasterizedGlyph> {
-        let ct_font = match self.get_ct_font(font.font_key, font.size) {
+        let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) {
             Some(font) => font,
             None => return Some(RasterizedGlyph::blank()),
         };
 
         let glyph = key.index as CGGlyph;
         let (x_offset, y_offset) = font.get_subpx_offset(key);
         let metrics = get_glyph_metrics(&ct_font, glyph, x_offset, y_offset);
         if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -30,16 +30,17 @@ use thread_profiler::register_thread_wit
 use time::precise_time_ns;
 
 struct Document {
     scene: Scene,
     frame: Frame,
     window_size: DeviceUintSize,
     inner_rect: DeviceUintRect,
     pan: DeviceIntPoint,
+    device_pixel_ratio: f32,
     page_zoom_factor: f32,
     pinch_zoom_factor: f32,
     // A set of pipelines that the caller has requested be
     // made available as output textures.
     output_pipelines: FastHashSet<PipelineId>,
     // A helper switch to prevent any frames rendering triggered by scrolling
     // messages between `SetDisplayList` and `GenerateFrame`.
     // If we allow them, then a reftest that scrolls a few layers before generating
@@ -48,58 +49,61 @@ struct Document {
     render_on_scroll: Option<bool>,
 }
 
 impl Document {
     pub fn new(
         config: FrameBuilderConfig,
         initial_size: DeviceUintSize,
         enable_render_on_scroll: bool,
+        default_device_pixel_ratio: f32,
     ) -> Self {
         let render_on_scroll = if enable_render_on_scroll {
             Some(false)
         } else {
             None
         };
         Document {
             scene: Scene::new(),
             frame: Frame::new(config),
             window_size: initial_size,
             inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_size),
             pan: DeviceIntPoint::zero(),
             page_zoom_factor: 1.0,
             pinch_zoom_factor: 1.0,
+            device_pixel_ratio: default_device_pixel_ratio,
             render_on_scroll,
             output_pipelines: FastHashSet::default(),
         }
     }
 
-    fn accumulated_scale_factor(&self, hidpi_factor: f32) -> f32 {
-        hidpi_factor * self.page_zoom_factor * self.pinch_zoom_factor
+    fn accumulated_scale_factor(&self) -> f32 {
+        self.device_pixel_ratio *
+        self.page_zoom_factor *
+        self.pinch_zoom_factor
     }
 
-    fn build_scene(&mut self, resource_cache: &mut ResourceCache, hidpi_factor: f32) {
-        let accumulated_scale_factor = self.accumulated_scale_factor(hidpi_factor);
+    fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
+        let accumulated_scale_factor = self.accumulated_scale_factor();
         self.frame.create(
             &self.scene,
             resource_cache,
             self.window_size,
             self.inner_rect,
             accumulated_scale_factor,
         );
     }
 
     fn render(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         resource_profile: &mut ResourceProfileCounters,
-        hidpi_factor: f32,
     ) -> RendererFrame {
-        let accumulated_scale_factor = self.accumulated_scale_factor(hidpi_factor);
+        let accumulated_scale_factor = self.accumulated_scale_factor();
         let pan = LayerPoint::new(
             self.pan.x as f32 / accumulated_scale_factor,
             self.pan.y as f32 / accumulated_scale_factor,
         );
         self.frame.build(
             resource_cache,
             gpu_cache,
             &self.scene.display_lists,
@@ -124,20 +128,18 @@ enum DocumentOp {
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
 /// The render backend operates on its own thread.
 pub struct RenderBackend {
     api_rx: MsgReceiver<ApiMsg>,
     payload_rx: PayloadReceiver,
     payload_tx: PayloadSender,
     result_tx: Sender<ResultMsg>,
-
-    // TODO(gw): Consider using strongly typed units here.
-    hidpi_factor: f32,
     next_namespace_id: IdNamespace,
+    default_device_pixel_ratio: f32,
 
     gpu_cache: GpuCache,
     resource_cache: ResourceCache,
 
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, Document>,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
@@ -147,17 +149,17 @@ pub struct RenderBackend {
 }
 
 impl RenderBackend {
     pub fn new(
         api_rx: MsgReceiver<ApiMsg>,
         payload_rx: PayloadReceiver,
         payload_tx: PayloadSender,
         result_tx: Sender<ResultMsg>,
-        hidpi_factor: f32,
+        default_device_pixel_ratio: f32,
         texture_cache: TextureCache,
         workers: Arc<ThreadPool>,
         notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
         frame_config: FrameBuilderConfig,
         recorder: Option<Box<ApiRecordingReceiver>>,
         blob_image_renderer: Option<Box<BlobImageRenderer>>,
         enable_render_on_scroll: bool,
     ) -> RenderBackend {
@@ -165,18 +167,17 @@ impl RenderBackend {
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
             api_rx,
             payload_rx,
             payload_tx,
             result_tx,
-            hidpi_factor,
-
+            default_device_pixel_ratio,
             resource_cache,
             gpu_cache: GpuCache::new(),
             frame_config,
             documents: FastHashMap::default(),
             next_namespace_id: IdNamespace(1),
             notifier,
             recorder,
 
@@ -212,19 +213,21 @@ impl RenderBackend {
             }
             DocumentMsg::SetPan(pan) => {
                 doc.pan = pan;
                 DocumentOp::Nop
             }
             DocumentMsg::SetWindowParameters {
                 window_size,
                 inner_rect,
+                device_pixel_ratio,
             } => {
                 doc.window_size = window_size;
                 doc.inner_rect = inner_rect;
+                doc.device_pixel_ratio = device_pixel_ratio;
                 DocumentOp::Nop
             }
             DocumentMsg::SetDisplayList {
                 epoch,
                 pipeline_id,
                 background,
                 viewport_size,
                 content_size,
@@ -266,17 +269,17 @@ impl RenderBackend {
                     doc.scene.set_display_list(
                         pipeline_id,
                         epoch,
                         built_display_list,
                         background,
                         viewport_size,
                         content_size,
                     );
-                    doc.build_scene(&mut self.resource_cache, self.hidpi_factor);
+                    doc.build_scene(&mut self.resource_cache);
                 }
 
                 if let Some(ref mut ros) = doc.render_on_scroll {
                     *ros = false; //wait for `GenerateFrame`
                 }
 
                 // Note: this isn't quite right as auxiliary values will be
                 // pulled out somewhere in the prim_store, but aux values are
@@ -295,17 +298,17 @@ impl RenderBackend {
                 DocumentOp::Built
             }
             DocumentMsg::SetRootPipeline(pipeline_id) => {
                 profile_scope!("SetRootPipeline");
 
                 doc.scene.set_root_pipeline_id(pipeline_id);
                 if doc.scene.display_lists.get(&pipeline_id).is_some() {
                     let _timer = profile_counters.total_time.timer();
-                    doc.build_scene(&mut self.resource_cache, self.hidpi_factor);
+                    doc.build_scene(&mut self.resource_cache);
                     DocumentOp::Built
                 } else {
                     DocumentOp::Nop
                 }
             }
             DocumentMsg::RemovePipeline(pipeline_id) => {
                 profile_scope!("RemovePipeline");
 
@@ -317,50 +320,47 @@ impl RenderBackend {
                 let _timer = profile_counters.total_time.timer();
 
                 if doc.frame.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true)
                 {
                     let frame = doc.render(
                         &mut self.resource_cache,
                         &mut self.gpu_cache,
                         &mut profile_counters.resources,
-                        self.hidpi_factor,
                     );
                     DocumentOp::Scrolled(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
             DocumentMsg::ScrollNodeWithId(origin, id, clamp) => {
                 profile_scope!("ScrollNodeWithScrollId");
                 let _timer = profile_counters.total_time.timer();
 
                 if doc.frame.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
                     let frame = doc.render(
                         &mut self.resource_cache,
                         &mut self.gpu_cache,
                         &mut profile_counters.resources,
-                        self.hidpi_factor,
                     );
                     DocumentOp::Scrolled(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
             DocumentMsg::TickScrollingBounce => {
                 profile_scope!("TickScrollingBounce");
                 let _timer = profile_counters.total_time.timer();
 
                 doc.frame.tick_scrolling_bounce_animations();
                 if doc.render_on_scroll == Some(true) {
                     let frame = doc.render(
                         &mut self.resource_cache,
                         &mut self.gpu_cache,
                         &mut profile_counters.resources,
-                        self.hidpi_factor,
                     );
                     DocumentOp::Scrolled(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
             DocumentMsg::GetScrollNodeState(tx) => {
                 profile_scope!("GetScrollNodeState");
@@ -378,29 +378,28 @@ impl RenderBackend {
                 // just rebuilds the frame if there are animated property
                 // bindings present for now.
                 // TODO(gw): Once the scrolling / reference frame changes
                 //           are completed, optimize the internals of
                 //           animated properties to not require a full
                 //           rebuild of the frame!
                 if let Some(property_bindings) = property_bindings {
                     doc.scene.properties.set_properties(property_bindings);
-                    doc.build_scene(&mut self.resource_cache, self.hidpi_factor);
+                    doc.build_scene(&mut self.resource_cache);
                 }
 
                 if let Some(ref mut ros) = doc.render_on_scroll {
                     *ros = true;
                 }
 
                 if doc.scene.root_pipeline_id.is_some() {
                     let frame = doc.render(
                         &mut self.resource_cache,
                         &mut self.gpu_cache,
                         &mut profile_counters.resources,
-                        self.hidpi_factor,
                     );
                     DocumentOp::Rendered(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
         }
     }
@@ -451,16 +450,17 @@ impl RenderBackend {
                     self.next_namespace_id = IdNamespace(namespace.0 + 1);
                     sender.send(namespace).unwrap();
                 }
                 ApiMsg::AddDocument(document_id, initial_size) => {
                     let document = Document::new(
                         self.frame_config.clone(),
                         initial_size,
                         self.enable_render_on_scroll,
+                        self.default_device_pixel_ratio,
                     );
                     self.documents.insert(document_id, document);
                 }
                 ApiMsg::UpdateDocument(document_id, doc_msg) => match self.process_document(
                     document_id,
                     doc_msg,
                     frame_counter,
                     &mut profile_counters,
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates};
 use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
 use api::{ColorF, FontRenderMode, SubpixelDirection};
 use api::{DevicePoint, DeviceUintRect, DeviceUintSize};
 use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate};
 use api::{ExternalImageData, ExternalImageType};
-use api::{FontInstanceOptions, FontInstancePlatformOptions};
+use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, GlyphKey, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize};
 use app_units::Au;
 use device::TextureFilter;
 use frame::FrameId;
 use glyph_cache::GlyphCache;
 use glyph_rasterizer::{GlyphRasterizer, GlyphRequest};
@@ -298,16 +298,17 @@ impl ResourceCache {
                 }
                 ResourceUpdate::AddFontInstance(instance) => {
                     self.add_font_instance(
                         instance.key,
                         instance.font_key,
                         instance.glyph_size,
                         instance.options,
                         instance.platform_options,
+                        instance.variations,
                     );
                 }
                 ResourceUpdate::DeleteFontInstance(instance) => {
                     self.delete_font_instance(instance);
                 }
             }
         }
     }
@@ -329,32 +330,34 @@ impl ResourceCache {
 
     pub fn add_font_instance(
         &mut self,
         instance_key: FontInstanceKey,
         font_key: FontKey,
         glyph_size: Au,
         options: Option<FontInstanceOptions>,
         platform_options: Option<FontInstancePlatformOptions>,
+        variations: Vec<FontVariation>,
     ) {
         let mut render_mode = FontRenderMode::Subpixel;
         let mut subpx_dir = SubpixelDirection::Horizontal;
         if let Some(options) = options {
             render_mode = options.render_mode;
             if render_mode == FontRenderMode::Mono {
                 subpx_dir = SubpixelDirection::None;
             }
         }
         let instance = FontInstance::new(
             font_key,
             glyph_size,
             ColorF::new(0.0, 0.0, 0.0, 1.0),
             render_mode,
             subpx_dir,
             platform_options,
+            variations,
         );
         self.resources.font_instances.insert(instance_key, instance);
     }
 
     pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
         self.resources.font_instances.remove(&instance_key);
         if let Some(ref mut r) = self.blob_image_renderer {
             r.delete_font_instance(instance_key);
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -665,17 +665,17 @@ impl TextureCache {
                 // This handle has never been allocated, so just
                 // insert a new cache entry.
                 Some(self.entries.insert(new_cache_entry))
             }
         };
 
         // If the cache entry is new, update it in the cache handle.
         if let Some(new_entry_handle) = new_entry_handle {
-            handle.entry = Some(self.entries.create_weak_handle(&new_entry_handle));
+            handle.entry = Some(new_entry_handle.weak());
             // Store the strong handle in the list that we scan for
             // cache evictions.
             if allocated_in_shared_cache {
                 self.shared_entry_handles.push(new_entry_handle);
             } else {
                 self.standalone_entry_handles.push(new_entry_handle);
             }
         }
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.50.0"
+version = "0.51.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint};
 use {DeviceUintRect, DeviceUintSize, FontInstanceKey, FontKey, GlyphDimensions, GlyphKey};
-use {FontInstance, FontInstanceOptions, FontInstancePlatformOptions, NativeFontHandle, WorldPoint};
+use {FontInstance, FontInstanceOptions, FontInstancePlatformOptions, FontVariation,
+     NativeFontHandle, WorldPoint};
 use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutSize, LayoutTransform,
      LayoutVector2D};
 use app_units::Au;
 use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
 
@@ -89,24 +90,26 @@ impl ResourceUpdates {
 
     pub fn add_font_instance(
         &mut self,
         key: FontInstanceKey,
         font_key: FontKey,
         glyph_size: Au,
         options: Option<FontInstanceOptions>,
         platform_options: Option<FontInstancePlatformOptions>,
+        variations: Vec<FontVariation>,
     ) {
         self.updates
             .push(ResourceUpdate::AddFontInstance(AddFontInstance {
                 key,
                 font_key,
                 glyph_size,
                 options,
                 platform_options,
+                variations,
             }));
     }
 
     pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
         self.updates.push(ResourceUpdate::DeleteFontInstance(key));
     }
 
     pub fn merge(&mut self, mut other: ResourceUpdates) {
@@ -142,16 +145,17 @@ pub enum AddFont {
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct AddFontInstance {
     pub key: FontInstanceKey,
     pub font_key: FontKey,
     pub glyph_size: Au,
     pub options: Option<FontInstanceOptions>,
     pub platform_options: Option<FontInstancePlatformOptions>,
+    pub variations: Vec<FontVariation>,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum DocumentMsg {
     SetDisplayList {
         list_descriptor: BuiltDisplayListDescriptor,
         epoch: Epoch,
         pipeline_id: PipelineId,
@@ -165,16 +169,17 @@ pub enum DocumentMsg {
     SetPinchZoom(ZoomFactor),
     SetPan(DeviceIntPoint),
     SetRootPipeline(PipelineId),
     RemovePipeline(PipelineId),
     EnableFrameOutput(PipelineId, bool),
     SetWindowParameters {
         window_size: DeviceUintSize,
         inner_rect: DeviceUintRect,
+        device_pixel_ratio: f32,
     },
     Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
     ScrollNodeWithId(LayoutPoint, ClipId, ScrollClamping),
     TickScrollingBounce,
     GetScrollNodeState(MsgSender<Vec<ScrollLayerState>>),
     GenerateFrame(Option<DynamicProperties>),
 }
 
@@ -619,22 +624,24 @@ impl RenderApi {
         self.send(document_id, DocumentMsg::SetPan(pan));
     }
 
     pub fn set_window_parameters(
         &self,
         document_id: DocumentId,
         window_size: DeviceUintSize,
         inner_rect: DeviceUintRect,
+        device_pixel_ratio: f32,
     ) {
         self.send(
             document_id,
             DocumentMsg::SetWindowParameters {
                 window_size,
                 inner_rect,
+                device_pixel_ratio,
             },
         );
     }
 
     pub fn tick_scrolling_bounce_animations(&self, document_id: DocumentId) {
         self.send(document_id, DocumentMsg::TickScrollingBounce);
     }
 
--- a/gfx/webrender_api/src/color.rs
+++ b/gfx/webrender_api/src/color.rs
@@ -55,17 +55,17 @@ impl Hash for ColorF {
         self.r._to_bits().hash(state);
         self.g._to_bits().hash(state);
         self.b._to_bits().hash(state);
         self.a._to_bits().hash(state);
     }
 }
 
 // FIXME: remove this when Rust 1.21 is stable (float_bits_conv)
-trait ToBits {
+pub trait ToBits {
     fn _to_bits(self) -> u32;
 }
 impl ToBits for f32 {
     fn _to_bits(self) -> u32 {
         unsafe { ::std::mem::transmute(self) }
     }
 }
 
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -1,24 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use {ColorF, ColorU, IdNamespace, LayoutPoint};
+use {ColorF, ColorU, IdNamespace, LayoutPoint, ToBits};
 use app_units::Au;
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 #[cfg(target_os = "windows")]
 use dwrote::FontDescriptor;
 #[cfg(target_os = "macos")]
 use serde::de::{self, Deserialize, Deserializer};
 #[cfg(target_os = "macos")]
 use serde::ser::{Serialize, Serializer};
+use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
 use std::sync::Arc;
 
 
 #[cfg(target_os = "macos")]
 #[derive(Clone)]
 pub struct NativeFontHandle(pub CGFont);
 
 #[cfg(target_os = "macos")]
@@ -149,16 +151,46 @@ impl Into<f64> for SubpixelOffset {
             SubpixelOffset::Quarter => 0.25,
             SubpixelOffset::Half => 0.5,
             SubpixelOffset::ThreeQuarters => 0.75,
         }
     }
 }
 
 #[repr(C)]
+#[derive(Clone, Copy, Debug, PartialOrd, Deserialize, Serialize)]
+pub struct FontVariation {
+    pub tag: u32,
+    pub value: f32,
+}
+
+impl Ord for FontVariation {
+    fn cmp(&self, other: &FontVariation) -> Ordering {
+        self.tag.cmp(&other.tag)
+            .then(self.value._to_bits().cmp(&other.value._to_bits()))
+    }
+}
+
+impl PartialEq for FontVariation {
+    fn eq(&self, other: &FontVariation) -> bool {
+        self.tag == other.tag &&
+        self.value._to_bits() == other.value._to_bits()
+    }
+}
+
+impl Eq for FontVariation {}
+
+impl Hash for FontVariation {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.tag.hash(state);
+        self.value._to_bits().hash(state);
+    }
+}
+
+#[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct GlyphOptions {
     pub render_mode: FontRenderMode,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct FontInstanceOptions {
@@ -181,41 +213,44 @@ pub struct FontInstance {
     // can't store as a f32 due to use of this type as a hash key.
     // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
     //           or something similar to that.
     pub size: Au,
     pub color: ColorU,
     pub render_mode: FontRenderMode,
     pub subpx_dir: SubpixelDirection,
     pub platform_options: Option<FontInstancePlatformOptions>,
+    pub variations: Vec<FontVariation>,
 }
 
 impl FontInstance {
     pub fn new(
         font_key: FontKey,
         size: Au,
         mut color: ColorF,
         render_mode: FontRenderMode,
         subpx_dir: SubpixelDirection,
         platform_options: Option<FontInstancePlatformOptions>,
+        variations: Vec<FontVariation>,
     ) -> FontInstance {
         // In alpha/mono mode, the color of the font is irrelevant.
         // Forcing it to black in those cases saves rasterizing glyphs
         // of different colors when not needed.
         if render_mode != FontRenderMode::Subpixel {
             color = ColorF::new(0.0, 0.0, 0.0, 1.0);
         }
 
         FontInstance {
             font_key,
             size,
             color: color.into(),
             render_mode,
             subpx_dir,
             platform_options,
+            variations,
         }
     }
 
     pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
         match self.subpx_dir {
             SubpixelDirection::None => (0.0, 0.0),
             SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
             SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,19 +1,19 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_api = {path = "../webrender_api", version = "0.50.0"}
+webrender_api = {path = "../webrender_api", version = "0.51.0"}
 bincode = "0.8"
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5.6"
 gleam = "0.4"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.50.0"
+version = "0.51.0"
 default-features = false