Merge mozilla-central to inbound. a=merge CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 09 Mar 2018 02:20:43 +0200
changeset 462322 16cdea986fe14af9eb4a5b04e5e0769ae1d5a759
parent 462321 bce9970180dc86666599d1af464beab098976fa7 (current diff)
parent 462297 31a33fc619562e5b326585c6864d86832dac5126 (diff)
child 462323 2be3c0d8f1e9ab1fa1a88d2716204b2005c8d6d4
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.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
Merge mozilla-central to inbound. a=merge CLOSED TREE
mobile/android/geckoview/BuildConfig.java.in
--- a/accessible/base/XULMap.h
+++ b/accessible/base/XULMap.h
@@ -39,158 +39,148 @@ XULMAP_TYPE(treecol, XULColumnItemAccess
 XULMAP_TYPE(treecolpicker, XULButtonAccessible)
 XULMAP_TYPE(treecols, XULTreeColumAccessible)
 XULMAP_TYPE(toolbar, XULToolbarAccessible)
 XULMAP_TYPE(toolbarbutton, XULToolbarButtonAccessible)
 XULMAP_TYPE(tooltip, XULTooltipAccessible)
 
 XULMAP(
   colorpicker,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    if (aContent->IsElement() &&
-        aContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                           nsGkAtoms::button, eIgnoreCase)) {
-      return new XULColorPickerAccessible(aContent, aContext->Document());
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                              nsGkAtoms::button, eIgnoreCase)) {
+      return new XULColorPickerAccessible(aElement, aContext->Document());
     }
     return nullptr;
   }
 )
 
 XULMAP(
   label,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    if (aContent->IsElement() &&
-        aContent->AsElement()->ClassList()->Contains(NS_LITERAL_STRING("text-link"))) {
-      return new XULLinkAccessible(aContent, aContext->Document());
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    if (aElement->ClassList()->Contains(NS_LITERAL_STRING("text-link"))) {
+      return new XULLinkAccessible(aElement, aContext->Document());
     }
-    return new XULLabelAccessible(aContent, aContext->Document());
+    return new XULLabelAccessible(aElement, aContext->Document());
   }
 )
 
 XULMAP(
   image,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    if (!aContent->IsElement()) {
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
+      return new XULToolbarButtonAccessible(aElement, aContext->Document());
+    }
+
+    if (aElement->ClassList()->Contains(NS_LITERAL_STRING("colorpickertile"))) {
+      return new XULColorPickerTileAccessible(aElement, aContext->Document());
+    }
+
+    // Don't include nameless images in accessible tree.
+    if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext)) {
       return nullptr;
     }
 
-    if (aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
-      return new XULToolbarButtonAccessible(aContent, aContext->Document());
-    }
-
-    if (aContent->AsElement()->ClassList()->Contains(NS_LITERAL_STRING("colorpickertile"))) {
-      return new XULColorPickerTileAccessible(aContent, aContext->Document());
-    }
-
-    // Don't include nameless images in accessible tree.
-    if (!aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext)) {
-      return nullptr;
-    }
-
-    return new ImageAccessibleWrap(aContent, aContext->Document());
+    return new ImageAccessibleWrap(aElement, aContext->Document());
   }
 )
 
 XULMAP(
   listcell,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
     // Only create cells if there's more than one per row.
-    nsIContent* listItem = aContent->GetParent();
+    nsIContent* listItem = aElement->GetParent();
     if (!listItem) {
       return nullptr;
     }
 
     for (nsIContent* child = listItem->GetFirstChild(); child;
          child = child->GetNextSibling()) {
-      if (child->IsXULElement(nsGkAtoms::listcell) && child != aContent) {
-        return new XULListCellAccessibleWrap(aContent, aContext->Document());
+      if (child->IsXULElement(nsGkAtoms::listcell) && child != aElement) {
+        return new XULListCellAccessibleWrap(aElement, aContext->Document());
       }
     }
 
     return nullptr;
   }
 )
 
 XULMAP(
   menupopup,
-  [](nsIContent* aContent, Accessible* aContext) {
-    return CreateMenupopupAccessible(aContent, aContext);
+  [](Element* aElement, Accessible* aContext) {
+    return CreateMenupopupAccessible(aElement, aContext);
   }
 )
 
 XULMAP(
   panel,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
     static const Element::AttrValuesArray sIgnoreTypeVals[] =
       { &nsGkAtoms::autocomplete_richlistbox, &nsGkAtoms::autocomplete, nullptr };
 
-    if (!aContent->IsElement() ||
-        aContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
-                                               sIgnoreTypeVals, eIgnoreCase) >= 0) {
+    if (aElement->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
+                                  sIgnoreTypeVals, eIgnoreCase) >= 0) {
       return nullptr;
     }
 
-    if (aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                           nsGkAtoms::noautofocus,
-                                           nsGkAtoms::_true, eCaseMatters)) {
-      return new XULAlertAccessible(aContent, aContext->Document());
+    if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
+                              nsGkAtoms::_true, eCaseMatters)) {
+      return new XULAlertAccessible(aElement, aContext->Document());
     }
 
-    return new EnumRoleAccessible<roles::PANE>(aContent, aContext->Document());
+    return new EnumRoleAccessible<roles::PANE>(aElement, aContext->Document());
   }
 )
 
 XULMAP(
   popup,
-  [](nsIContent* aContent, Accessible* aContext) {
-    return CreateMenupopupAccessible(aContent, aContext);
+  [](Element* aElement, Accessible* aContext) {
+    return CreateMenupopupAccessible(aElement, aContext);
   }
 )
 
 XULMAP(
   textbox,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    if (aContent->IsElement() &&
-        aContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                           nsGkAtoms::autocomplete, eIgnoreCase)) {
-      return new XULComboboxAccessible(aContent, aContext->Document());
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                              nsGkAtoms::autocomplete, eIgnoreCase)) {
+      return new XULComboboxAccessible(aElement, aContext->Document());
     }
 
-    return new EnumRoleAccessible<roles::SECTION>(aContent, aContext->Document());
+    return new EnumRoleAccessible<roles::SECTION>(aElement, aContext->Document());
   }
 )
 
 XULMAP(
   thumb,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    if (aContent->IsElement() &&
-        aContent->AsElement()->ClassList()->Contains(NS_LITERAL_STRING("scale-thumb"))) {
-      return new XULThumbAccessible(aContent, aContext->Document());
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    if (aElement->ClassList()->Contains(NS_LITERAL_STRING("scale-thumb"))) {
+      return new XULThumbAccessible(aElement, aContext->Document());
     }
     return nullptr;
   }
 )
 
 XULMAP(
   tree,
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
-    nsIContent* child = nsTreeUtils::GetDescendantChild(aContent,
+  [](Element* aElement, Accessible* aContext) -> Accessible* {
+    nsIContent* child = nsTreeUtils::GetDescendantChild(aElement,
                                                         nsGkAtoms::treechildren);
     if (!child)
       return nullptr;
 
     nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
     if (!treeFrame)
       return nullptr;
 
     RefPtr<nsTreeColumns> treeCols = treeFrame->Columns();
     int32_t count = 0;
     treeCols->GetCount(&count);
 
     // Outline of list accessible.
     if (count == 1) {
-      return new XULTreeAccessible(aContent, aContext->Document(), treeFrame);
+      return new XULTreeAccessible(aElement, aContext->Document(), treeFrame);
     }
 
     // Table or tree table accessible.
-    return new XULTreeGridAccessibleWrap(aContent, aContext->Document(), treeFrame);
+    return new XULTreeGridAccessibleWrap(aElement, aContext->Document(), treeFrame);
   }
 )
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -144,159 +144,153 @@ MustBeAccessible(nsIContent* aContent, D
   return false;
 }
 
 /**
  * Used by XULMap.h to map both menupopup and popup elements
  */
 #ifdef MOZ_XUL
 Accessible*
-CreateMenupopupAccessible(nsIContent* aContent, Accessible* aContext)
+CreateMenupopupAccessible(Element* aElement, Accessible* aContext)
 {
 #ifdef MOZ_ACCESSIBILITY_ATK
     // ATK considers this node to be redundant when within menubars, and it makes menu
     // navigation with assistive technologies more difficult
     // XXX In the future we will should this for consistency across the nsIAccessible
     // implementations on each platform for a consistent scripting environment, but
     // then strip out redundant accessibles in the AccessibleWrap class for each platform.
-    nsIContent *parent = aContent->GetParent();
+    nsIContent *parent = aElement->GetParent();
     if (parent && parent->IsXULElement(nsGkAtoms::menu))
       return nullptr;
 #endif
 
-    return new XULMenupopupAccessible(aContent, aContext->Document());
+    return new XULMenupopupAccessible(aElement, aContext->Document());
 }
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible constructors
 
 static Accessible*
-New_HTMLLink(nsIContent* aContent, Accessible* aContext)
+New_HTMLLink(Element* aElement, Accessible* aContext)
 {
   // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
   // see closed bug 494807.
-  const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent->AsElement());
+  const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
   if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
       roleMapEntry->role != roles::LINK) {
-    return new HyperTextAccessibleWrap(aContent, aContext->Document());
+    return new HyperTextAccessibleWrap(aElement, aContext->Document());
   }
 
-  return new HTMLLinkAccessible(aContent, aContext->Document());
+  return new HTMLLinkAccessible(aElement, aContext->Document());
 }
 
-static Accessible* New_HyperText(nsIContent* aContent, Accessible* aContext)
-  { return new HyperTextAccessibleWrap(aContent, aContext->Document()); }
+static Accessible* New_HyperText(Element* aElement, Accessible* aContext)
+  { return new HyperTextAccessibleWrap(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLFigcaptionAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLFigcaption(Element* aElement, Accessible* aContext)
+  { return new HTMLFigcaptionAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLFigureAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLFigure(Element* aElement, Accessible* aContext)
+  { return new HTMLFigureAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLHeaderOrFooter(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLHeaderOrFooterAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLHeaderOrFooter(Element* aElement, Accessible* aContext)
+  { return new HTMLHeaderOrFooterAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLLegendAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLLegend(Element* aElement, Accessible* aContext)
+  { return new HTMLLegendAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLOption(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLSelectOptionAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLOption(Element* aElement, Accessible* aContext)
+  { return new HTMLSelectOptionAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLOptgroup(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLSelectOptGroupAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLOptgroup(Element* aElement, Accessible* aContext)
+  { return new HTMLSelectOptGroupAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLList(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLListAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLList(Element* aElement, Accessible* aContext)
+  { return new HTMLListAccessible(aElement, aContext->Document()); }
 
 static Accessible*
-New_HTMLListitem(nsIContent* aContent, Accessible* aContext)
+New_HTMLListitem(Element* aElement, Accessible* aContext)
 {
   // If list item is a child of accessible list then create an accessible for
   // it unconditionally by tag name. nsBlockFrame creates the list item
   // accessible for other elements styled as list items.
-  if (aContext->IsList() && aContext->GetContent() == aContent->GetParent())
-    return new HTMLLIAccessible(aContent, aContext->Document());
+  if (aContext->IsList() && aContext->GetContent() == aElement->GetParent())
+    return new HTMLLIAccessible(aElement, aContext->Document());
 
   return nullptr;
 }
 
 static Accessible*
-New_HTMLDefinition(nsIContent* aContent, Accessible* aContext)
+New_HTMLDefinition(Element* aElement, Accessible* aContext)
 {
   if (aContext->IsList())
-    return new HyperTextAccessibleWrap(aContent, aContext->Document());
+    return new HyperTextAccessibleWrap(aElement, aContext->Document());
   return nullptr;
 }
 
-static Accessible* New_HTMLLabel(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLLabelAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLLabel(Element* aElement, Accessible* aContext)
+  { return new HTMLLabelAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLInput(nsIContent* aContent, Accessible* aContext)
+static Accessible* New_HTMLInput(Element* aElement, Accessible* aContext)
 {
-  if (!aContent->IsElement()) {
-    return nullptr;
-  }
-
-  Element* element = aContent->AsElement();
-  if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            nsGkAtoms::checkbox, eIgnoreCase)) {
-    return new HTMLCheckboxAccessible(aContent, aContext->Document());
+    return new HTMLCheckboxAccessible(aElement, aContext->Document());
   }
-  if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            nsGkAtoms::radio, eIgnoreCase)) {
-    return new HTMLRadioButtonAccessible(aContent, aContext->Document());
+    return new HTMLRadioButtonAccessible(aElement, aContext->Document());
   }
-  if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            nsGkAtoms::time, eIgnoreCase)) {
-    return new EnumRoleAccessible<roles::GROUPING>(aContent, aContext->Document());
+    return new EnumRoleAccessible<roles::GROUPING>(aElement, aContext->Document());
   }
-  if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+  if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            nsGkAtoms::date, eIgnoreCase)) {
-    return new EnumRoleAccessible<roles::DATE_EDITOR>(aContent, aContext->Document());
+    return new EnumRoleAccessible<roles::DATE_EDITOR>(aElement, aContext->Document());
   }
   return nullptr;
 }
 
-static Accessible* New_HTMLOutput(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLOutputAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLOutput(Element* aElement, Accessible* aContext)
+  { return new HTMLOutputAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLProgress(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLProgressMeterAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLProgress(Element* aElement, Accessible* aContext)
+  { return new HTMLProgressMeterAccessible(aElement, aContext->Document()); }
 
-static Accessible* New_HTMLSummary(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLSummaryAccessible(aContent, aContext->Document()); }
+static Accessible* New_HTMLSummary(Element* aElement, Accessible* aContext)
+  { return new HTMLSummaryAccessible(aElement, aContext->Document()); }
 
 static Accessible*
-New_HTMLTableAccessible(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLTableAccessible(aContent, aContext->Document()); }
+New_HTMLTableAccessible(Element* aElement, Accessible* aContext)
+  { return new HTMLTableAccessible(aElement, aContext->Document()); }
 
 static Accessible*
-New_HTMLTableRowAccessible(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLTableRowAccessible(aContent, aContext->Document()); }
+New_HTMLTableRowAccessible(Element* aElement, Accessible* aContext)
+  { return new HTMLTableRowAccessible(aElement, aContext->Document()); }
 
 static Accessible*
-New_HTMLTableCellAccessible(nsIContent* aContent, Accessible* aContext)
-  { return new HTMLTableCellAccessible(aContent, aContext->Document()); }
+New_HTMLTableCellAccessible(Element* aElement, Accessible* aContext)
+  { return new HTMLTableCellAccessible(aElement, aContext->Document()); }
 
 static Accessible*
-New_HTMLTableHeaderCell(nsIContent* aContent, Accessible* aContext)
+New_HTMLTableHeaderCell(Element* aElement, Accessible* aContext)
 {
-  if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent())
-    return new HTMLTableHeaderCellAccessibleWrap(aContent, aContext->Document());
+  if (aContext->IsTableRow() && aContext->GetContent() == aElement->GetParent())
+    return new HTMLTableHeaderCellAccessibleWrap(aElement, aContext->Document());
   return nullptr;
 }
 
 static Accessible*
-New_HTMLTableHeaderCellIfScope(nsIContent* aContent, Accessible* aContext)
+New_HTMLTableHeaderCellIfScope(Element* aElement, Accessible* aContext)
 {
-  if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent() &&
-      aContent->IsElement() &&
-      aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope))
-    return new HTMLTableHeaderCellAccessibleWrap(aContent, aContext->Document());
+  if (aContext->IsTableRow() && aContext->GetContent() == aElement->GetParent() &&
+      aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::scope))
+    return new HTMLTableHeaderCellAccessibleWrap(aElement, aContext->Document());
   return nullptr;
 }
 
 /**
  * Cached value of the PREF_ACCESSIBILITY_FORCE_DISABLED preference.
  */
 static int32_t sPlatformDisabledState = 0;
 
@@ -323,18 +317,18 @@ static const HTMLMarkupMapInfo sHTMLMark
 
 #ifdef MOZ_XUL
 #define XULMAP(atom, ...) \
   { &nsGkAtoms::atom, __VA_ARGS__ },
 
 #define XULMAP_TYPE(atom, new_type) \
 XULMAP( \
   atom, \
-  [](nsIContent* aContent, Accessible* aContext) -> Accessible* { \
-    return new new_type(aContent, aContext->Document()); \
+  [](Element* aElement, Accessible* aContext) -> Accessible* { \
+    return new new_type(aElement, aContext->Document()); \
   } \
 )
 
 static const XULMarkupMapInfo sXULMarkupMapList[] = {
   #include "XULMap.h"
 };
 
 #undef XULMAP_TYPE
@@ -1173,17 +1167,17 @@ nsAccessibilityService::CreateAccessible
     if (!isARIATablePart ||
         frame->AccessibleType() == eHTMLTableCellType ||
         frame->AccessibleType() == eHTMLTableRowType ||
         frame->AccessibleType() == eHTMLTableType) {
       // Prefer to use markup to decide if and what kind of accessible to create,
       const HTMLMarkupMapInfo* markupMap =
         mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
       if (markupMap && markupMap->new_func)
-        newAcc = markupMap->new_func(content, aContext);
+        newAcc = markupMap->new_func(content->AsElement(), aContext);
 
       if (!newAcc) // try by frame accessible type.
         newAcc = CreateAccessibleByFrameType(frame, content, aContext);
     }
 
     // In case of ARIA grid or table use table-specific classes if it's not
     // native table based.
     if (isARIATablePart && (!newAcc || newAcc->IsGenericHyperText())) {
@@ -1236,17 +1230,17 @@ nsAccessibilityService::CreateAccessible
       }
     }
 
 #ifdef MOZ_XUL
     // Prefer to use XUL to decide if and what kind of accessible to create.
     const XULMarkupMapInfo* xulMap =
       mXULMarkupMap.Get(content->NodeInfo()->NameAtom());
     if (xulMap && xulMap->new_func) {
-      newAcc = xulMap->new_func(content, aContext);
+      newAcc = xulMap->new_func(content->AsElement(), aContext);
     }
 #endif
 
     // Any XUL box can be used as tabpanel, make sure we create a proper
     // accessible for it.
     if (!newAcc && aContext->IsXULTabpanels() &&
         content->GetParent() == aContext->GetContent()) {
       LayoutFrameType frameType = frame->Type();
@@ -1268,17 +1262,17 @@ nsAccessibilityService::CreateAccessible
       } else if (content->IsSVGElement(nsGkAtoms::svg)) {
         newAcc = new EnumRoleAccessible<roles::DIAGRAM>(content, document);
       }
 
     } else if (content->IsMathMLElement()) {
       const HTMLMarkupMapInfo* markupMap =
         mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
       if (markupMap && markupMap->new_func)
-        newAcc = markupMap->new_func(content, aContext);
+        newAcc = markupMap->new_func(content->AsElement(), aContext);
 
       // Fall back to text when encountering Content MathML.
       if (!newAcc && !content->IsAnyOfMathMLElements(nsGkAtoms::annotation_,
                                                      nsGkAtoms::annotation_xml_,
                                                      nsGkAtoms::mpadded_,
                                                      nsGkAtoms::mphantom_,
                                                      nsGkAtoms::maligngroup_,
                                                      nsGkAtoms::malignmark_,
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -46,17 +46,17 @@ FocusManager* FocusMgr();
 SelectionManager* SelectionMgr();
 
 /**
  * Returns the application accessible.
  */
 ApplicationAccessible* ApplicationAcc();
 xpcAccessibleApplication* XPCApplicationAcc();
 
-typedef Accessible* (New_Accessible)(nsIContent* aContent, Accessible* aContext);
+typedef Accessible* (New_Accessible)(Element* aElement, Accessible* aContext);
 
 struct MarkupAttrInfo {
   nsStaticAtom** name;
   nsStaticAtom** value;
 
   nsStaticAtom** DOMAttrName;
   nsStaticAtom** DOMAttrValue;
 };
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -145,18 +145,17 @@ function Inspector(toolbox) {
 Inspector.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   init: Task.async(function* () {
     // Localize all the nodes containing a data-localization attribute.
     localizeMarkup(this.panelDoc);
 
-    this._cssPropertiesLoaded = initCssProperties(this.toolbox);
-    yield this._cssPropertiesLoaded;
+    this._cssProperties = yield initCssProperties(this.toolbox);
     yield this.target.makeRemote();
     yield this._getPageStyle();
 
     // This may throw if the document is still loading and we are
     // refering to a dead about:blank document
     let defaultSelection = yield this._getDefaultNodeForSelection()
       .catch(this._handleRejectionIfNotDestroyed);
 
@@ -1292,21 +1291,17 @@ Inspector.prototype = {
     if (this.fontinspector) {
       this.fontinspector.destroy();
     }
 
     if (this.animationinspector) {
       this.animationinspector.destroy();
     }
 
-    let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => {
-      if (front) {
-        front.destroy();
-      }
-    });
+    let cssPropertiesDestroyer = this._cssProperties.front.destroy();
 
     this.sidebar.off("select", this.onSidebarSelect);
     let sidebarDestroyer = this.sidebar.destroy();
 
     let ruleViewSideBarDestroyer = this.ruleViewSideBar ?
       this.ruleViewSideBar.destroy() : null;
 
     this.teardownSplitter();
--- a/devtools/client/netmonitor/test/browser_net_timing-division.js
+++ b/devtools/client/netmonitor/test/browser_net_timing-division.js
@@ -1,18 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
- * Tests if timing intervals are divided againts seconds when appropriate.
+ * Tests if timing intervals are divided against seconds when appropriate.
  */
+add_task(async function () {
+  // Show only few columns, so there is enough space
+  // for the waterfall.
+  await pushPref("devtools.netmonitor.visibleColumns",
+    '["status", "contentSize", "waterfall"]');
 
-add_task(async function () {
   let { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 2);
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -45,20 +45,24 @@ SimpleTest.waitForExplicitFinish();
 
 // Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
 // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
 // should be enough.
 requestLongerTimeout(2);
 
 flags.testing = true;
 Services.prefs.setCharPref("devtools.devices.url", TEST_URI_ROOT + "devices.json");
+// The appearance of this notification causes intermittent behavior in some tests that
+// send mouse events, since it causes the content to shift when it appears.
+Services.prefs.setBoolPref("devtools.responsive.reloadNotification.enabled", false);
 
 registerCleanupFunction(async () => {
   flags.testing = false;
   Services.prefs.clearUserPref("devtools.devices.url");
+  Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
   await asyncStorage.removeItem("devtools.devices.url_cache");
   await removeLocalDevices();
 });
 
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
--- a/devtools/client/webaudioeditor/includes.js
+++ b/devtools/client/webaudioeditor/includes.js
@@ -1,17 +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 strict";
 
 const { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const { Task } = require("devtools/shared/task");
-const OldEventEmitter = require("devtools/shared/old-event-emitter");
 const EventEmitter = require("devtools/shared/event-emitter");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
 
 // Use privileged promise in panel documents to prevent having them to freeze
@@ -76,17 +75,17 @@ XPCOMUtils.defineConstant(this, "EVENTS"
 /**
  * The current target and the Web Audio Editor front, set by this tool's host.
  */
 var gToolbox, gTarget, gFront;
 
 /**
  * Convenient way of emitting events from the panel window.
  */
-OldEventEmitter.decorate(this);
+EventEmitter.decorate(this);
 
 /**
  * DOM query helper.
  */
 function $(selector, target = document) { return target.querySelector(selector); }
 function $$(selector, target = document) { return target.querySelectorAll(selector); }
 
 /**
--- a/devtools/client/webaudioeditor/panel.js
+++ b/devtools/client/webaudioeditor/panel.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
-const EventEmitter = require("devtools/shared/old-event-emitter");
+const EventEmitter = require("devtools/shared/event-emitter");
 const { WebAudioFront } = require("devtools/shared/fronts/webaudio");
 
 function WebAudioEditorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   EventEmitter.decorate(this);
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -146,17 +146,17 @@ function getNSpread(front, eventName, co
  * resolves when the graph was rendered with the correct count of
  * nodes and edges.
  */
 function waitForGraphRendered(front, nodeCount, edgeCount, paramEdgeCount) {
   let eventName = front.EVENTS.UI_GRAPH_RENDERED;
   info(`Wait for graph rendered with ${nodeCount} nodes, ${edgeCount} edges`);
 
   return new Promise((resolve, reject) => {
-    front.on(eventName, function onGraphRendered(_, nodes, edges, pEdges) {
+    front.on(eventName, function onGraphRendered(nodes, edges, pEdges) {
       let paramEdgesDone = paramEdgeCount != null ? paramEdgeCount === pEdges : true;
       info(`Got graph rendered with ${nodes} / ${nodeCount} nodes, ` +
            `${edges} / ${edgeCount} edges`);
       if (nodes === nodeCount && edges === edgeCount && paramEdgesDone) {
         front.off(eventName, onGraphRendered);
         resolve();
       }
     });
@@ -202,42 +202,42 @@ function checkVariableView(view, index, 
 }
 
 function modifyVariableView(win, view, index, prop, value) {
   let scope = view.getScopeAtIndex(index);
   let aVar = scope.get(prop);
   scope.expand();
 
   return new Promise((resolve, reject) => {
-    win.on(win.EVENTS.UI_SET_PARAM, handleSetting);
-    win.on(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
+    const onParamSetSuccess = () => {
+      win.off(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
+      resolve();
+    }
+
+    const onParamSetError = () => {
+      win.off(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
+      reject();
+    }
+    win.once(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
+    win.once(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
 
     // Focus and select the variable to begin editing
     win.focus();
     aVar.focus();
     EventUtils.sendKey("RETURN", win);
 
     // Must wait for the scope DOM to be available to receive
     // events
     executeSoon(() => {
       info("Setting " + value + " for " + prop + "....");
       for (let c of (value + "")) {
         EventUtils.synthesizeKey(c, {}, win);
       }
       EventUtils.sendKey("RETURN", win);
     });
-
-    function handleSetting(eventName) {
-      win.off(win.EVENTS.UI_SET_PARAM, handleSetting);
-      win.off(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
-      if (eventName === win.EVENTS.UI_SET_PARAM)
-        resolve();
-      if (eventName === win.EVENTS.UI_SET_PARAM_ERROR)
-        reject();
-    }
   });
 }
 
 function findGraphEdge(win, source, target, param) {
   let selector = ".edgePaths .edgePath[data-source='" + source + "'][data-target='" + target + "']";
   if (param) {
     selector += "[data-param='" + param + "']";
   }
--- a/devtools/client/webaudioeditor/views/automation.js
+++ b/devtools/client/webaudioeditor/views/automation.js
@@ -148,12 +148,12 @@ var AutomationView = {
    */
   _onResize: function () {
     this.graph.refresh();
   },
 
   /**
    * Called when the inspector view determines a node is selected.
    */
-  _onNodeSet: function (_, id) {
+  _onNodeSet: function (id) {
     this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
   }
 };
--- a/devtools/client/webaudioeditor/views/context.js
+++ b/devtools/client/webaudioeditor/views/context.js
@@ -280,17 +280,17 @@ var ContextView = {
     if (~GRAPH_REDRAW_EVENTS.indexOf(eventName)) {
       this.draw();
     }
   },
 
   /**
    * Fired when the devtools theme changes.
    */
-  _onThemeChange: function (eventName, theme) {
+  _onThemeChange: function (theme) {
     let markerColor = MARKER_STYLING[theme];
     let marker = $("#arrowhead");
     if (marker) {
       marker.setAttribute("style", "fill: " + markerColor);
     }
   },
 
   /**
--- a/devtools/client/webaudioeditor/views/inspector.js
+++ b/devtools/client/webaudioeditor/views/inspector.js
@@ -136,17 +136,17 @@ var InspectorView = {
   /**
    * Event handlers
    */
 
   /**
    * Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
    * and calls `setCurrentAudioNode` to scaffold the inspector view.
    */
-  _onNodeSelect: function (_, id) {
+  _onNodeSelect: function (id) {
     this.setCurrentAudioNode(gAudioNodes.get(id));
 
     // Ensure inspector is visible when selecting a new node
     this.show();
   },
 
   _onResize: function () {
     if (this.el.getAttribute("width") < MIN_INSPECTOR_WIDTH) {
--- a/devtools/client/webaudioeditor/views/properties.js
+++ b/devtools/client/webaudioeditor/views/properties.js
@@ -114,17 +114,17 @@ var PropertiesView = {
 
   /**
    * Event handlers
    */
 
   /**
    * Called when the inspector view determines a node is selected.
    */
-  _onNodeSet: function (_, id) {
+  _onNodeSet: function (id) {
     this._setAudioNode(gAudioNodes.get(id));
   },
 
   /**
    * Executed when an audio prop is changed in the UI.
    */
   _onEval: Task.async(function* (variable, value) {
     let ownerScope = variable.ownerView;
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -6,17 +6,16 @@
 loader.lazyRequireGetter(this, "CSS_PROPERTIES_DB",
   "devtools/shared/css/properties-db", true);
 
 loader.lazyRequireGetter(this, "cssColors",
   "devtools/shared/css/color-db", true);
 
 const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
-const { Task } = require("devtools/shared/task");
 
 /**
  * Build up a regular expression that matches a CSS variable token. This is an
  * ident token that starts with two dashes "--".
  *
  * https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
  */
 var NON_ASCII = "[^\\x00-\\x7F]";
@@ -223,38 +222,38 @@ CssProperties.prototype = {
  * is potentially async and should be handled up-front when the tool is created.
  *
  * The front is returned only with this function so that it can be destroyed
  * once the toolbox is destroyed.
  *
  * @param {Toolbox} The current toolbox.
  * @returns {Promise} Resolves to {cssProperties, cssPropertiesFront}.
  */
-const initCssProperties = Task.async(function* (toolbox) {
+const initCssProperties = async function (toolbox) {
   const client = toolbox.target.client;
   if (cachedCssProperties.has(client)) {
     return cachedCssProperties.get(client);
   }
 
   let db, front;
 
   // Get the list dynamically if the cssProperties actor exists.
   if (toolbox.target.hasActor("cssProperties")) {
     front = CssPropertiesFront(client, toolbox.target.form);
-    db = yield front.getCSSDatabase();
+    db = await front.getCSSDatabase();
   } else {
     // The target does not support this actor, so require a static list of supported
     // properties.
     db = CSS_PROPERTIES_DB;
   }
 
   const cssProperties = new CssProperties(normalizeCssData(db));
   cachedCssProperties.set(client, {cssProperties, front});
   return {cssProperties, front};
-});
+};
 
 /**
  * Synchronously get a cached and initialized CssProperties.
  *
  * @param {Toolbox} The current toolbox.
  * @returns {CssProperties}
  */
 function getCssProperties(toolbox) {
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -14,16 +14,17 @@
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSRendering.h"
 #include "nsIFrame.h"
 #include "nsIFrameInlines.h"
 #include "nsIPresShellInlines.h"
 #include "nsPlaceholderFrame.h"
 #include "nsStyleChangeList.h"
 #include "nsStyleUtil.h"
+#include "nsTransitionManager.h"
 #include "StickyScrollContainer.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ViewportFrame.h"
 #include "SVGObserverUtils.h"
 #include "SVGTextFrame.h"
 #include "ActiveLayerTracker.h"
 #include "nsSVGIntegrationUtils.h"
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -25,17 +25,16 @@ GARBAGE_DIRS += classes db jars res sync
 generated_resources := \
   AndroidManifest.xml \
   res/raw/browsersearch.json \
   res/raw/suggestedsites.json \
   res/values/strings.xml \
   $(NULL)
 
 generated_files := \
-  ../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java \
   AndroidManifest.xml \
   generated/preprocessed/org/mozilla/gecko/AdjustConstants.java \
   generated/preprocessed/org/mozilla/gecko/AppConstants.java \
   generated/preprocessed/org/mozilla/gecko/MmaConstants.java \
   $(NULL)
 
 gradle_dir := $(topobjdir)/gradle/build/mobile/android
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -107,25 +107,21 @@ with Files('../app/src/*/res/menu/*activ
     BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
 
 with Files('../app/src/*/res/menu/browsersearch_contextmenu.xml'):
     BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
 
 DIRS += ['locales']
 
 GENERATED_FILES += [
-    '../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java',
     'AndroidManifest.xml',
     'generated/preprocessed/org/mozilla/gecko/AdjustConstants.java',
     'generated/preprocessed/org/mozilla/gecko/AppConstants.java',
     'generated/preprocessed/org/mozilla/gecko/MmaConstants.java',
 ]
-w = GENERATED_FILES['../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java']
-w.script = 'generate_build_config.py:generate_java'
-w.inputs += ['../geckoview/BuildConfig.java.in']
 x = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AdjustConstants.java']
 x.script = 'generate_build_config.py:generate_java'
 x.inputs += ['AdjustConstants.java.in']
 y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AppConstants.java']
 y.script = 'generate_build_config.py:generate_java'
 y.inputs += ['AppConstants.java.in']
 y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/MmaConstants.java']
 y.script = 'generate_build_config.py:generate_java'
deleted file mode 100644
--- a/mobile/android/geckoview/BuildConfig.java.in
+++ /dev/null
@@ -1,106 +0,0 @@
-//#filter substitution
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.geckoview;
-
-import android.os.Build;
-
-/**
- * A collection of constants that pertain to the build state of the GeckoView
- * library, sourced from build-time definitions.  This is a partial Java-side
- * substitute for nsIXULAppInfo, amongst other things.
- *
- * See also SysInfo.java, which includes some of the values available from
- * nsSystemInfo inside Gecko.
- */
-public class BuildConfig {
-    public static final String GRE_MILESTONE = "@GRE_MILESTONE@";
-
-    public static final String MOZ_APP_ABI = "@MOZ_APP_ABI@";
-    public static final String MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@";
-
-    // For the benefit of future archaeologists:
-    // GRE_BUILDID is exactly the same as MOZ_APP_BUILDID unless you're running
-    // on XULRunner, which is never the case on Android.
-    public static final String MOZ_APP_BUILDID = "@MOZ_BUILDID@";
-    public static final String MOZ_APP_ID = "@MOZ_APP_ID@";
-    public static final String MOZ_APP_NAME = "@MOZ_APP_NAME@";
-    public static final String MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@";
-    public static final String MOZ_APP_VERSION = "@MOZ_APP_VERSION@";
-    public static final String MOZ_APP_DISPLAYNAME = "@MOZ_APP_DISPLAYNAME@";
-    public static final String MOZ_APP_UA_NAME = "@MOZ_APP_UA_NAME@";
-
-    // MOZILLA_VERSION is already quoted when it gets substituted in. If we
-    // add additional quotes we end up with ""x.y"", which is a syntax error.
-    public static final String MOZILLA_VERSION = @MOZILLA_VERSION@;
-    public static final String OMNIJAR_NAME = "@OMNIJAR_NAME@";
-
-    public static final String USER_AGENT_GECKOVIEW_MOBILE = "Mozilla/5.0 (Android " +
-        Build.VERSION.RELEASE + "; Mobile; rv:" +
-        MOZ_APP_VERSION + ") Gecko/" +
-        MOZ_APP_VERSION + " GeckoView/" +
-        MOZ_APP_VERSION;
-
-    public static final String USER_AGENT_GECKOVIEW_TABLET = "Mozilla/5.0 (Android " +
-        Build.VERSION.RELEASE + "; Tablet; rv:" +
-        MOZ_APP_VERSION + ") Gecko/" +
-        MOZ_APP_VERSION + " GeckoView/" +
-        MOZ_APP_VERSION;
-
-    /**
-     * Target CPU architecture: "armeabi-v7a", "arm64-v8a", "x86", "mips", ..
-     */
-    public static final String ANDROID_CPU_ARCH = "@ANDROID_CPU_ARCH@";
-
-    public static final String MOZ_UPDATE_CHANNEL = "@MOZ_UPDATE_CHANNEL@";
-
-    public static final int MIN_SDK_VERSION = @MOZ_ANDROID_MIN_SDK_VERSION@;
-
-    // Is the underlying compiled C/C++ code compiled with --enable-debug?
-    public static final boolean DEBUG_BUILD =
-//#ifdef MOZ_DEBUG
-    true;
-//#else
-    false;
-//#endif
-
-    // See this wiki page for more details about channel specific build defines:
-    // https://wiki.mozilla.org/Platform/Channel-specific_build_defines
-    // This makes no sense for GeckoView and should be removed as soon as possible.
-    public static final boolean RELEASE_OR_BETA =
-//#ifdef RELEASE_OR_BETA
-    true;
-//#else
-    false;
-//#endif
-
-    // This makes no sense for GeckoView and should be removed as soon as possible.
-    public static final boolean NIGHTLY_BUILD =
-//#ifdef NIGHTLY_BUILD
-    true;
-//#else
-    false;
-//#endif
-
-    // This makes no sense for GeckoView and should be removed as soon as possible.
-    public static final boolean MOZ_CRASHREPORTER =
-//#ifdef MOZ_CRASHREPORTER
-    true;
-//#else
-    false;
-//#endif
-
-    // Official corresponds, roughly, to whether this build is performed on
-    // Mozilla's continuous integration infrastructure. You should disable
-    // developer-only functionality when this flag is set.
-    // This makes no sense for GeckoView and should be removed as soon as possible.
-    public static final boolean MOZILLA_OFFICIAL =
-//#ifdef MOZILLA_OFFICIAL
-    true;
-//#else
-    false;
-//#endif
-}
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -48,18 +48,16 @@ android {
         manifestPlaceholders = project.ext.manifestPlaceholders
 
         versionCode computeVersionCode()
         versionName "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
         consumerProguardFiles 'proguard-rules.txt'
 
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 
-        // TODO: ensure these fields always agree with mobile/android/geckoview/BuildConfig.java.in,
-        // either by diffing the processed files or by generating the output from a single source.
         buildConfigField 'String', "GRE_MILESTONE", "\"${mozconfig.substs.GRE_MILESTONE}\""
         // This should really come from the included binaries, but that's not easy.
         buildConfigField 'String', "MOZ_APP_ABI", mozconfig.substs['COMPILE_ENVIRONMENT'] ? "\"${ mozconfig.substs.TARGET_XPCOM_ABI}\"" : '"arm-eabi-gcc3"';
         buildConfigField 'String', "MOZ_APP_BASENAME", "\"${mozconfig.substs.MOZ_APP_BASENAME}\"";
 
         // For the benefit of future archaeologists:
         // GRE_BUILDID is exactly the same as MOZ_APP_BUILDID unless you're running
         // on XULRunner, which is never the case on Android.
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -24,20 +24,16 @@ from mozbuild.frontend.context import (
     AbsolutePath,
     Path,
     RenamedSourcePath,
     SourcePath,
     ObjDirPath,
 )
 from .common import CommonBackend
 from ..frontend.data import (
-    AndroidAssetsDirs,
-    AndroidResDirs,
-    AndroidExtraResDirs,
-    AndroidExtraPackages,
     BaseLibrary,
     BaseProgram,
     ChromeManifestEntry,
     ComputedFlags,
     ConfigFileSubstitution,
     ContextDerived,
     ContextWrapped,
     Defines,
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -1128,69 +1128,16 @@ class GeneratedFile(ContextDerived):
             '.inc',
             '.py',
             '.rs',
             'new', # 'new' is an output from make-stl-wrappers.py
         )
         self.required_for_compile = any(f.endswith(suffixes) for f in self.outputs)
 
 
-class AndroidResDirs(ContextDerived):
-    """Represents Android resource directories."""
-
-    __slots__ = (
-        'paths',
-    )
-
-    def __init__(self, context, paths):
-        ContextDerived.__init__(self, context)
-        self.paths = paths
-
-
-class AndroidAssetsDirs(ContextDerived):
-    """Represents Android assets directories."""
-
-    __slots__ = (
-        'paths',
-    )
-
-    def __init__(self, context, paths):
-        ContextDerived.__init__(self, context)
-        self.paths = paths
-
-
-class AndroidExtraResDirs(ContextDerived):
-    """Represents Android extra resource directories.
-
-    Extra resources are resources provided by libraries and including in a
-    packaged APK, but not otherwise redistributed.  In practice, this means
-    resources included in Fennec but not in GeckoView.
-    """
-
-    __slots__ = (
-        'paths',
-    )
-
-    def __init__(self, context, paths):
-        ContextDerived.__init__(self, context)
-        self.paths = paths
-
-
-class AndroidExtraPackages(ContextDerived):
-    """Represents Android extra packages."""
-
-    __slots__ = (
-        'packages',
-    )
-
-    def __init__(self, context, packages):
-        ContextDerived.__init__(self, context)
-        self.packages = packages
-
-
 class ChromeManifestEntry(ContextDerived):
     """Represents a chrome.manifest entry."""
 
     __slots__ = (
         'path',
         'entry',
     )
 
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -18,20 +18,16 @@ from mozbuild.util import (
     OrderedDefaultDict,
 )
 
 import mozpack.path as mozpath
 import mozinfo
 import pytoml
 
 from .data import (
-    AndroidAssetsDirs,
-    AndroidExtraPackages,
-    AndroidExtraResDirs,
-    AndroidResDirs,
     BaseRustProgram,
     BaseSources,
     ChromeManifestEntry,
     ComputedFlags,
     ConfigFileSubstitution,
     ContextWrapped,
     Defines,
     DirectoryTraversal,
@@ -1375,28 +1371,29 @@ class TreeMetadataEmitter(LoggingMixin):
                     if not os.path.exists(script):
                         raise SandboxValidationError(
                             'Script for generating %s does not exist: %s'
                             % (f, script), context)
                     if os.path.splitext(script)[1] != '.py':
                         raise SandboxValidationError(
                             'Script for generating %s does not end in .py: %s'
                             % (f, script), context)
-
-                    for i in flags.inputs:
-                        p = Path(context, i)
-                        if (isinstance(p, SourcePath) and
-                                not os.path.exists(p.full_path)):
-                            raise SandboxValidationError(
-                                'Input for generating %s does not exist: %s'
-                                % (f, p.full_path), context)
-                        inputs.append(p)
                 else:
                     script = None
                     method = None
+
+                for i in flags.inputs:
+                    p = Path(context, i)
+                    if (isinstance(p, SourcePath) and
+                            not os.path.exists(p.full_path)):
+                        raise SandboxValidationError(
+                            'Input for generating %s does not exist: %s'
+                            % (f, p.full_path), context)
+                    inputs.append(p)
+
                 yield GeneratedFile(context, script, method, outputs, inputs,
                                     flags.flags, localized=localized)
 
     def _process_test_manifests(self, context):
         for prefix, info in TEST_MANIFESTS.items():
             for path, manifest in context.get('%s_MANIFESTS' % prefix, []):
                 for obj in self._process_test_manifest(context, info, path, manifest):
                     yield obj
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -9,17 +9,16 @@ import unittest
 
 from mozunit import main
 
 from mozbuild.frontend.context import (
     ObjDirPath,
     Path,
 )
 from mozbuild.frontend.data import (
-    AndroidResDirs,
     ChromeManifestEntry,
     ComputedFlags,
     ConfigFileSubstitution,
     Defines,
     DirectoryTraversal,
     Exports,
     FinalTargetPreprocessedFiles,
     GeneratedFile,
--- a/python/mozbuild/mozbuild/vendor_rust.py
+++ b/python/mozbuild/mozbuild/vendor_rust.py
@@ -259,18 +259,16 @@ license file's hash.
         cargo = self._ensure_cargo()
         if not cargo:
             return
 
         relative_vendor_dir = 'third_party/rust'
         vendor_dir = mozpath.join(self.topsrcdir, relative_vendor_dir)
         self.log(logging.INFO, 'rm_vendor_dir', {}, 'rm -rf %s' % vendor_dir)
         mozfile.remove(vendor_dir)
-        # Once we require a new enough cargo to switch to workspaces, we can
-        # just do this once on the workspace root crate.
 
         # We use check_call instead of mozprocess to ensure errors are displayed.
         # We do an |update -p| here to regenerate the Cargo.lock file with minimal changes. See bug 1324462
         subprocess.check_call([cargo, 'update', '-p', 'gkrust'], cwd=self.topsrcdir)
 
         subprocess.check_call([cargo, 'vendor', '--quiet', '--no-delete', '--sync', 'Cargo.lock'] + [vendor_dir], cwd=self.topsrcdir)
 
         if not self._check_licenses(vendor_dir):
--- a/security/sandbox/linux/launch/SandboxLaunch.cpp
+++ b/security/sandbox/linux/launch/SandboxLaunch.cpp
@@ -236,46 +236,46 @@ SandboxLaunchPrepare(GeckoProcessType aT
 
   // At this point, we know we'll be using sandboxing; generic
   // sandboxing support goes here.  The MOZ_SANDBOXED env var tells
   // the child process whether this is the case.
   aOptions->env_map["MOZ_SANDBOXED"] = "1";
   PreloadSandboxLib(&aOptions->env_map);
   AttachSandboxReporter(&aOptions->fds_to_remap);
 
+  bool canChroot = false;
+  int flags = 0;
+
+  if (aType == GeckoProcessType_Content && level >= 1) {
+      static const bool needSysV = ContentNeedsSysVIPC();
+      if (needSysV) {
+        // Tell the child process so it can adjust its seccomp-bpf
+        // policy.
+        aOptions->env_map["MOZ_SANDBOX_ALLOW_SYSV"] = "1";
+      } else {
+        flags |= CLONE_NEWIPC;
+      }
+  }
+
   // Anything below this requires unprivileged user namespaces.
   if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
     return;
   }
 
-  bool canChroot = false;
-  int flags = 0;
-
   switch (aType) {
 #ifdef MOZ_GMP_SANDBOX
   case GeckoProcessType_GMPlugin:
     if (level >= 1) {
       canChroot = true;
       flags |= CLONE_NEWNET | CLONE_NEWIPC;
     }
     break;
 #endif
 #ifdef MOZ_CONTENT_SANDBOX
   case GeckoProcessType_Content:
-    if (level >= 1) {
-      static const bool needSysV = ContentNeedsSysVIPC();
-      if (needSysV) {
-        // Tell the child process so it can adjust its seccomp-bpf
-        // policy.
-        aOptions->env_map["MOZ_SANDBOX_ALLOW_SYSV"] = "1";
-      } else {
-        flags |= CLONE_NEWIPC;
-      }
-    }
-
     if (level >= 4) {
       canChroot = true;
       // Unshare network namespace if allowed by graphics; see
       // function definition above for details.  (The display
       // local-ness is cached because it won't change.)
       static const bool isDisplayLocal = IsDisplayLocal();
       if (isDisplayLocal) {
         flags |= CLONE_NEWNET;
--- a/services/sync/tps/extensions/tps/resource/modules/formautofill.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/formautofill.jsm
@@ -23,42 +23,48 @@ class FormAutofillBase {
     if ("changes" in props) {
       this.updateProps = props.changes;
     }
     for (const field of this._fields) {
       this.props[field] = (field in props) ? props[field] : null;
     }
   }
 
-  get storage() {
+  async getStorage() {
+    await formAutofillStorage.initialize();
     return formAutofillStorage[this._subStorageName];
   }
 
-  Create() {
-    this.storage.add(this.props);
+  async Create() {
+    const storage = await this.getStorage();
+    storage.add(this.props);
   }
 
-  Find() {
-    return this.storage._data.find(entry =>
+  async Find() {
+    const storage = await this.getStorage();
+    return storage._data.find(entry =>
       this._fields.every(field => entry[field] === this.props[field])
     );
   }
 
-  Update() {
-    const {guid} = this.Find();
-    this.storage.update(guid, this.updateProps, true);
+  async Update() {
+    const storage = await this.getStorage();
+    const {guid} = await this.Find();
+    storage.update(guid, this.updateProps, true);
   }
 
-  Remove() {
-    const {guid} = this.Find();
-    this.storage.remove(guid);
+  async Remove() {
+    const storage = await this.getStorage();
+    const {guid} = await this.Find();
+    storage.remove(guid);
   }
 }
 
-function DumpStorage(subStorageName) {
+async function DumpStorage(subStorageName) {
+  await formAutofillStorage.initialize();
   Logger.logInfo(`\ndumping ${subStorageName} list\n`, true);
   const entries = formAutofillStorage[subStorageName]._data;
   for (const entry of entries) {
     Logger.logInfo(JSON.stringify(entry), true);
   }
   Logger.logInfo(`\n\nend ${subStorageName} list\n`, true);
 }
 
@@ -77,35 +83,36 @@ const ADDRESS_FIELDS = [
 ];
 
 class Address extends FormAutofillBase {
   constructor(props) {
     super(props, "addresses", ADDRESS_FIELDS);
   }
 }
 
-function DumpAddresses() {
-  DumpStorage("addresses");
+async function DumpAddresses() {
+  await DumpStorage("addresses");
 }
 
 const CREDIT_CARD_FIELDS = [
   "cc-name",
   "cc-number",
   "cc-exp-month",
   "cc-exp-year",
 ];
 
 class CreditCard extends FormAutofillBase {
   constructor(props) {
     super(props, "creditCards", CREDIT_CARD_FIELDS);
   }
 
-  Find() {
-    return this.storage._data.find(entry => {
+  async Find() {
+    const storage = await this.getStorage();
+    return storage._data.find(entry => {
       entry["cc-number"] = MasterPassword.decryptSync(entry["cc-number-encrypted"]);
       return this._fields.every(field => entry[field] === this.props[field]);
     });
   }
 }
 
-function DumpCreditCards() {
-  DumpStorage("creditCards");
+async function DumpCreditCards() {
+  await DumpStorage("creditCards");
 }
--- a/services/sync/tps/extensions/tps/resource/tps.jsm
+++ b/services/sync/tps/extensions/tps/resource/tps.jsm
@@ -580,76 +580,76 @@ var TPS = {
   async HandleAddresses(addresses, action) {
     try {
       for (let address of addresses) {
         Logger.logInfo("executing action " + action.toUpperCase() +
                       " on address " + JSON.stringify(address));
         let addressOb = new Address(address);
         switch (action) {
           case ACTION_ADD:
-            addressOb.Create();
+            await addressOb.Create();
             break;
           case ACTION_MODIFY:
-            addressOb.Update();
+            await addressOb.Update();
             break;
           case ACTION_VERIFY:
-            Logger.AssertTrue(addressOb.Find(), "address not found");
+            Logger.AssertTrue(await addressOb.Find(), "address not found");
             break;
           case ACTION_VERIFY_NOT:
-            Logger.AssertTrue(!addressOb.Find(),
+            Logger.AssertTrue(!await addressOb.Find(),
               "address found, but it shouldn't exist");
             break;
           case ACTION_DELETE:
-            Logger.AssertTrue(addressOb.Find(), "address not found");
-            addressOb.Remove();
+            Logger.AssertTrue(await addressOb.Find(), "address not found");
+            await addressOb.Remove();
             break;
           default:
             Logger.AssertTrue(false, "invalid action: " + action);
         }
       }
       Logger.logPass("executing action " + action.toUpperCase() +
                      " on addresses");
     } catch (e) {
-      DumpAddresses();
+      await DumpAddresses();
       throw (e);
     }
   },
 
   async HandleCreditCards(creditCards, action) {
     try {
       for (let creditCard of creditCards) {
         Logger.logInfo("executing action " + action.toUpperCase() +
                       " on creditCard " + JSON.stringify(creditCard));
         let creditCardOb = new CreditCard(creditCard);
         switch (action) {
           case ACTION_ADD:
-            creditCardOb.Create();
+            await creditCardOb.Create();
             break;
           case ACTION_MODIFY:
-            creditCardOb.Update();
+            await creditCardOb.Update();
             break;
           case ACTION_VERIFY:
-            Logger.AssertTrue(creditCardOb.Find(), "creditCard not found");
+            Logger.AssertTrue(await creditCardOb.Find(), "creditCard not found");
             break;
           case ACTION_VERIFY_NOT:
-            Logger.AssertTrue(!creditCardOb.Find(),
+            Logger.AssertTrue(!await creditCardOb.Find(),
               "creditCard found, but it shouldn't exist");
             break;
           case ACTION_DELETE:
-            Logger.AssertTrue(creditCardOb.Find(), "creditCard not found");
-            creditCardOb.Remove();
+            Logger.AssertTrue(await creditCardOb.Find(), "creditCard not found");
+            await creditCardOb.Remove();
             break;
           default:
             Logger.AssertTrue(false, "invalid action: " + action);
         }
       }
       Logger.logPass("executing action " + action.toUpperCase() +
                      " on creditCards");
     } catch (e) {
-      DumpCreditCards();
+      await DumpCreditCards();
       throw (e);
     }
   },
 
   async Cleanup() {
     try {
       await this.WipeServer();
     } catch (ex) {
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -611,17 +611,18 @@ impl<'le> GeckoElement<'le> {
     }
 
     #[inline]
     fn xbl_binding(&self) -> Option<GeckoXBLBinding<'le>> {
         if !self.may_be_in_binding_manager() {
             return None;
         }
 
-        unsafe { bindings::Gecko_GetXBLBinding(self.0).map(GeckoXBLBinding) }
+        let slots = self.extended_slots()?;
+        unsafe { slots.mXBLBinding.mRawPtr.as_ref().map(GeckoXBLBinding) }
     }
 
     #[inline]
     fn xbl_binding_with_content(&self) -> Option<GeckoXBLBinding<'le>> {
         self.xbl_binding().and_then(|b| b.binding_with_content())
     }
 
     #[inline]
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -58,17 +58,16 @@ pub mod system_colors {
                           TextSelectForegroundCustom TextSelectBackgroundDisabled TextSelectBackgroundAttention
                           TextHighlightBackground TextHighlightForeground IMERawInputBackground
                           IMERawInputForeground IMERawInputUnderline IMESelectedRawTextBackground
                           IMESelectedRawTextForeground IMESelectedRawTextUnderline
                           IMEConvertedTextBackground IMEConvertedTextForeground IMEConvertedTextUnderline
                           IMESelectedConvertedTextBackground IMESelectedConvertedTextForeground
                           IMESelectedConvertedTextUnderline SpellCheckerUnderline""".split()
     %>
-    use cssparser::Parser;
     use gecko_bindings::bindings::Gecko_GetLookAndFeelSystemColor;
     use gecko_bindings::structs::root::mozilla::LookAndFeel_ColorID;
     use std::fmt::{self, Write};
     use style_traits::{CssWriter, ToCss};
     use values::computed::{Context, ToComputedValue};
 
     pub type SystemColor = LookAndFeel_ColorID;
 
@@ -104,23 +103,22 @@ pub mod system_colors {
 
         #[inline]
         fn from_computed_value(_: &Self::ComputedValue) -> Self {
             unreachable!()
         }
     }
 
     impl SystemColor {
-        pub fn parse<'i, 't>(input: &mut Parser<'i, 't>,) -> Result<Self, ()> {
+        pub fn from_ident<'i, 't>(ident: &str) -> Result<Self, ()> {
             ascii_case_insensitive_phf_map! {
                 color_name -> SystemColor = {
                     % for color in system_colors:
                         "${color}" => LookAndFeel_ColorID::eColorID_${to_rust_ident(color)},
                     % endfor
                 }
             }
 
-            let ident = input.expect_ident().map_err(|_| ())?;
             color_name(ident).cloned().ok_or(())
         }
     }
 }
 % endif
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -10,41 +10,39 @@
                          spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position",
                          servo_restyle_damage="rebuild_and_reflow")}
 
 // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
 //
 //     decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
 //     upper-roman
 //
-// TODO(bholley): Missing quite a few gecko properties here as well.
-//
-// In gecko, {upper,lower}-{roman,alpha} are implemented as @counter-styles in the
-// UA, however they can also be set from pres attrs. When @counter-style is supported
-// we may need to look into this and handle these differently.
-//
 // [1]: http://dev.w3.org/csswg/css-counter-styles/
 % if product == "servo":
-    ${helpers.single_keyword("list-style-type", """
-        disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
+    ${helpers.single_keyword(
+        "list-style-type",
+        """disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
         arabic-indic bengali cambodian cjk-decimal devanagari gujarati gurmukhi kannada khmer lao
         malayalam mongolian myanmar oriya persian telugu thai tibetan cjk-earthly-branch
         cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana katakana-iroha""",
         animation_value_type="discrete",
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
-        servo_restyle_damage="rebuild_and_reflow")}
+        servo_restyle_damage="rebuild_and_reflow",
+    )}
 % else:
-    ${helpers.predefined_type("list-style-type",
-                              "ListStyleType",
-                              "computed::ListStyleType::disc()",
-                              initial_specified_value="specified::ListStyleType::disc()",
-                              animation_value_type="discrete",
-                              boxed=True,
-                              spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
-                              servo_restyle_damage="rebuild_and_reflow")}
+    ${helpers.predefined_type(
+        "list-style-type",
+        "ListStyleType",
+        "computed::ListStyleType::disc()",
+        initial_specified_value="specified::ListStyleType::disc()",
+        animation_value_type="discrete",
+        boxed=True,
+        spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
+        servo_restyle_damage="rebuild_and_reflow",
+    )}
 % endif
 
 ${helpers.predefined_type("list-style-image",
                           "ListStyleImage",
                           initial_value="specified::ListStyleImage::none()",
                           initial_specified_value="specified::ListStyleImage::none()",
                           animation_value_type="discrete",
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -57,19 +57,19 @@ pub enum ShapeSource<BasicShape, Referen
     #[animation(error)]
     None,
 }
 
 #[allow(missing_docs)]
 #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum BasicShape<H, V, LengthOrPercentage> {
-    Inset(InsetRect<LengthOrPercentage>),
-    Circle(Circle<H, V, LengthOrPercentage>),
-    Ellipse(Ellipse<H, V, LengthOrPercentage>),
+    Inset(#[css(field_bound)] InsetRect<LengthOrPercentage>),
+    Circle(#[css(field_bound)] Circle<H, V, LengthOrPercentage>),
+    Ellipse(#[css(field_bound)] Ellipse<H, V, LengthOrPercentage>),
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
 #[allow(missing_docs)]
 #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct InsetRect<LengthOrPercentage> {
     pub rect: Rect<LengthOrPercentage>,
--- a/servo/components/style/values/generics/border.rs
+++ b/servo/components/style/values/generics/border.rs
@@ -27,29 +27,29 @@ pub struct BorderImageSlice<NumberOrPerc
     pub offsets: Rect<NumberOrPercentage>,
     /// Whether to fill the middle part.
     pub fill: bool,
 }
 
 /// A generic value for the `border-*-radius` longhand properties.
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
 #[derive(MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
-pub struct BorderCornerRadius<L>(pub Size<L>);
+pub struct BorderCornerRadius<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderCornerRadius<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderCornerRadius(Size::new(w, h))
     }
 }
 
 /// A generic value for the `border-spacing` property.
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
 #[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
-pub struct BorderSpacing<L>(pub Size<L>);
+pub struct BorderSpacing<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderSpacing<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderSpacing(Size::new(w, h))
     }
 }
 
--- a/servo/components/style/values/generics/font.rs
+++ b/servo/components/style/values/generics/font.rs
@@ -63,18 +63,19 @@ where
         if self.tag != other.tag {
             return Err(());
         }
         self.value.compute_squared_distance(&other.value)
     }
 }
 
 /// A value both for font-variation-settings and font-feature-settings.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
-pub struct FontSettings<T>(pub Box<[T]>);
+#[css(comma)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
 
 impl<T> FontSettings<T> {
     /// Default value of font settings as `normal`.
     #[inline]
     pub fn normal() -> Self {
         FontSettings(vec![].into_boxed_slice())
     }
 }
@@ -91,40 +92,16 @@ impl<T: Parse> Parse for FontSettings<T>
         }
 
         Ok(FontSettings(
             input.parse_comma_separated(|i| T::parse(context, i))?.into_boxed_slice()
         ))
     }
 }
 
-impl<T: ToCss> ToCss for FontSettings<T> {
-    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
-    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        if self.0.is_empty() {
-            return dest.write_str("normal");
-        }
-
-        let mut first = true;
-        for item in self.0.iter() {
-            if !first {
-                dest.write_str(", ")?;
-            }
-            first = false;
-            item.to_css(dest)?;
-        }
-
-        Ok(())
-    }
-}
-
 /// A font four-character tag, represented as a u32 for convenience.
 ///
 /// See:
 ///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
 ///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
 ///
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct FontTag(pub u32);
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -1,18 +1,17 @@
 /* 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/. */
 
 //! Generic types for CSS values in SVG
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{ParseError, StyleParseErrorKind};
 use values::{Either, None_};
 use values::computed::NumberOrPercentage;
 use values::computed::length::LengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// An SVG paint value
 ///
 /// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
@@ -194,51 +193,26 @@ impl <LengthOrPercentageType: Parse, Num
 pub enum SVGLength<LengthType> {
     /// `<length> | <percentage> | <number>`
     Length(LengthType),
     /// `context-value`
     ContextValue,
 }
 
 /// Generic value for stroke-dasharray.
-#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToComputedValue)]
+#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
+#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
 pub enum SVGStrokeDashArray<LengthType> {
     /// `[ <length> | <percentage> | <number> ]#`
-    Values(Vec<LengthType>),
+    #[css(comma)]
+    Values(#[css(if_empty = "none", iterable)] Vec<LengthType>),
     /// `context-value`
     ContextValue,
 }
 
-impl<LengthType> ToCss for SVGStrokeDashArray<LengthType> where LengthType: ToCss {
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        match self {
-            &SVGStrokeDashArray::Values(ref values) => {
-                let mut iter = values.iter();
-                if let Some(first) = iter.next() {
-                    first.to_css(dest)?;
-                    for item in iter {
-                        dest.write_str(", ")?;
-                        item.to_css(dest)?;
-                    }
-                    Ok(())
-                } else {
-                    dest.write_str("none")
-                }
-            }
-            &SVGStrokeDashArray::ContextValue => {
-                dest.write_str("context-value")
-            }
-        }
-    }
-}
-
 /// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
 /// addition to opacity value.
 #[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
 #[derive(PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
 pub enum SVGOpacity<OpacityType> {
     /// `<opacity-value>`
     Opacity(OpacityType),
     /// `context-fill-opacity`
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -2,18 +2,16 @@
  * 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/. */
 
 //! Generic types for CSS values that are related to transformations.
 
 use app_units::Au;
 use euclid::{self, Rect, Transform3D};
 use num_traits::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
 use values::{computed, CSSFloat};
 use values::computed::length::Length as ComputedLength;
 use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
 use values::specified::length::Length as SpecifiedLength;
 use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
@@ -75,31 +73,34 @@ pub struct TransformOrigin<H, V, Depth> 
     pub vertical: V,
     /// The depth.
     pub depth: Depth,
 }
 
 /// A generic timing function.
 ///
 /// <https://drafts.csswg.org/css-timing-1/#single-timing-function-production>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum TimingFunction<Integer, Number> {
     /// `linear | ease | ease-in | ease-out | ease-in-out`
     Keyword(TimingKeyword),
     /// `cubic-bezier(<number>, <number>, <number>, <number>)`
     #[allow(missing_docs)]
+    #[css(comma, function)]
     CubicBezier {
         x1: Number,
         y1: Number,
         x2: Number,
         y2: Number,
     },
     /// `step-start | step-end | steps(<integer>, [ start | end ]?)`
-    Steps(Integer, StepPosition),
+    #[css(comma, function)]
+    Steps(Integer, #[css(skip_if = "is_end")] StepPosition),
     /// `frames(<integer>)`
+    #[css(comma, function)]
     Frames(Integer),
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum TimingKeyword {
@@ -114,16 +115,21 @@ pub enum TimingKeyword {
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum StepPosition {
     Start,
     End,
 }
 
+#[inline]
+fn is_end(position: &StepPosition) -> bool {
+    *position == StepPosition::End
+}
+
 impl<H, V, D> TransformOrigin<H, V, D> {
     /// Returns a new transform origin.
     pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
             depth: depth,
         }
@@ -133,61 +139,16 @@ impl<H, V, D> TransformOrigin<H, V, D> {
 impl<Integer, Number> TimingFunction<Integer, Number> {
     /// `ease`
     #[inline]
     pub fn ease() -> Self {
         TimingFunction::Keyword(TimingKeyword::Ease)
     }
 }
 
-impl<Integer, Number> ToCss for TimingFunction<Integer, Number>
-where
-    Integer: ToCss,
-    Number: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        match *self {
-            TimingFunction::Keyword(keyword) => keyword.to_css(dest),
-            TimingFunction::CubicBezier {
-                ref x1,
-                ref y1,
-                ref x2,
-                ref y2,
-            } => {
-                dest.write_str("cubic-bezier(")?;
-                x1.to_css(dest)?;
-                dest.write_str(", ")?;
-                y1.to_css(dest)?;
-                dest.write_str(", ")?;
-                x2.to_css(dest)?;
-                dest.write_str(", ")?;
-                y2.to_css(dest)?;
-                dest.write_str(")")
-            },
-            TimingFunction::Steps(ref intervals, position) => {
-                dest.write_str("steps(")?;
-                intervals.to_css(dest)?;
-                if position != StepPosition::End {
-                    dest.write_str(", ")?;
-                    position.to_css(dest)?;
-                }
-                dest.write_str(")")
-            },
-            TimingFunction::Frames(ref frames) => {
-                dest.write_str("frames(")?;
-                frames.to_css(dest)?;
-                dest.write_str(")")
-            },
-        }
-    }
-}
-
 impl TimingKeyword {
     /// Returns the keyword as a quadruplet of Bezier point coordinates
     /// `(x1, y1, x2, y2)`.
     #[inline]
     pub fn to_bezier(self) -> (CSSFloat, CSSFloat, CSSFloat, CSSFloat) {
         match self {
             TimingKeyword::Linear => (0., 0., 1., 1.),
             TimingKeyword::Ease => (0.25, 0.1, 0.25, 1.),
@@ -282,28 +243,26 @@ pub enum TransformOperation<Angle, Numbe
     /// The value must be greater than or equal to zero.
     #[css(function)]
     Perspective(Length),
     /// A intermediate type for interpolation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "interpolatematrix")]
     InterpolateMatrix {
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
@@ -311,45 +270,42 @@ pub enum TransformOperation<Angle, Numbe
         #[compute(clone)]
         progress: computed::Percentage,
     },
     /// A intermediate type for accumulation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "accumulatematrix")]
     AccumulateMatrix {
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         count: Integer,
     },
 }
 
-#[derive(Animate, ToComputedValue)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 /// A value of the `transform` property
-pub struct Transform<T>(pub Vec<T>);
+pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
 
 impl<Angle, Number, Length, Integer, LengthOrPercentage>
     TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
     /// Check if it is any translate function
     pub fn is_translate(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
             Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => true,
@@ -547,37 +503,16 @@ where
                 // Note: DOMMatrix doesn't go into this arm.
                 Transform3D::identity()
             },
         };
         Ok(matrix)
     }
 }
 
-impl<T: ToCss> ToCss for Transform<T> {
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        if self.0.is_empty() {
-            return dest.write_str("none");
-        }
-
-        let mut first = true;
-        for operation in &self.0 {
-            if !first {
-                dest.write_str(" ")?;
-            }
-            first = false;
-            operation.to_css(dest)?
-        }
-        Ok(())
-    }
-}
-
 impl<T> Transform<T> {
     /// `none`
     pub fn none() -> Self {
         Transform(vec![])
     }
 }
 
 impl<T: ToMatrix> Transform<T> {
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -47,18 +47,18 @@ pub enum Color {
 
 #[cfg(feature = "gecko")]
 mod gecko {
     #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss)]
     pub enum SpecialColorKeyword {
         MozDefaultColor,
         MozDefaultBackgroundColor,
         MozHyperlinktext,
-        MozActiveHyperlinktext,
-        MozVisitedHyperlinktext,
+        MozActivehyperlinktext,
+        MozVisitedhyperlinktext,
     }
 }
 
 impl From<RGBA> for Color {
     fn from(value: RGBA) -> Self {
         Color::rgba(value)
     }
 }
@@ -155,22 +155,24 @@ impl Parse for Color {
                         parsed: rgba,
                         authored: authored.map(|s| s.to_ascii_lowercase().into_boxed_str()),
                     },
                 })
             }
             Err(e) => {
                 #[cfg(feature = "gecko")]
                 {
-                    if let Ok(system) = input.try(SystemColor::parse) {
-                        return Ok(Color::System(system));
-                    }
+                    if let Ok(ident) = input.expect_ident() {
+                        if let Ok(system) = SystemColor::from_ident(ident) {
+                            return Ok(Color::System(system));
+                        }
 
-                    if let Ok(c) = gecko::SpecialColorKeyword::parse(input) {
-                        return Ok(Color::Special(c));
+                        if let Ok(c) = gecko::SpecialColorKeyword::from_ident(ident) {
+                            return Ok(Color::Special(c));
+                        }
                     }
                 }
 
                 match e.kind {
                     ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
                         Err(e.location.new_custom_error(
                             StyleParseErrorKind::ValueError(ValueParseErrorKind::InvalidColor(t))
                         ))
@@ -363,18 +365,18 @@ impl Color {
             Color::Special(special) => {
                 use self::gecko::SpecialColorKeyword as Keyword;
                 _context.map(|context| {
                     let pres_context = context.device().pres_context();
                     convert_nscolor_to_computedcolor(match special {
                         Keyword::MozDefaultColor => pres_context.mDefaultColor,
                         Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                         Keyword::MozHyperlinktext => pres_context.mLinkColor,
-                        Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
-                        Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
+                        Keyword::MozActivehyperlinktext => pres_context.mActiveLinkColor,
+                        Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor,
                     })
                 })
             }
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => {
                 _context.map(|context| {
                     ComputedColor::rgba(context.device().body_text_color())
                 })
--- a/servo/components/style_derive/animate.rs
+++ b/servo/components/style_derive/animate.rs
@@ -27,26 +27,32 @@ pub fn derive(input: DeriveInput) -> Tok
         let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
         let (result_value, result_info) = cg::value(&variant, "result");
         let mut computations = quote!();
         let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
         computations.append_all(iter.map(|(result, (this, other))| {
             let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.ast());
             if field_attrs.constant {
                 if cg::is_parameterized(&result.ast().ty, &where_clause.params, None) {
-                    where_clause.add_predicate(cg::where_predicate(
-                        result.ast().ty.clone(),
-                        &parse_quote!(std::cmp::PartialEq),
-                        None,
-                    ));
-                    where_clause.add_predicate(cg::where_predicate(
-                        result.ast().ty.clone(),
-                        &parse_quote!(std::clone::Clone),
-                        None,
-                    ));
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            result.ast().ty.clone(),
+                            &parse_quote!(std::cmp::PartialEq),
+                            None,
+                        ),
+                    );
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            result.ast().ty.clone(),
+                            &parse_quote!(std::clone::Clone),
+                            None,
+                        ),
+                    );
                 }
                 quote! {
                     if #this != #other {
                         return Err(());
                     }
                     let #result = ::std::clone::Clone::clone(#this);
                 }
             } else {
--- a/servo/components/style_derive/cg.rs
+++ b/servo/components/style_derive/cg.rs
@@ -1,20 +1,20 @@
 /* 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 darling::{FromDeriveInput, FromField, FromVariant};
 use quote::{ToTokens, Tokens};
 use std::collections::HashSet;
-use syn::{self, DeriveInput, Field, Ident};
-use syn::{ImplGenerics, Path, PathArguments, PathSegment, AngleBracketedGenericArguments, GenericParam};
-use syn::{QSelf, Type, TypeGenerics, TypeParam};
-use syn::{TypeSlice, TypeArray, TypeTuple, TypePath, TypeParen};
-use syn::{Variant, WherePredicate, GenericArgument, Binding};
+use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
+use syn::{GenericArgument, GenericParam, Ident, ImplGenerics, Path};
+use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGenerics};
+use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
+use syn::{Variant, WherePredicate};
 use syn::visit::{self, Visit};
 use synstructure::{self, BindingInfo, BindStyle, VariantAst, VariantInfo};
 
 pub struct WhereClause<'input, 'path> {
     pub inner: Option<syn::WhereClause>,
     pub params: Vec<&'input TypeParam>,
     trait_path: &'path Path,
     trait_output: Option<Ident>,
@@ -37,61 +37,60 @@ impl<'input, 'path> WhereClause<'input, 
         if !is_parameterized(&ty, &self.params, found.as_mut()) {
             return;
         }
         self.bounded_types.insert(ty.clone());
 
         let output = if let Some(output) = self.trait_output {
             output
         } else {
-            self.add_predicate(where_predicate(ty.clone(), trait_path, None));
+            add_predicate(&mut self.inner, where_predicate(ty.clone(), trait_path, None));
             return;
         };
 
         if let Type::Path(syn::TypePath { ref path, .. }) = *ty {
             if path_to_ident(path).is_some() {
-                self.add_predicate(where_predicate(ty.clone(), trait_path, None));
+                add_predicate(&mut self.inner, where_predicate(ty.clone(), trait_path, None));
                 return;
             }
         }
 
         let output_type = map_type_params(ty, &self.params, &mut |ident| {
             let ty = Type::Path(syn::TypePath { qself: None, path: ident.clone().into() });
             fmap_output_type(ty, trait_path, output)
         });
 
         let pred = where_predicate(
             ty.clone(),
             trait_path,
             Some((output, output_type)),
         );
 
-        self.add_predicate(pred);
+        add_predicate(&mut self.inner, pred);
 
         if let Some(found) = found {
             for ident in found {
                 let ty = Type::Path(syn::TypePath { qself: None, path: ident.into() });
                 if !self.bounded_types.contains(&ty) {
                     self.bounded_types.insert(ty.clone());
-                    self.add_predicate(
+                    add_predicate(
+                        &mut self.inner,
                         where_predicate(ty, trait_path, None),
                     );
                 };
             }
         }
     }
+}
 
-    pub fn add_predicate(&mut self, pred: WherePredicate) {
-        if let Some(ref mut inner) = self.inner {
-            inner.predicates.push(pred);
-        } else {
-            self.inner = Some(parse_quote!(where));
-            self.add_predicate(pred);
-        }
-    }
+pub fn add_predicate(
+    where_clause: &mut Option<syn::WhereClause>,
+    pred: WherePredicate,
+) {
+    where_clause.get_or_insert(parse_quote!(where)).predicates.push(pred);
 }
 
 pub fn fmap_match<F>(
     input: &DeriveInput,
     bind_style: BindStyle,
     mut f: F,
 ) -> Tokens
 where
--- a/servo/components/style_derive/to_animated_zero.rs
+++ b/servo/components/style_derive/to_animated_zero.rs
@@ -22,21 +22,24 @@ pub fn derive(input: syn::DeriveInput) -
         }
         let (mapped, mapped_bindings) = cg::value(variant, "mapped");
         let bindings_pairs = variant.bindings().into_iter().zip(mapped_bindings);
         let mut computations = quote!();
         computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {
             let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());
             if field_attrs.constant {
                 if cg::is_parameterized(&binding.ast().ty, &where_clause.params, None) {
-                    where_clause.add_predicate(cg::where_predicate(
-                        binding.ast().ty.clone(),
-                        &parse_quote!(std::clone::Clone),
-                        None,
-                    ));
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            binding.ast().ty.clone(),
+                            &parse_quote!(std::clone::Clone),
+                            None,
+                        ),
+                    );
                 }
                 quote! {
                     let #mapped_binding = ::std::clone::Clone::clone(#binding);
                 }
             } else {
                 where_clause.add_trait_bound(&binding.ast().ty);
                 quote! {
                     let #mapped_binding =
--- a/servo/components/style_derive/to_computed_value.rs
+++ b/servo/components/style_derive/to_computed_value.rs
@@ -35,21 +35,24 @@ pub fn derive(input: DeriveInput) -> Tok
             }
         }
     }
 
     let to_body = cg::fmap_match(&input, BindStyle::Ref, |binding| {
         let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast());
         if attrs.clone {
             if cg::is_parameterized(&binding.ast().ty, &where_clause.params, None) {
-                where_clause.add_predicate(cg::where_predicate(
-                    binding.ast().ty.clone(),
-                    &parse_quote!(std::clone::Clone),
-                    None,
-                ));
+                cg::add_predicate(
+                    &mut where_clause.inner,
+                    cg::where_predicate(
+                        binding.ast().ty.clone(),
+                        &parse_quote!(std::clone::Clone),
+                        None,
+                    ),
+                );
             }
             quote! { ::std::clone::Clone::clone(#binding) }
         } else {
             if !attrs.ignore_bound {
                 where_clause.add_trait_bound(&binding.ast().ty);
             }
             quote! {
                 ::values::computed::ToComputedValue::to_computed_value(#binding, context)
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -1,45 +1,58 @@
 /* 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 cg::{self, WhereClause};
+use cg;
 use darling::util::Override;
 use quote::{ToTokens, Tokens};
-use syn::{self, Data};
+use syn::{self, Data, GenericParam, Path, WhereClause};
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
-pub fn derive(input: syn::DeriveInput) -> Tokens {
-    let name = &input.ident;
-    let trait_path = parse_quote!(::style_traits::ToCss);
-    let (impl_generics, ty_generics, mut where_clause) =
-        cg::trait_parts(&input, &trait_path);
+pub fn derive(mut input: syn::DeriveInput) -> Tokens {
+    let mut where_clause = input.generics.where_clause.take();
+    for param in &input.generics.params {
+        let param = match *param {
+            GenericParam::Type(ref param) => param,
+            _ => continue,
+        };
+        cg::add_predicate(
+            &mut where_clause,
+            parse_quote!(#param: ::style_traits::ToCss),
+        );
+    }
 
     let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
     if let Data::Enum(_) = input.data {
         assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums");
         assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums");
     }
-    let s = Structure::new(&input);
 
-    let match_body = s.each_variant(|variant| {
-        derive_variant_arm(variant, &mut where_clause)
-    });
+    let match_body = {
+        let s = Structure::new(&input);
+        s.each_variant(|variant| {
+            derive_variant_arm(variant, &mut where_clause)
+        })
+    };
+    input.generics.where_clause = where_clause;
+
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 
     let mut impls = quote! {
         impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
             #[allow(unused_variables)]
             #[inline]
             fn to_css<W>(
                 &self,
                 dest: &mut ::style_traits::CssWriter<W>,
             ) -> ::std::fmt::Result
             where
-                W: ::std::fmt::Write
+                W: ::std::fmt::Write,
             {
                 match *self {
                     #match_body
                 }
             }
         }
     };
 
@@ -56,17 +69,17 @@ pub fn derive(input: syn::DeriveInput) -
         });
     }
 
     impls
 }
 
 fn derive_variant_arm(
     variant: &VariantInfo,
-    where_clause: &mut WhereClause,
+    generics: &mut Option<WhereClause>,
 ) -> Tokens {
     let bindings = variant.bindings();
     let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
     let ast = variant.ast();
     let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
     let separator = if variant_attrs.comma { ", " } else { " " };
 
     if variant_attrs.dimension {
@@ -74,22 +87,21 @@ fn derive_variant_arm(
         assert!(
             variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
             "That makes no sense"
         );
     }
 
     let mut expr = if let Some(keyword) = variant_attrs.keyword {
         assert!(bindings.is_empty());
-        let keyword = keyword.to_string();
         quote! {
             ::std::fmt::Write::write_str(dest, #keyword)
         }
     } else if !bindings.is_empty() {
-        derive_variant_fields_expr(bindings, where_clause, separator)
+        derive_variant_fields_expr(bindings, generics, separator)
     } else {
         quote! {
             ::std::fmt::Write::write_str(dest, #identifier)
         }
     };
 
     if variant_attrs.dimension {
         expr = quote! {
@@ -105,36 +117,45 @@ fn derive_variant_arm(
             ::std::fmt::Write::write_str(dest, ")")
         }
     }
     expr
 }
 
 fn derive_variant_fields_expr(
     bindings: &[BindingInfo],
-    where_clause: &mut WhereClause,
+    where_clause: &mut Option<WhereClause>,
     separator: &str,
 ) -> Tokens {
     let mut iter = bindings.iter().filter_map(|binding| {
         let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
         if attrs.skip {
             return None;
         }
         Some((binding, attrs))
     }).peekable();
 
     let (first, attrs) = match iter.next() {
         Some(pair) => pair,
         None => return quote! { Ok(()) },
     };
     if !attrs.iterable && iter.peek().is_none() {
-        if !attrs.ignore_bound {
-            where_clause.add_trait_bound(&first.ast().ty);
+        if attrs.field_bound {
+            let ty = &first.ast().ty;
+            cg::add_predicate(where_clause, parse_quote!(#ty: ::style_traits::ToCss));
         }
-        return quote! { ::style_traits::ToCss::to_css(#first, dest) };
+        let mut expr = quote! { ::style_traits::ToCss::to_css(#first, dest) };
+        if let Some(condition) = attrs.skip_if {
+            expr = quote! {
+                if !#condition(#first) {
+                    #expr
+                }
+            }
+        }
+        return expr;
     }
 
     let mut expr = derive_single_field_expr(first, attrs, where_clause);
     for (binding, attrs) in iter {
         derive_single_field_expr(binding, attrs, where_clause).to_tokens(&mut expr)
     }
 
     quote! {{
@@ -142,19 +163,19 @@ fn derive_variant_fields_expr(
         #expr
         Ok(())
     }}
 }
 
 fn derive_single_field_expr(
     field: &BindingInfo,
     attrs: CssFieldAttrs,
-    where_clause: &mut WhereClause,
+    where_clause: &mut Option<WhereClause>,
 ) -> Tokens {
-    if attrs.iterable {
+    let mut expr = if attrs.iterable {
         if let Some(if_empty) = attrs.if_empty {
             return quote! {
                 {
                     let mut iter = #field.iter().peekable();
                     if iter.peek().is_none() {
                         writer.item(&::style_traits::values::Verbatim(#if_empty))?;
                     } else {
                         for item in iter {
@@ -165,21 +186,32 @@ fn derive_single_field_expr(
             };
         }
         quote! {
             for item in #field.iter() {
                 writer.item(&item)?;
             }
         }
     } else {
-        if !attrs.ignore_bound {
-            where_clause.add_trait_bound(&field.ast().ty);
+        if attrs.field_bound {
+            let ty = &field.ast().ty;
+            cg::add_predicate(where_clause, parse_quote!(#ty: ::style_traits::ToCss));
         }
         quote! { writer.item(#field)?; }
+    };
+
+    if let Some(condition) = attrs.skip_if {
+        expr = quote! {
+            if !#condition(#field) {
+                #expr
+            }
+        }
     }
+
+    expr
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromDeriveInput)]
 struct CssInputAttrs {
     derive_debug: bool,
     // Here because structs variants are also their whole type definition.
     function: Option<Override<String>>,
@@ -196,12 +228,13 @@ pub struct CssVariantAttrs {
     pub keyword: Option<String>,
     pub aliases: Option<String>,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromField)]
 struct CssFieldAttrs {
     if_empty: Option<String>,
-    ignore_bound: bool,
+    field_bound: bool,
     iterable: bool,
     skip: bool,
+    skip_if: Option<Path>,
 }
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -29,16 +29,19 @@ use std::fmt::{self, Write};
 ///   iterable will be serialized as the arguments for the function;
 /// * an iterable field can also be annotated with `#[css(if_empty = "foo")]`
 ///   to print `"foo"` if the iterator is empty;
 /// * if `#[css(dimension)]` is found on a variant, that variant needs
 ///   to have a single member. The variant would be serialized as a CSS
 ///   dimension token, like: <member><identifier>;
 /// * if `#[css(skip)]` is found on a field, the `ToCss` call for that field
 ///   is skipped;
+/// * if `#[css(skip_if = "function")]` is found on a field, the `ToCss` call
+///   for that field is skipped if `function` returns true. This function is
+///   provided the field as an argument;
 /// * finally, one can put `#[css(derive_debug)]` on the whole type, to
 ///   implement `Debug` by a single call to `ToCss::to_css`.
 pub trait ToCss {
     /// Serialize `self` in CSS syntax, writing to `dest`.
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write;
 
     /// Serialize `self` in CSS syntax and return a string.
     ///
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -262117,16 +262117,21 @@
      {}
     ]
    ],
    "css/support/b-green.css": [
     [
      {}
     ]
    ],
+   "css/support/blue32x32.ico": [
+    [
+     {}
+    ]
+   ],
    "css/support/c-red.css": [
     [
      {}
     ]
    ],
    "css/support/cat.png": [
     [
      {}
@@ -502296,17 +502301,17 @@
    "28849b8293b2f4e1e660662f77e025b51a32332e",
    "testharness"
   ],
   "css/css-tables/support/base.css": [
    "abe7468e88cad3aef32c7e59fa4a33a7930ef53b",
    "support"
   ],
   "css/css-tables/table-model-fixup-2.html": [
-   "19e3577e862601a0cf00dd4644017218c363f0db",
+   "57f45e21a6f9f51154f872c724856d41681857e7",
    "testharness"
   ],
   "css/css-tables/table-model-fixup.html": [
    "bf4a9b28167ad2488496b4d4e89ca6d4a8a001ed",
    "testharness"
   ],
   "css/css-tables/tools/markup-generator.html": [
    "51db0929423a4932e4b783dba135adc100da34d9",
@@ -528771,16 +528776,20 @@
   "css/support/alignment.css": [
    "0a5f57a816f56c3e16f992a4bbea8d50b3c6fad9",
    "support"
   ],
   "css/support/b-green.css": [
    "eb78a4d12f35b4249051826ea000c53d04df80b7",
    "support"
   ],
+  "css/support/blue32x32.ico": [
+   "a968207eb33ff204ef5af90af452e6c53f5cde76",
+   "support"
+  ],
   "css/support/c-red.css": [
    "dc288b7aa49b57e0abf803741e78582ba5ceffdb",
    "support"
   ],
   "css/support/cat.png": [
    "461fd17b274662b88500cdf42bab7f3b79e6019d",
    "support"
   ],
@@ -573296,17 +573305,17 @@
    "9285ebf2cd716ea072c18fe668e95cf6ce4ec3de",
    "manual"
   ],
   "payment-request/historical.https.html": [
    "6695acdcd1647fdd37702a7f63658dcd50f25596",
    "testharness"
   ],
   "payment-request/interfaces.https.html": [
-   "d269e8378f2a84ba96c981536667817e0db9e2d1",
+   "2280f0ef821cdc3093e10c2162d3756f5eeb78de",
    "testharness"
   ],
   "payment-request/payment-request-abort-method.https.html": [
    "30c62af4a05a4d83cbbd1e82d0df62bae9a85e96",
    "testharness"
   ],
   "payment-request/payment-request-canmakepayment-method.https.html": [
    "0d863558b996df81a36207201bbf8c649688845d",
@@ -592868,17 +592877,17 @@
    "a9a99d58daec7719ee53ed758f566ccceb582f65",
    "wdspec"
   ],
   "webdriver/tests/actions/sequence.py": [
    "d43caf0f8607a76c3baed7806664b686bde21fda",
    "wdspec"
   ],
   "webdriver/tests/actions/special_keys.py": [
-   "64eb2401664b71d68f7b53e236a947eec6d651cc",
+   "e5726d2628fafd70c026ed912602135fc50ca27a",
    "wdspec"
   ],
   "webdriver/tests/actions/support/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/actions/support/keys.py": [
    "528ab8473914c14f9671d89b8a888d30162714ec",
--- a/testing/web-platform/meta/css/css-tables/table-model-fixup-2.html.ini
+++ b/testing/web-platform/meta/css/css-tables/table-model-fixup-2.html.ini
@@ -1,2 +1,25 @@
 [table-model-fixup-2.html]
-  expected: CRASH
+  [Replaced elements outside a table cannot be table-row and are considered block -- input=text elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row and are considered block -- input=button elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row and are considered block -- input=file elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row and are considered block -- img elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row-group and are considered block -- input=text elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row-group and are considered block -- input=button elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row-group and are considered block -- input=file elements]
+    expected: FAIL
+
+  [Replaced elements outside a table cannot be table-row-group and are considered block -- img elements]
+    expected: FAIL
+
--- a/testing/web-platform/tests/css/css-tables/table-model-fixup-2.html
+++ b/testing/web-platform/tests/css/css-tables/table-model-fixup-2.html
@@ -74,18 +74,18 @@
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should look identical, on their own line:</p>
     <p>Replaced elements inside a table cannot be <mark>table-row</mark> and are considered <mark>block</mark> -- img elements</p>
     <div>
         <x-table style="width: 400px">
-            <x-tr><x-td><img block src="http://w3.org/favicon.ico" target=a /></x-tr>
-            <img src="http://w3.org/favicon.ico" table-row target=b />
+            <x-tr><x-td><img block src="../support/blue32x32.ico" target=a /></x-tr>
+            <img src="../support/blue32x32.ico" table-row target=b />
         </x-table>
     </div>
     <script>
 
         assertEqualOffsetWidths('a','b');
 
     </script>
 
@@ -104,18 +104,18 @@
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should look identical, on their own line:</p>
     <p>Replaced elements inside a table cannot be <mark>table-column</mark> and are considered <mark>inline</mark> -- img elements</p>
     <div>
         <x-table style="width: 400px">
-            <x-tr><x-td><img inline src="http://w3.org/favicon.ico" target=a /></x-tr>
-            <img src="http://w3.org/favicon.ico" table-column target=b />
+            <x-tr><x-td><img inline src="../support/blue32x32.ico" target=a /></x-tr>
+            <img src="../support/blue32x32.ico" table-column target=b />
         </x-table>
     </div>
     <script>
 
         assertEqualOffsetWidths('a','b');
 
     </script>
 
@@ -134,18 +134,18 @@
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should look identical, on their own line:</p>
     <p>Replaced elements inside a table cannot be <mark>table-cell</mark> and are considered <mark>inline</mark> -- img elements</p>
     <div>
         <x-table style="width: 400px">
-            <x-tr><x-td><img inline src="http://w3.org/favicon.ico" target=a /></x-tr>
-            <x-tr><img src="http://w3.org/favicon.ico" table-cell target=b /></x-tr>
+            <x-tr><x-td><img inline src="../support/blue32x32.ico" target=a /></x-tr>
+            <x-tr><img src="../support/blue32x32.ico" table-cell target=b /></x-tr>
         </x-table>
     </div>
     <script>
 
         assertEqualOffsetWidths('a','b');
 
     </script>
 
@@ -187,18 +187,18 @@
         assertEqualOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should share the same line:</p>
     <p>Replaced elements outside a table cannot be <mark>inline-table</mark> and are considered <mark>inline</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" inline-table target=a />
-        <img src="http://w3.org/favicon.ico" inline-table target=b />
+        <img src="../support/blue32x32.ico" inline-table target=a />
+        <img src="../support/blue32x32.ico" inline-table target=b />
     </div>
     <script>
 
         assertEqualOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
@@ -239,18 +239,18 @@
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should stand on their own line:</p>
     <p>Replaced elements outside a table cannot be <mark>table</mark> and are considered <mark>block</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" table target=a />
-        <img src="http://w3.org/favicon.ico" table target=b />
+        <img src="../support/blue32x32.ico" table target=a />
+        <img src="../support/blue32x32.ico" table target=b />
     </div>
     <script>
 
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
@@ -291,18 +291,18 @@
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should stand on their own line:</p>
     <p>Replaced elements outside a table cannot be <mark>table-row</mark> and are considered <mark>block</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" table-row target=a />
-        <img src="http://w3.org/favicon.ico" table-row target=b />
+        <img src="../support/blue32x32.ico" table-row target=a />
+        <img src="../support/blue32x32.ico" table-row target=b />
     </div>
     <script>
 
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
@@ -343,18 +343,18 @@
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should stand on their own line:</p>
     <p>Replaced elements outside a table cannot be <mark>table-row-group</mark> and are considered <mark>block</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" table-row-group target=a />
-        <img src="http://w3.org/favicon.ico" table-row-group target=b />
+        <img src="../support/blue32x32.ico" table-row-group target=a />
+        <img src="../support/blue32x32.ico" table-row-group target=b />
     </div>
     <script>
 
         assertUnequalOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
@@ -395,18 +395,18 @@
         assertEqualOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should share the same line:</p>
     <p>Replaced elements outside a table cannot be <mark>table-column</mark> and are considered <mark>inline</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" table-column target=a />
-        <img src="http://w3.org/favicon.ico" table-column target=b />
+        <img src="../support/blue32x32.ico" table-column target=a />
+        <img src="../support/blue32x32.ico" table-column target=b />
     </div>
     <script>
 
         assertEqualOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
@@ -447,18 +447,18 @@
         assertEqualOffsetTops('a','b');
 
     </script>
 
     <hr/><!------------------------------------------------------------------------------------------------------------>
     <p>Both images should share the same line:</p>
     <p>Replaced elements outside a table cannot be <mark>table-cell</mark> and are considered <mark>inline</mark> -- img elements</p>
     <div>
-        <img src="http://w3.org/favicon.ico" table-cell target=a />
-        <img src="http://w3.org/favicon.ico" table-cell target=b />
+        <img src="../support/blue32x32.ico" table-cell target=a />
+        <img src="../support/blue32x32.ico" table-cell target=b />
     </div>
     <script>
 
         assertEqualOffsetTops('a','b');
 
     </script>
 
 </main>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5844dd614c22028ce81422233c0aa400a6f41e0d
GIT binary patch
literal 326
oc${NkU<5)11tu_Uz{tQL#=yX!0mKSG>;S|daa`a(PMMJf09Ppj7XSbN
--- a/toolkit/components/places/SyncedBookmarksMirror.jsm
+++ b/toolkit/components/places/SyncedBookmarksMirror.jsm
@@ -1081,19 +1081,19 @@ class SyncedBookmarksMirror {
     this.recordTelemetryEvent("mirror", "fetch", "localTree",
                               { time: String(elapsedTime),
                                 count: String(totalRows) });
 
     return localTree;
   }
 
   /**
-   * Fetches content info for all NEW local items that don't exist in the
-   * mirror. We'll try to dedupe them to changed items with similar contents and
-   * different GUIDs in the mirror.
+   * Fetches content info for all NEW and UNKNOWN local items that don't exist
+   * in the mirror. We'll try to dedupe them to changed items with similar
+   * contents and different GUIDs in the mirror.
    *
    * @return {Map.<String, BookmarkContent>}
    *         New items in Places that don't exist in the mirror, keyed by their
    *         GUIDs.
    */
   async fetchNewLocalContents() {
     let newLocalContents = new Map();
     let startTime = Cu.now();
@@ -2130,19 +2130,20 @@ async function initializeTempMirrorEntit
   // remote items, and flags remote items as merged. In the trigger body, `OLD`
   // refers to the row for the unmerged item in `itemsToMerge`.
   await db.execute(`
     CREATE TEMP TRIGGER mergeGuids
     INSTEAD OF DELETE ON itemsToMerge
     BEGIN
       /* We update GUIDs here, instead of in the "updateExistingLocalItems"
          trigger, because deduped items where we're keeping the local value
-         state won't have "needsMerge" set. */
+         state won't have "hasRemoteValue" set. */
       UPDATE moz_bookmarks SET
-        guid = OLD.newGuid
+        guid = OLD.newGuid,
+        syncStatus = ${PlacesUtils.bookmarks.SYNC_STATUS.NORMAL}
       WHERE OLD.oldGuid <> OLD.newGuid AND
             id = OLD.localId;
 
       /* Record item changed notifications for the updated GUIDs. */
       INSERT INTO guidsChanged(itemId, oldGuid, level)
       SELECT OLD.localId, OLD.oldGuid, OLD.newLevel
       WHERE OLD.oldGuid <> OLD.newGuid;
 
--- a/toolkit/components/places/tests/sync/test_bookmark_deduping.js
+++ b/toolkit/components/places/tests/sync/test_bookmark_deduping.js
@@ -70,16 +70,23 @@ add_task(async function test_duping() {
       url: "place:sort=8&maxResults=10",
       title: "Most Visited",
       annos: [{
         name: PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO,
         value: "MostVisited",
       }],
     }],
   });
+
+  // Make sure we still dedupe this even though it doesn't have SYNC_STATUS.NEW
+  PlacesTestUtils.setBookmarkSyncFields({
+    guid: "folderBBBBBB",
+    syncStatus: PlacesUtils.bookmarks.SYNC_STATUS.UNKNOWN
+  });
+
   // Not a candidate for `bookmarkH111` because we didn't dupe `folderAAAAAA`.
   await PlacesUtils.bookmarks.insert({
     parentGuid: "folderAAAAAA",
     guid: "bookmarkHHHH",
     url: "http://example.com/h",
     title: "H",
   });
 
@@ -233,16 +240,22 @@ add_task(async function test_duping() {
     }, {
       guid: PlacesUtils.bookmarks.mobileGuid,
       type: PlacesUtils.bookmarks.TYPE_FOLDER,
       index: 4,
       title: MobileBookmarksTitle,
     }],
   }, "Should dedupe matching NEW bookmarks");
 
+  ok((await PlacesTestUtils.fetchBookmarkSyncFields(
+    "menu________", "folderB11111", "bookmarkC222", "separatorF11",
+    "folderA11111", "bookmarkG111", "separatorE11", "queryD111111"))
+    .every(info => info.syncStatus == PlacesUtils.bookmarks.SYNC_STATUS.NORMAL));
+
+
   await buf.finalize();
   await PlacesUtils.bookmarks.eraseEverything();
   await PlacesSyncUtils.bookmarks.reset();
 });
 
 add_task(async function test_applying_two_empty_folders_doesnt_smush() {
   let buf = await openMirror("applying_two_empty_folders_doesnt_smush");