Bug 549861 - add missing files from reland patch. r=bustage
authorJohn Daggett <jdaggett@mozilla.com>
Mon, 20 May 2013 12:47:32 +0900
changeset 143875 25a1e66c0f1dcf877dd5aa3ec8474700ea5ccc82
parent 143874 9aee5481871596c76337edb81e0fd1a568851a9e
child 143876 2ba61101fe9431420046bfe169e5bc51cfe0d715
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage
bugs549861
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 549861 - add missing files from reland patch. r=bustage
dom/interfaces/css/nsIDOMCSSFontFeatureValuesRule.idl
gfx/thebes/gfxFontFeatures.cpp
layout/reftests/font-features/alternates-order-ref.html
layout/reftests/font-features/alternates-order.html
layout/reftests/font-features/annotations-ref.html
layout/reftests/font-features/annotations.html
layout/reftests/font-features/font-features-order-3.html
layout/reftests/font-features/font-features-order-4.html
layout/reftests/font-features/font-features-order-5.html
layout/reftests/font-features/font-kerning-1.html
layout/reftests/font-features/font-kerning-2.html
layout/reftests/font-features/font-kerning-3.html
layout/reftests/font-features/font-kerning-auto.html
layout/reftests/font-features/font-kerning-kern.html
layout/reftests/font-features/font-kerning-nokern.html
layout/reftests/font-features/font-kerning-none.html
layout/reftests/font-features/font-kerning-normal.html
layout/reftests/font-features/font-kerning-table-none.html
layout/reftests/font-features/font-kerning-table-normal.html
layout/reftests/font-features/font-variant-alternates-ref.html
layout/reftests/font-features/font-variant-alternates.html
layout/reftests/font-features/font-variant-caps-ref.html
layout/reftests/font-features/font-variant-caps.html
layout/reftests/font-features/font-variant-debug.html
layout/reftests/font-features/font-variant-east-asian-ref.html
layout/reftests/font-features/font-variant-east-asian.html
layout/reftests/font-features/font-variant-features.css
layout/reftests/font-features/font-variant-features.js
layout/reftests/font-features/font-variant-ligatures-ref.html
layout/reftests/font-features/font-variant-ligatures.html
layout/reftests/font-features/font-variant-numeric-ref.html
layout/reftests/font-features/font-variant-numeric.html
layout/reftests/font-features/font-variant-position-ref.html
layout/reftests/font-features/font-variant-position.html
layout/style/test/test_font_feature_values_parsing.html
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/css/nsIDOMCSSFontFeatureValuesRule.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   John Daggett <jdaggett@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIDOMCSSRule.idl"
+
+[scriptable, uuid(f4cb1776-389d-4f52-a4d8-68bea5bd00c1)]
+interface nsIDOMCSSFontFeatureValuesRule : nsIDOMCSSRule
+{
+  attribute DOMString fontFamily;
+                      // raises(DOMException) on setting
+
+  attribute DOMString valueText;
+                      // raises(DOMException) on setting
+};
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxFontFeatures.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#include "gfxFontFeatures.h"
+#include "nsUnicharUtils.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla;
+
+gfxFontFeatureValueSet::gfxFontFeatureValueSet()
+{
+    mFontFeatureValues.Init(10);
+}
+
+bool
+gfxFontFeatureValueSet::GetFontFeatureValuesFor(const nsAString& aFamily,
+                                                uint32_t aVariantProperty,
+                                                const nsAString& aName,
+                                                nsTArray<uint32_t>& aValues)
+{
+    nsAutoString family(aFamily), name(aName);
+    ToLowerCase(family);
+    ToLowerCase(name);
+    FeatureValueHashKey key(family, aVariantProperty, name);
+
+    aValues.Clear();
+    FeatureValueHashEntry *entry = mFontFeatureValues.GetEntry(key);
+    if (entry) {
+        NS_ASSERTION(entry->mValues.Length() > 0,
+                     "null array of font feature values");
+        aValues.AppendElements(entry->mValues);
+        return true;
+    }
+
+    return false;
+}
+
+
+void
+gfxFontFeatureValueSet::AddFontFeatureValues(const nsAString& aFamily,
+                 const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aValues)
+{
+    nsAutoString family(aFamily);
+    ToLowerCase(family);
+
+    uint32_t i, numFeatureValues = aValues.Length();
+    for (i = 0; i < numFeatureValues; i++) {
+        const FeatureValues& fv = aValues.ElementAt(i);
+        uint32_t alternate = fv.alternate;
+        uint32_t j, numValues = fv.valuelist.Length();
+        for (j = 0; j < numValues; j++) {
+            const ValueList& v = fv.valuelist.ElementAt(j);
+            nsAutoString name(v.name);
+            ToLowerCase(name);
+            FeatureValueHashKey key(family, alternate, name);
+            FeatureValueHashEntry *entry = mFontFeatureValues.PutEntry(key);
+            entry->mKey = key;
+            entry->mValues = v.featureSelectors;
+        }
+    }
+}
+
+bool
+gfxFontFeatureValueSet::FeatureValueHashEntry::KeyEquals(
+                                               const KeyTypePointer aKey) const
+{
+    return aKey->mPropVal == mKey.mPropVal &&
+           aKey->mFamily.Equals(mKey.mFamily) &&
+           aKey->mName.Equals(mKey.mName);
+}
+
+PLDHashNumber
+gfxFontFeatureValueSet::FeatureValueHashEntry::HashKey(
+                                                     const KeyTypePointer aKey)
+{
+    return HashString(aKey->mFamily) + HashString(aKey->mName) +
+           aKey->mPropVal * uint32_t(0xdeadbeef);
+}
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/alternates-order-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  font-family: libertine, sans-serif;
+  font-size: 800%;
+  line-height: 1.2em;
+}
+
+div { margin: 0 20px; }
+span {
+  -moz-font-feature-settings: "ss05"; /* crossed W */
+}
+
+</style>
+</head>
+<body lang="en">
+<div><span id="test1">W</span> <span id="test2">W</span> <span id="test3">W</span></div>
+<div><span id="test4">W</span> <span id="test5">W</span> <span id="test6">W</span></div>
+<div><span id="test7">W</span> <span id="test8">W</span> <span id="test9">W</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/alternates-order.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+
+@font-face {
+  font-family: fontA;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  -moz-font-feature-settings: "ss05" on;
+}
+
+@font-face {
+  font-family: fontB;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  -moz-font-feature-settings: "ss05" off;
+}
+
+@font-feature-values libertine {
+  @styleset { 
+    crossed-doubleu: 5;
+    somethingElse: 4;
+  }
+}
+
+@font-feature-values fontA {
+  @styleset { aLtW: 5; }
+}
+
+@font-feature-values fontB {
+  @styleset { crossedW: 5; several: 1 3 5; }
+  @styleset { altW: 4; }
+}
+
+@font-feature-values fontB {
+  @styleset {
+    AlTw: 5;
+    defined-for-fontB: 5;
+    scriptJ: 3;
+  }
+}
+
+body {
+  font-family: libertine, sans-serif;
+  font-size: 800%;
+  line-height: 1.2em;
+}
+
+/* -moz-font-feature-settings: "ss05"; crossed W */
+
+div { margin: 0 20px; }
+
+#test1 {
+  font-variant-alternates: styleset(crossed-doubleu);
+}
+
+#test2 {
+  /* testing case-insensitivity of styleset name */
+  font-family: fontB;
+  font-variant-alternates: styleset(altW);
+}
+
+#test3 {
+  /* testing case-insensitivity of styleset name */
+  font-family: fontB;
+  font-variant-alternates: styleset(ALTW);
+}
+
+#test4 {
+  /* testing escapes in styleset name */
+  font-family: fontB;
+  font-variant-alternates: styleset(\41 ltW);
+}
+
+#test5 {
+  /* testing font-specificity of feature value rule */
+  font-family: fontA;
+  font-variant-alternates: styleset(defined-for-fontB);
+}
+
+#test6 {
+  /* testing one feature doesn't affect another */
+  font-variant-alternates: styleset(somethingElse);
+  -moz-font-feature-settings: "ss05" on;
+}
+
+#test7 {
+  /* testing font-specificity of feature value rule */
+  font-family: fontA;
+  font-variant-alternates: styleset(scriptJ);
+  -moz-font-feature-settings: "ss06";
+}
+
+#test8 {
+  /* testing that an undefined value doesn't affect the results */
+  font-family: fontB;
+  font-variant-alternates: styleset(scriptJ, somethingUndefined, defined-for-fontB);
+}
+
+#test9 {
+  /* testing matching of font name with escape */
+  font-family: font\62 ;
+  font-variant-alternates: styleset(defined-for-fontB);
+}
+
+</style>
+</head>
+<body lang="en">
+<div><span id="test1">W</span> <span id="test2">W</span> <span id="test3">W</span></div>
+<div><span id="test4">W</span> <span id="test5">W</span> <span id="test6">W</span></div>
+<div><span id="test7">W</span> <span id="test8">W</span> <span id="test9">W</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/annotations-ref.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Annotations test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<style type="text/css">
+
+body {
+  margin: 20px;
+  font-size: 800%;
+  font-family: Hiragino Maru Gothic ProN, Meiryo;
+}
+
+/* for platforms without these fonts the default glyphs will show */
+
+@font-face {
+  font-family: testMeiryo-circled;
+  src: local(Meiryo);
+  -moz-font-feature-settings: "nalt" 4;
+}
+
+@font-face {
+  font-family: testMeiryo-black-circled;
+  src: local(Meiryo);
+  -moz-font-feature-settings: "nalt" 5;
+}
+
+@font-face {
+  font-family: testMeiryo-rounded-box;
+  src: local(Meiryo);
+  -moz-font-feature-settings: "nalt" 9;
+}
+
+@font-face {
+  font-family: testHiraginoMaru-circled;
+  src: local(HiraMaruProN-W4), local(Hiragino Maru Gothic ProN W4);
+  -moz-font-feature-settings: "nalt" 1;
+}
+
+@font-face {
+  font-family: testHiraginoMaru-black-circled;
+  src: local(HiraMaruProN-W4), local(Hiragino Maru Gothic ProN W4);
+  -moz-font-feature-settings: "nalt" 4;
+}
+
+@font-face {
+  font-family: testHiraginoMaru-rounded-box;
+  src: local(HiraMaruProN-W4), local(Hiragino Maru Gothic ProN W4);
+  -moz-font-feature-settings: "nalt" 10;
+}
+
+#test1 {
+  font-family: testHiraginoMaru-circled, testMeiryo-circled;
+}
+
+#test2 {
+  font-family: testHiraginoMaru-black-circled, testMeiryo-black-circled;
+}
+
+#test3 {
+  font-family: testHiraginoMaru-rounded-box, testMeiryo-rounded-box;
+}
+
+</style>
+
+</head>
+<body>
+<div><span id="test1">1</span> <span id="test2">2</span> <span id="test3">3</span></div>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/annotations.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Annotations test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<style type="text/css">
+
+body {
+  margin: 20px;
+  font-size: 800%;
+  font-family: Hiragino Maru Gothic ProN, Meiryo;
+}
+
+/* for platforms without these fonts the default glyphs will show */
+
+@font-feature-values Hiragino Maru Gothic ProN {
+  @annotation { circled: 1; }
+  @annotation { black-circled: 4; }
+  @annotation { black-box: 8; }
+  @annotation { black-rounded-box: 10; }
+}
+
+@font-feature-values Meiryo {
+  @annotation { 
+    circled: 4;
+    black-circled: 5;
+    black-box: 7;
+    black-rounded-box: 9;
+  }
+}
+
+#test1 {
+  font-variant-alternates: annotation(circled);
+}
+
+#test2 {
+  font-variant-alternates: annotation(black-circled);
+}
+
+#test3 {
+  font-variant-alternates: annotation(black-rounded-box);
+}
+
+</style>
+
+</head>
+<body>
+<div><span id="test1">1</span> <span id="test2">2</span> <span id="test3">3</span></div>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-features-order-3.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  -moz-font-feature-settings: "liga" on, "hlig" on;
+}
+body {
+  font-family: libertine, sans-serif;
+  font-size: 400%;
+  line-height: 2em;
+  /* font feature settings property should override font setting */
+  -moz-font-feature-settings: "liga" off, "hlig" off;
+}
+</style>
+</head>
+<body lang="en">
+fastest firefox
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-features-order-4.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  font-feature-settings: "liga" on, "hlig" on;
+}
+body {
+  font-family: libertine, sans-serif;
+  font-size: 400%;
+  line-height: 2em;
+  /* font variant property should override font setting */
+  font-variant-ligatures: no-common-ligatures no-historical-ligatures;
+}
+</style>
+</head>
+<body lang="en">
+fastest firefox
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-features-order-5.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  font-feature-settings: "liga" on, "hlig" on;
+}
+body {
+  font-family: libertine, sans-serif;
+  font-size: 400%;
+  line-height: 2em;
+  /* font variant property should override font setting but font feature
+     settings property should override that */
+  font-variant-ligatures: no-common-ligatures no-historical-ligatures;
+  font-feature-settings: "liga" on, "hlig" on;
+}
+</style>
+</head>
+<body lang="en">
+fastest firefox
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  /* font-feature-settings should take precedence over font-kerning,
+     so kerning should be DISabled here */
+  font-feature-settings: "kern" off;
+  font-kerning: normal;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  /* font-feature-settings should take precedence over font-kerning,
+     so kerning should be ENabled here. */
+  font-feature-settings: "kern" on;
+  font-kerning: none;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+  font-feature-settings: "kern" off;
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-auto.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-kerning: auto;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-kern.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-feature-settings: "kern" on;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-nokern.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-feature-settings: "kern" off;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-none.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-kerning: none;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-normal.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: libertine;
+  src: url(../fonts/LinLibertine_Re-4.7.5.woff) format("woff");
+}
+body {
+  margin: 10px;
+  font-family: libertine, sans-serif;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-kerning: normal;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-table-none.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: gentium;
+  /* font that has an old-style 'kern' table rather than GPOS 'kern' feature */
+  src: url(../fonts/sil/GenR102.ttf);
+}
+body {
+  margin: 10px;
+  font-family: gentium;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-kerning: none;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-kerning-table-normal.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style type="text/css">
+@font-face {
+  font-family: gentium;
+  src: url(../fonts/sil/GenR102.ttf);
+}
+body {
+  margin: 10px;
+  font-family: gentium;
+  font-size: 600%;
+  line-height: 1.2em;
+  font-kerning: normal;
+}
+</style>
+</head>
+<body lang="en">
+<div>Ta To</div>
+<div>AVA</div>
+<div>AWAY</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-alternates-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-alternates test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+
+<style type="text/css">
+</style>
+
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-alternates", true, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-alternates.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-alternates test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+
+<style type="text/css">
+</style>
+
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-alternates", false, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-caps-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-caps test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-caps", true, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-caps.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-caps test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-caps", false, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-debug.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "all", false, true));
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-east-asian-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-east-asian test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-east-asian", true, false));
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-east-asian.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-east-asian test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-east-asian", false, false));
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-features.css
@@ -0,0 +1,63 @@
+body { margin: 10px; }
+
+@font-face {
+  font-family: gsub-test;
+  src: url(../fonts/gsubtest/gsubtest-lookup3.otf);
+}
+
+td.prop {
+  font-family: Menlo, monospace;
+  font-weight: normal;
+  text-align: left;
+  font-size: 80%;
+}
+
+td.features {
+  font-family: gsub-test;
+}
+
+.invalid {
+  color: red;
+}
+
+@font-feature-values gsub-test {
+  @styleset { ok-alt-a: 1 3 5; ok-alt-b: 19; }
+  @character-variant { ok-1: 78 2; }
+  @character-variant { ok-3: 23; }
+  @character-variant { not-good: 0 2; }
+  @annotation { ok-4: 1; }
+  @annotation { bogus-font-doesnt-support: 23; }
+  @annotation { circled: 1; }
+  @character-variant { multi-def: 4; }
+  @annotation { multi-def: 3; }
+  @styleset { multi-def2: 3 4 5; }
+  @styleset { MULTI-def2: 2 6; }
+  @styleset { out-of-bounds1: 0; out-of-bounds2: 100; }
+}
+
+@font-feature-values bogus-family {
+  @styleset { bogus: 3 4 7; }
+}
+
+@font-feature-values GSUB-tEsT {
+  @styleset { mixed-case: 3 4 7; }
+}
+
+@font-feature-values gSuB-tEsT {
+  @styleset { 3blah: 1 3; }
+}
+
+@font-feature-values gSuB-tEsT {
+  @styleset { moxie: 14; }
+  @styleset { 3blah: 1; }
+}
+
+@font-feature-values gSUB-TeST {
+  @styleset { moxie2: 14; }
+  @bongo { blah: 1; }
+}
+
+@font-feature-values gSUB-TEst {
+  @bongo { blah2: 1; }
+  @styleset { moxie3: 14; }
+}
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-features.js
@@ -0,0 +1,220 @@
+
+// data associated with gsubtest test font for testing font features
+
+// prefix
+gPrefix = "";
+
+// equivalent properties
+// setting prop: value should match the specific feature settings listed
+//
+// each of these tests evaluate whether a given feature is enabled as required
+// and also whether features that shouldn't be enabled are or not.
+var gPropertyData = [
+  // font-variant-caps
+  // valid values
+  { prop: "font-variant-caps", value: "normal", features: {"smcp": 0} },
+  { prop: "font-variant-caps", value: "small-caps", features: {"smcp": 1, "c2sc": 0} },
+  { prop: "font-variant-caps", value: "all-small-caps", features: {"smcp": 1, "c2sc": 1, "pcap": 0} },
+  { prop: "font-variant-caps", value: "petite-caps", features: {"pcap": 1, "smcp": 0} },
+  { prop: "font-variant-caps", value: "all-petite-caps", features: {"c2pc": 1, "pcap": 1, "smcp": 0} },
+  { prop: "font-variant-caps", value: "titling-caps", features: {"titl": 1, "smcp": 0} },
+  { prop: "font-variant-caps", value: "unicase", features: {"unic": 1, "titl": 0} },
+
+  // invalid values
+  { prop: "font-variant-caps", value: "normal small-caps", features: {"smcp": 0}, invalid: true },
+  { prop: "font-variant-caps", value: "small-caps potato", features: {"smcp": 0}, invalid: true },
+  { prop: "font-variant-caps", value: "small-caps petite-caps", features: {"smcp": 0, "pcap": 0}, invalid: true },
+  { prop: "font-variant-caps", value: "small-caps all-small-caps", features: {"smcp": 0, "c2sc": 0}, invalid: true },
+  { prop: "font-variant-caps", value: "small-cap", features: {"smcp": 0}, invalid: true },
+
+  // font-variant-east-asian
+  // valid values
+  { prop: "font-variant-east-asian", value: "jis78", features: {"jp78": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "jis83", features: {"jp83": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "jis90", features: {"jp90": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "jis04", features: {"jp04": 1, "jp78": 0} },
+  { prop: "font-variant-east-asian", value: "simplified", features: {"smpl": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "traditional", features: {"trad": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "full-width", features: {"fwid": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "proportional-width", features: {"pwid": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "ruby", features: {"ruby": 1, "jp04": 0} },
+  { prop: "font-variant-east-asian", value: "jis78 full-width", features: {"jp78": 1, "fwid": 1, "jp83": 0} },
+  { prop: "font-variant-east-asian", value: "jis78 full-width ruby", features: {"jp78": 1, "fwid": 1, "jp83": 0, "ruby": 1} },
+  { prop: "font-variant-east-asian", value: "simplified proportional-width", features: {"smpl": 1, "pwid": 1, "jp83": 0} },
+  { prop: "font-variant-east-asian", value: "ruby simplified", features: {"ruby": 1, "smpl": 1, "trad": 0} },
+
+  // invalid values
+  { prop: "font-variant-east-asian", value: "ruby normal", features: {"ruby": 0}, invalid: true },
+  { prop: "font-variant-east-asian", value: "jis90 jis04", features: {"jp90": 0, "jp04": 0}, invalid: true },
+  { prop: "font-variant-east-asian", value: "simplified traditional", features: {"smpl": 0, "trad": 0}, invalid: true },
+  { prop: "font-variant-east-asian", value: "full-width proportional-width", features: {"fwid": 0, "pwid": 0}, invalid: true },
+  { prop: "font-variant-east-asian", value: "ruby simplified ruby", features: {"ruby": 0, "smpl": 0, "jp04": 0}, invalid: true },
+  { prop: "font-variant-east-asian", value: "jis78 ruby simplified", features: {"ruby": 0, "smpl": 0, "jp78": 0}, invalid: true },
+
+  // font-variant-ligatures
+  // valid values
+  { prop: "font-variant-ligatures", value: "normal", features: {"liga": 1, "dlig": 0} },
+  { prop: "font-variant-ligatures", value: "common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "no-common-ligatures", features: {"liga": 0, "clig": 0, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 1, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "no-discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 1, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "no-historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "no-contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 0} },
+  { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "historical-ligatures no-common-ligatures", features: {"clig": 0, "liga": 0, "dlig": 0, "hlig": 1, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "no-historical-ligatures discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 1, "hlig": 0, "calt": 1} },
+  { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures historical-ligatures no-contextual", features: {"clig": 1, "dlig": 0, "hlig": 1, "liga": 1, "calt": 0} },
+
+  // invalid values
+  { prop: "font-variant-ligatures", value: "common-ligatures normal", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "common-ligatures no-common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "common-ligatures common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "no-historical-ligatures historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "no-contextual contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "no-discretionary-ligatures discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
+  { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures no-common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
+
+  // font-variant-numeric
+  // valid values
+  { prop: "font-variant-numeric", value: "normal", features: {"lnum": 0, "tnum": 0, "pnum": 0, "onum": 0} },
+  { prop: "font-variant-numeric", value: "lining-nums", features: {"lnum": 1, "onum": 0, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "oldstyle-nums", features: {"lnum": 0, "onum": 1, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "proportional-nums", features: {"lnum": 0, "onum": 0, "pnum": 1, "tnum": 0} },
+  { prop: "font-variant-numeric", value: "proportional-nums oldstyle-nums", features: {"lnum": 0, "onum": 1, "pnum": 1, "tnum": 0} },
+  { prop: "font-variant-numeric", value: "tabular-nums", features: {"tnum": 1, "onum": 0, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "diagonal-fractions", features: {"frac": 1, "afrc": 0, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "stacked-fractions", features: {"frac": 0, "afrc": 1, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "slashed-zero", features: {"zero": 1, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "ordinal", features: {"ordn": 1, "pnum": 0} },
+  { prop: "font-variant-numeric", value: "lining-nums diagonal-fractions", features: {"frac": 1, "afrc": 0, "lnum": 1} },
+  { prop: "font-variant-numeric", value: "tabular-nums stacked-fractions", features: {"frac": 0, "afrc": 1, "tnum": 1} },
+  { prop: "font-variant-numeric", value: "tabular-nums slashed-zero stacked-fractions", features: {"frac": 0, "afrc": 1, "tnum": 1, "zero": 1} },
+  { prop: "font-variant-numeric", value: "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal", features: {"frac": 1, "afrc": 0, "tnum": 0, "pnum": 1, "onum": 1, "ordn": 1, "zero": 1} },
+
+  // invalid values
+  { prop: "font-variant-numeric", value: "lining-nums normal", features: {"lnum": 0, "onum": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "lining-nums oldstyle-nums", features: {"lnum": 0, "onum": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "lining-nums normal slashed-zero ordinal", features: {"lnum": 0, "onum": 0, "zero": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "proportional-nums tabular-nums", features: {"pnum": 0, "tnum": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "diagonal-fractions stacked-fractions", features: {"frac": 0, "afrc": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "slashed-zero diagonal-fractions slashed-zero", features: {"frac": 0, "afrc": 0, "zero": 0}, invalid: true },
+  { prop: "font-variant-numeric", value: "lining-nums slashed-zero diagonal-fractions oldstyle-nums", features: {"frac": 0, "afrc": 0, "zero": 0, "onum": 0}, invalid: true },
+
+  // font-variant-position
+  // valid values
+  { prop: "font-variant-position", value: "normal", features: {"subs": 0, "sups": 0} },
+  { prop: "font-variant-position", value: "super", features: {"subs": 0, "sups": 1} },
+  { prop: "font-variant-position", value: "sub", features: {"subs": 1, "sups": 0} },
+
+  // invalid values
+  { prop: "font-variant-position", value: "super sub", features: {"subs": 0, "sups": 0}, invalid: true },
+];
+
+// note: the code below requires an array "gFeatures" from :
+//   layout/reftests/fonts/gsubtest/gsubtest-features.js
+
+// The font defines feature lookups for all OpenType features for a
+// specific set of PUA codepoints, as listed in the gFeatures array.
+// Using these codepoints and feature combinations, tests can be
+// constructed to detect when certain features are enabled or not.
+
+// return a created table containing tests for a given property
+//
+// Ex: { prop: "font-variant-ligatures", value: "common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0} }
+//
+// This means that for the property 'font-variant-ligatures' with the value 'common-ligatures', the features listed should
+// either be explicitly enabled or disabled.
+
+// propData is the prop/value list with corresponding feature assertions
+// whichProp is either "all" or a specific subproperty (i.e. "font-variant-position")
+// isRef is true when this is the reference
+// debug outputs the prop/value pair along with the tests
+
+function createFeatureTestTable(propData, whichProp, isRef, debug)
+{
+  var table = document.createElement("table");
+
+  if (typeof(isRef) == "undefined") {
+    isRef = false;
+  }
+
+  if (typeof(debug) == "undefined") {
+    debug = false;
+  }
+
+  var doAll = (whichProp == "all");
+  for (var i in propData) {
+    var data = propData[i];
+
+    if (!doAll && data.prop != whichProp) continue;
+
+    var row = document.createElement("tr");
+    var invalid = false;
+    if ("invalid" in data) {
+      invalid = true;
+      row.className = "invalid";
+    }
+
+    var cell = document.createElement("td");
+    cell.className = "prop";
+    var styledecl = gPrefix + data.prop + ": " + data.value + ";";
+    cell.innerHTML = styledecl;
+    row.appendChild(cell);
+    if (debug) {
+      table.appendChild(row);
+    }
+
+    row = document.createElement("tr");
+    if (invalid) {
+      row.className = "invalid";
+    }
+
+    cell = document.createElement("td");
+    cell.className = "features";
+    if (!isRef) {
+      cell.style.cssText = styledecl;
+    }
+
+    for (var f in data.features) {
+      var feature = data.features[f];
+
+      var cp, unsupported = "F".charCodeAt(0);
+      var basecp = gFeatures[f];
+
+      if (typeof(basecp) == "undefined") {
+        cp = unsupported;
+      } else {
+        switch(feature) {
+        case 0:
+          cp = basecp;
+          break;
+        case 1:
+          cp = basecp + 1;
+          break;
+        case 2:
+          cp = basecp + 2;
+          break;
+        case 3:
+          cp = basecp + 3;
+          break;
+        default:
+          cp = basecp + 1;
+          break;
+        }
+      }
+
+      var span = document.createElement("span");
+      span.innerHTML = (isRef ? "P " : "&#x" + cp.toString(16) + "; ");
+      span.title = f + "=" + feature;
+      cell.appendChild(span);
+    }
+    row.appendChild(cell);
+    table.appendChild(row);
+  }
+
+  return table;
+}
+
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-ligatures-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-ligatures test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-ligatures", true, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-ligatures.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-ligatures test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-ligatures", false, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-numeric-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-numeric test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-numeric", true, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-numeric.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-numeric test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-numeric", false, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-position-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-position test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-position", true, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-features/font-variant-position.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>font-variant-position test</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script type="text/javascript" src="../fonts/gsubtest/gsubtest-features.js"></script>
+<script type="text/javascript" src="font-variant-features.js"></script>
+<link rel="stylesheet" href="font-variant-features.css" type="text/css"/>
+</head>
+<body>
+<div id="content"></div>
+<script type="text/javascript">
+  document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-position", false, false));
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_font_feature_values_parsing.html
@@ -0,0 +1,356 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset=utf-8>
+  <title>@font-feature-values rule parsing tests</title>
+  <link rel="author" title="John Daggett" href="mailto:jdaggett@mozilla.com">
+  <link rel="help" href="http://www.w3.org/TR/css3-fonts/#font-feature-values" />
+  <meta name="assert" content="tests that valid @font-feature-values rules parse and invalid ones don't" />
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=549861 -->
+  <script type="text/javascript" src="/resources/testharness.js"></script>
+  <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+  <style type="text/css">
+  </style>
+</head>
+<body>
+<div id="log"></div>
+<pre id="display"></pre>
+<style type="text/css" id="testbox"></style>
+
+<script type="text/javascript">
+var gPrefix = "";
+var kFontFeatureValuesRuleType = 14;
+
+function ruleName() { return "@" + gPrefix + "font-feature-values"; }
+function makeRule(f, v) {
+  return ruleName() + " " + f + " { " + v + " }";
+}
+
+function _()
+{
+  var i, decl = [];
+  for (i = 0; i < arguments.length; i++) {
+    decl.push(arguments[i]);
+  }
+  return makeRule("bongo", decl.join(" "));
+}
+
+// note: because of bugs in the way family names are serialized,
+// 'serializationSame' only implies that the value definition block
+// is the same (i.e. not including the family name list)
+
+var testrules = [
+
+  /* basic syntax */
+  { rule: ruleName() + ";", invalid: true },
+  { rule: ruleName() + " bongo;", invalid: true },
+  { rule: ruleName().replace("values", "value") + " {;}", invalid: true },
+  { rule: ruleName().replace("feature", "features") + " {;}", invalid: true },
+  { rule: makeRule("bongo", ""), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", ";"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", ",;"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", ";,"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", ",;,"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset;"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset,;"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset abc;"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { ;;abc }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc;; }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc: }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc,: }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc:, }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc:,; }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { a,b }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { a;b }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { a:;b: }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { a:,;b: }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { a:1,;b: }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc 1 2 3 }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc:, 1 2 3 }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc:; 1 2 3 }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc:; 1 2 3 }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc: 1 2 3a }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@styleset { abc: 1 2 3, def: 1; }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@blah @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@blah } @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
+  { rule: makeRule("bongo", "@blah , @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
+  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3; }", serialization: _("@styleset { abc: 1 2 3; }") },
+  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3 }", serialization: _("@styleset { abc: 1 2 3; }") },
+  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3;", serialization: _("@styleset { abc: 1 2 3; }") },
+  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3", serialization: _("@styleset { abc: 1 2 3; }") },
+  { rule: _("@styleset { ok-1: 1; }"), serializationSame: true },
+  { rule: _("@annotation { ok-1: 3; }"), serializationSame: true },
+  { rule: _("@stylistic { blah: 3; }"), serializationSame: true },
+  { rule: makeRule("bongo", "\n@styleset\n  { blah: 3; super-blah: 4 5;\n  more-blah: 5 6 7;\n }"), serializationSame: true },
+  { rule: makeRule("bongo", "\n@styleset\n  {\n blah:\n 3\n;\n super-blah:\n 4\n 5\n;\n  more-blah:\n 5 6\n 7;\n }"), serializationSame: true },
+
+  /* limits on number of values */
+  { rule: _("@stylistic { blah: 1; }"), serializationSame: true },
+  { rule: _("@styleset { blah: 1 2 3 4; }"), serializationSame: true },
+  { rule: _("@character-variant { blah: 1 2; }"), serializationSame: true },
+  { rule: _("@swash { blah: 1; }"), serializationSame: true },
+  { rule: _("@ornaments { blah: 1; }"), serializationSame: true },
+  { rule: _("@annotation { blah: 1; }"), serializationSame: true },
+
+  /* values ignored when used */
+  { rule: _("@styleset { blah: 0; }"), serializationSame: true },
+  { rule: _("@styleset { blah: 120 124; }"), serializationSame: true },
+  { rule: _("@character-variant { blah: 0; }"), serializationSame: true },
+  { rule: _("@character-variant { blah: 111; }"), serializationSame: true },
+  { rule: _("@character-variant { blah: 111 13; }"), serializationSame: true },
+
+  /* invalid value name */
+  { rulesrc: ["styleset { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["stylistic { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["character-variant { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["swash { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["ornaments { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["annotation { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@bongo { blah: 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@bongo { blah: 1 2 3 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@bongo { blah: 1 2 3; burp: 1;;; }"], serializationNoValueDefn: true },
+
+  /* values */
+  { rulesrc: ["@styleset { blah: -1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1 -1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1.5 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 15px }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: red }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: (1) }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah:(1) }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah:, 1 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: <1> }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1! }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1,, }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1 1 1 1; }"], serializationSame: true },
+
+  /* limits on number of values */
+  { rulesrc: ["@stylistic { blah: 1 2 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@character-variant { blah: 1 2 3 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@swash { blah: 1 2 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@ornaments { blah: 1 2 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@annotation { blah: 1 2 }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { blah: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; }"], serializationSame: true },
+
+  /* family names */
+  { rule: makeRule("bongo", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("\"bongo\"", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("'bongo'", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("\\62 ongo", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("bongo, super bongo, bongo the supreme", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("bongo,, super bongo", "@styleset { blah: 1; }"), invalid: true },
+  { rule: makeRule("bongo,*", "@styleset { blah: 1; }"), invalid: true },
+  { rule: makeRule("bongo, sans-serif", "@styleset { blah: 1; }"), invalid: true },
+  { rule: makeRule("serif, sans-serif", "@styleset { blah: 1; }"), invalid: true },
+  { rule: makeRule("'serif', 'sans-serif'", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("bongo, \"super bongo\", 'bongo the supreme'", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("毎日カレーを食べたい!", "@styleset { blah: 1; }"), serializationSame: true },
+  { rule: makeRule("毎日カレーを食べたい!, 納豆嫌い", "@styleset { blah: 1; }"), serializationSame: true },
+
+  { rule: makeRule("bongo, \"super\" bongo, bongo the supreme", "@styleset { blah: 1; }"), invalid: true },
+  { rule: makeRule("--bongo", "@styleset { blah: 1; }"), invalid: true },
+
+  /* ident tests */
+  { rule: _("@styleset { blah: 1; blah: 1; }"), serializationSame: true },
+  { rule: _("@styleset { blah: 1; de-blah: 1; blah: 2; }"), serializationSame: true },
+  { rule: _("@styleset { \\tra-la: 1; }"), serialization: _("@styleset { tra-la: 1; }") },
+  { rule: _("@styleset { b\\lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
+  { rule: _("@styleset { \\62 lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
+  { rule: _("@styleset { \\:blah: 1; }"), serialization: _("@styleset { \\:blah: 1; }") },
+  { rule: _("@styleset { \\;blah: 1; }"), serialization: _("@styleset { \\;blah: 1; }") },
+  { rule: _("@styleset { complex\\20 blah: 1; }"), serialization: _("@styleset { complex\\ blah: 1; }") },
+  { rule: _("@styleset { complex\\ blah: 1; }"), serializationSame: true },
+  { rule: _("@styleset { Håkon: 1; }"), serializationSame: true },
+  { rule: _("@styleset { Åквариум: 1; }"), serializationSame: true },
+  { rule: _("@styleset { \\1f449\\1f4a9\\1f448: 1; }"), serialization: _("@styleset { 👉💩👈: 1; }") },
+  { rule: _("@styleset { 魅力: 1; }"), serializationSame: true },
+  { rule: _("@styleset { 毎日カレーを食べたい!: 1; }"), serializationSame: true },
+  /* from http://en.wikipedia.org/wiki/Metal_umlaut */
+  { rule: _("@styleset { TECHNICIÄNS\\ ÖF\\ SPÅCE\\ SHIP\\ EÅRTH\\ THIS\\ IS\\ YÖÜR\\ CÄPTÅIN\\ SPEÄKING\\ YÖÜR\\ ØÅPTÅIN\\ IS\\ DEA̋D: 1; }"), serializationSame: true },
+
+  { rulesrc: ["@styleset { 123blah: 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { :123blah 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { :123blah: 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { ?123blah: 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { \"blah\": 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { complex blah: 1; }"], serializationNoValueDefn: true },
+  { rulesrc: ["@styleset { complex\\  blah: 1; }"], serializationNoValueDefn: true }
+
+];
+
+// test that invalid value declarations don't affect the parsing of surrounding 
+// declarations.  So before + invalid + after should match the serialization 
+// given in s.
+
+var gSurroundingTests = [
+  // -- invalid, valid ==> valid
+  { before: "", after: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }") },
+
+  // -- valid, invalid ==> valid
+  { before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }", after: "", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }") },
+
+  // -- valid, invalid, valid ==> valid, valid
+  { before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", after: "@character-variant { whatchamacallit-2: 23 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; } @character-variant { whatchamacallit-2: 23 4; }") },
+
+  // -- invalid, valid, invalid ==> valid
+  { between: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }") }
+];
+
+/* strip out just values, along with empty value blocks (e.g. @swash { })*/
+function valuesText(ruletext)
+{
+  var t = ruletext.replace(/@[a-zA-Z0-9\-]+[ \n]*{[ \n]*}/g, "");
+  t = t.replace(/[ \n]+/g, " ");
+  t = t.replace(/^[^{]+{[ \n]*/, "");
+  t = t.replace(/[ \n]*}[^}]*$/, "");
+  t = t.replace(/[ \n]*;/g, ";");
+  return t;
+}
+
+function testParse(rulesrc)
+{
+  var sheet = document.styleSheets[1];
+  var rule = _.apply(this, rulesrc);
+
+  while(sheet.cssRules.length > 0)
+    sheet.deleteRule(0);
+  try {
+    sheet.insertRule(rule, 0);
+  } catch (e) {
+    return e.toString();
+  }
+
+  if (sheet.cssRules.length == 1 && sheet.cssRules[0].type == kFontFeatureValuesRuleType) {
+    return sheet.cssRules[0].cssText.replace(/[ \n]+/g, " ");
+  }
+
+  return "";
+}
+
+function testOneRule(testrule) {
+  var sheet = document.styleSheets[1];
+  var rule;
+
+  if ("rulesrc" in testrule) {
+    rule = _.apply(this, testrule.rulesrc);
+  } else {
+    rule = testrule.rule;
+  }
+
+  var parseErr = false;
+  var expectedErr = false;
+  var invalid = false;
+  if ("invalid" in testrule && testrule.invalid) invalid = true;
+
+  while(sheet.cssRules.length > 0)
+    sheet.deleteRule(0);
+  try {
+    sheet.insertRule(rule, 0);
+  } catch (e) {
+    expectedErr = (e.name == "SyntaxError"
+      && e instanceof DOMException
+      && e.code == DOMException.SYNTAX_ERR
+      && invalid);
+    parseErr = true;
+  }
+
+  test(function() { 
+    assert_true(!parseErr || expectedErr, "unexpected syntax error");
+    if (!parseErr) {
+      assert_equals(sheet.cssRules.length, 1, "bad rule count");
+      assert_equals(sheet.cssRules[0].type, kFontFeatureValuesRuleType, "bad rule type");
+    }
+  }, "basic parse tests - " + rule);
+
+  var sanitizedRule = rule.replace(/[ \n]+/g, " ");
+  if (parseErr) {
+    return;
+  }
+
+  // should result in one @font-feature-values rule constructed
+
+  // serialization matches expectation
+  // -- note: due to inconsistent font family serialization problems,
+  //    only the serialization of the values is tested currently
+
+  var ruleValues = valuesText(rule);
+  var serialized = sheet.cssRules[0].cssText;
+  var serializedValues = valuesText(serialized);
+  var haveSerialization = true;
+
+  if (testrule.serializationSame) {
+    test(function() {
+      assert_equals(serializedValues, ruleValues, "canonical cssText serialization doesn't match");
+    }, "serialization check - " + rule);
+  } else if ("serialization" in testrule) {
+    var s = valuesText(testrule.serialization);
+    test(function() {
+      assert_equals(serializedValues, s, "non-canonical cssText serialization doesn't match - ");
+    }, "serialization check - " + rule);
+  } else if (testrule.serializationNoValueDefn) {
+    test(function() {
+      assert_equals(serializedValues, "", "cssText serialization should have no value defintions - ");
+    }, "no value definitions in serialization - " + rule);
+
+    haveSerialization = false;
+
+    if ("rulesrc" in testrule) {
+      test(function() {
+        var j, rulesrc = testrule.rulesrc;
+    
+        // invalid value definitions shouldn't affect the parsing of valid
+        // definitions before or after an invalid one
+        for (var j = 0; j < gSurroundingTests.length; j++) {
+          var t = gSurroundingTests[j];
+          var srulesrc = [];
+    
+          if ("between" in t) {
+            srulesrc = srulesrc.concat(rulesrc);
+            srulesrc = srulesrc.concat(t.between);
+            srulesrc = srulesrc.concat(rulesrc);
+          } else {
+            if (t.before != "")
+              srulesrc = srulesrc.concat(t.before);
+            srulesrc = srulesrc.concat(rulesrc);
+            if (t.after != "")
+              srulesrc = srulesrc.concat(t.after);
+          }
+    
+          var result = testParse(srulesrc);
+          assert_equals(valuesText(result), valuesText(t.s), "invalid declarations should not affect valid ones - ");
+        }
+      }, "invalid declarations don't affect valid ones - " + rule);
+    }
+  }
+
+  // if serialization non-empty, serialization should round-trip to itself
+  if (haveSerialization) {
+    var roundTripText = testParse([serializedValues]);
+    test(function() {
+      assert_equals(valuesText(roundTripText), serializedValues,
+         "serialization should round-trip to itself - ");
+    }, "serialization round-trip - " + rule);
+  }
+}
+
+function testFontFeatureValuesRuleParsing() {
+  var i;
+  for (i = 0; i < testrules.length; i++) {
+    var testrule = testrules[i];
+    var rule;
+
+    if ("rulesrc" in testrule) {
+      rule = _.apply(this, testrule.rulesrc);
+    } else {
+      rule = testrule.rule;
+    }
+
+    testOneRule(testrule);
+    //test(function() { testOneRule(testrule); }, "parsing " + rule);
+  }
+}
+
+testFontFeatureValuesRuleParsing();
+</script>
+</body></html>