merge autoland to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 19 Mar 2017 16:23:54 +0100
changeset 348358 51844ae447dbc52cec81abb4c1a6e676b905a743
parent 348357 928b6052cbcf3c1c29436313b61760dc35248d4a (current diff)
parent 348350 f34476fa83fad02854ab80d15b5c133f2bfa23b7 (diff)
child 348371 1b9293be51637f841275541d8991314ca56561a5
push id39126
push userarchaeopteryx@coole-files.de
push dateSun, 19 Mar 2017 15:37:50 +0000
treeherderautoland@53392221fe78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.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 autoland to mozilla-central. r=merge a=merge MozReview-Commit-ID: 7moPYlC25sE
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -486,22 +486,23 @@ puAlertText=Click here for details
 geolocation.allowLocation=Allow Location Access
 geolocation.allowLocation.accesskey=A
 geolocation.dontAllowLocation=Don’t Allow
 geolocation.dontAllowLocation.accesskey=n
 geolocation.shareWithSite3=Will you allow %S to access your location?
 geolocation.shareWithFile3=Will you allow this local file to access your location?
 geolocation.remember=Remember this decision
 
-webNotifications.remember=Remember this decision
-webNotifications.rememberForSession=Remember decision for this session
 webNotifications.allow=Allow Notifications
 webNotifications.allow.accesskey=A
-webNotifications.dontAllow=Don’t Allow
-webNotifications.dontAllow.accesskey=n
+webNotifications.notNow=Not Now
+webNotifications.notNow.accesskey=n
+webNotifications.never=Never Allow
+webNotifications.neverForSession=Never For This Session
+webNotifications.never.accesskey=v
 webNotifications.receiveFromSite2=Will you allow %S to send notifications?
 # LOCALIZATION NOTE (webNotifications.upgradeTitle): When using native notifications on OS X, the title may be truncated around 32 characters.
 webNotifications.upgradeTitle=Upgraded notifications
 # LOCALIZATION NOTE (webNotifications.upgradeBody): When using native notifications on OS X, the body may be truncated around 100 characters in some views.
 webNotifications.upgradeBody=You can now receive notifications from sites that are not currently loaded. Click to learn more.
 
 # Phishing/Malware Notification Bar.
 # LOCALIZATION NOTE (notADeceptiveSite, notAnAttack)
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -296,17 +296,18 @@ this.PermissionPromptPrototype = {
         callback: state => {
           if (promptAction.callback) {
             promptAction.callback();
           }
 
           if (this.permissionKey) {
 
             // Permanently store permission.
-            if (state && state.checkboxChecked) {
+            if ((state && state.checkboxChecked) ||
+                promptAction.scope == SitePermissions.SCOPE_PERSISTENT) {
               let scope = SitePermissions.SCOPE_PERSISTENT;
               // Only remember permission for session if in PB mode.
               if (PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
                 scope = SitePermissions.SCOPE_SESSION;
               }
               SitePermissions.set(this.principal.URI,
                                   this.permissionKey,
                                   promptAction.action,
@@ -534,32 +535,18 @@ DesktopNotificationPermissionPrompt.prot
   get permissionKey() {
     return "desktop-notification";
   },
 
   get popupOptions() {
     let learnMoreURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
 
-    let checkbox = {
-      show: true,
-      checked: true,
-      label: gBrowserBundle.GetStringFromName("webNotifications.remember")
-    };
-
-    // In PB mode, the "always remember" checkbox should only remember for the
-    // session.
-    if (PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)) {
-      checkbox.label =
-        gBrowserBundle.GetStringFromName("webNotifications.rememberForSession");
-    }
-
     return {
       learnMoreURL,
-      checkbox,
       displayURI: false
     };
   },
 
   get notificationID() {
     return "web-notifications";
   },
 
@@ -578,21 +565,31 @@ DesktopNotificationPermissionPrompt.prot
 
   get promptActions() {
     return [
       {
         label: gBrowserBundle.GetStringFromName("webNotifications.allow"),
         accessKey:
           gBrowserBundle.GetStringFromName("webNotifications.allow.accesskey"),
         action: SitePermissions.ALLOW,
+        scope: SitePermissions.SCOPE_PERSISTENT,
       },
       {
-        label: gBrowserBundle.GetStringFromName("webNotifications.dontAllow"),
+        label: gBrowserBundle.GetStringFromName("webNotifications.notNow"),
         accessKey:
-          gBrowserBundle.GetStringFromName("webNotifications.dontAllow.accesskey"),
+          gBrowserBundle.GetStringFromName("webNotifications.notNow.accesskey"),
         action: SitePermissions.BLOCK,
       },
+      {
+        label: PrivateBrowsingUtils.isBrowserPrivate(this.browser) ?
+          gBrowserBundle.GetStringFromName("webNotifications.neverForSession") :
+          gBrowserBundle.GetStringFromName("webNotifications.never"),
+        accessKey:
+          gBrowserBundle.GetStringFromName("webNotifications.never.accesskey"),
+        action: SitePermissions.BLOCK,
+        scope: SitePermissions.SCOPE_PERSISTENT,
+      },
     ];
   },
 };
 
 PermissionUI.DesktopNotificationPermissionPrompt =
   DesktopNotificationPermissionPrompt;
--- a/browser/modules/test/browser/browser_PermissionUI_prompts.js
+++ b/browser/modules/test/browser/browser_PermissionUI_prompts.js
@@ -45,18 +45,21 @@ function* testPrompt(Prompt) {
     let curPerm = SitePermissions.get(principal.URI, permissionKey, browser);
     Assert.equal(curPerm.state, SitePermissions.UNKNOWN,
                  "Should be no permission set to begin with.");
 
     // First test denying the permission request without the checkbox checked.
     let popupNotification = getPopupNotificationNode();
     popupNotification.checkbox.checked = false;
 
-    Assert.equal(notification.secondaryActions.length, 1,
-                 "There should only be 1 secondary action");
+    let isNotificationPrompt = Prompt == PermissionUI.DesktopNotificationPermissionPrompt;
+
+    let expectedSecondaryActionsCount = isNotificationPrompt ? 2 : 1;
+    Assert.equal(notification.secondaryActions.length, expectedSecondaryActionsCount,
+                 "There should only be " + expectedSecondaryActionsCount + " secondary action(s)");
     yield clickSecondaryAction();
     curPerm = SitePermissions.get(principal.URI, permissionKey, browser);
     Assert.deepEqual(curPerm, {
                        state: SitePermissions.BLOCK,
                        scope: SitePermissions.SCOPE_TEMPORARY,
                      }, "Should have denied the action temporarily");
 
     Assert.ok(mockRequest._cancelled,
@@ -68,23 +71,29 @@ function* testPrompt(Prompt) {
     mockRequest._cancelled = false;
 
     // Bring the PopupNotification back up now...
     shownPromise =
       BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
     TestPrompt.prompt();
     yield shownPromise;
 
-    // Test denying the permission request with the checkbox checked.
+    // Test denying the permission request with the checkbox checked (for geolocation)
+    // or by clicking the "never" option from the dropdown (for notifications).
     popupNotification = getPopupNotificationNode();
-    popupNotification.checkbox.checked = true;
+    let secondaryActionToClickIndex = 0;
+    if (isNotificationPrompt) {
+      secondaryActionToClickIndex = 1;
+    } else {
+      popupNotification.checkbox.checked = true;
+    }
 
-    Assert.equal(notification.secondaryActions.length, 1,
-                 "There should only be 1 secondary action");
-    yield clickSecondaryAction();
+    Assert.equal(notification.secondaryActions.length, expectedSecondaryActionsCount,
+                 "There should only be " + expectedSecondaryActionsCount + " secondary action(s)");
+    yield clickSecondaryAction(secondaryActionToClickIndex);
     curPerm = SitePermissions.get(principal.URI, permissionKey);
     Assert.deepEqual(curPerm, {
                        state: SitePermissions.BLOCK,
                        scope: SitePermissions.SCOPE_PERSISTENT
                      }, "Should have denied the action permanently");
     Assert.ok(mockRequest._cancelled,
               "The request should have been cancelled");
     Assert.ok(!mockRequest._allowed,
--- a/browser/modules/test/browser/head.js
+++ b/browser/modules/test/browser/head.js
@@ -173,26 +173,49 @@ function clickMainAction() {
   popupNotification.button.click();
   return removePromise;
 }
 
 /**
  * For an opened PopupNotification, clicks on the secondary action,
  * and waits for the panel to fully close.
  *
+ * @param actionIndex (Number)
+ *        The index of the secondary action to be clicked. The default
+ *        secondary action (the button shown directly in the panel) is
+ *        treated as having index 0.
+ *
  * @return {Promise}
  *         Resolves once the panel has fired the "popuphidden"
  *         event.
  */
-function clickSecondaryAction() {
+function clickSecondaryAction(actionIndex) {
   let removePromise =
     BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
   let popupNotification = getPopupNotificationNode();
-  popupNotification.secondaryButton.click();
-  return removePromise;
+  if (!actionIndex) {
+    popupNotification.secondaryButton.click();
+    return removePromise;
+  }
+
+  return Task.spawn(function* () {
+    // Click the dropmarker arrow and wait for the menu to show up.
+    let dropdownPromise =
+      BrowserTestUtils.waitForEvent(popupNotification.menupopup, "popupshown");
+    yield EventUtils.synthesizeMouseAtCenter(popupNotification.menubutton, {});
+    yield dropdownPromise;
+
+    // The menuitems in the dropdown are accessible as direct children of the panel,
+    // because they are injected into a <children> node in the XBL binding.
+    // The target action is the menuitem at index actionIndex - 1, because the first
+    // secondary action (index 0) is the button shown directly in the panel.
+    let actionMenuItem = popupNotification.querySelectorAll("menuitem")[actionIndex - 1];
+    yield EventUtils.synthesizeMouseAtCenter(actionMenuItem, {});
+    yield removePromise;
+  });
 }
 
 /**
  * Makes sure that 1 (and only 1) <xul:popupnotification> is being displayed
  * by PopupNotification, and then returns that <xul:popupnotification>.
  *
  * @return {<xul:popupnotification>}
  */
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9970,8 +9970,21 @@ nsContentUtils::CreateJSValueFromSequenc
                                      JSPROP_ENUMERATE))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   aValue.setObject(*array);
   return NS_OK;
 }
+
+/* static */ Element*
+nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement)
+{
+  MOZ_ASSERT(aElement);
+  MOZ_ASSERT(aElement->IsNativeAnonymous());
+
+  Element* e = aElement;
+  while (e && e->IsNativeAnonymous()) {
+    e = e->GetParentElement();
+  }
+  return e;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2787,16 +2787,22 @@ public:
   static nsresult
   CreateJSValueFromSequenceOfObject(JSContext* aCx,
                                     const mozilla::dom::Sequence<JSObject*>& aTransfer,
                                     JS::MutableHandle<JS::Value> aValue);
 
   static bool
   IsWebComponentsEnabled() { return sIsWebComponentsEnabled; }
 
+  /**
+   * Walks up the tree from aElement until it finds an element that is
+   * not native anonymous content.  aElement must be NAC itself.
+   */
+  static Element* GetClosestNonNativeAnonymousAncestor(Element* aElement);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5623,16 +5623,30 @@ nsDocument::GetCustomElementRegistry()
   RefPtr<CustomElementRegistry> registry = window->CustomElements();
   if (!registry) {
     return nullptr;
   }
 
   return registry.forget();
 }
 
+// We only support pseudo-elements with two colons in this function.
+static CSSPseudoElementType
+GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
+{
+  MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null");
+  if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return CSSPseudoElementType::NotPseudo;
+  }
+  nsCOMPtr<nsIAtom> pseudo = NS_Atomize(Substring(aString, 1));
+  return nsCSSPseudoElements::GetPseudoType(pseudo,
+      nsCSSProps::EnabledState::eInUASheets);
+}
+
 already_AddRefed<Element>
 nsDocument::CreateElement(const nsAString& aTagName,
                           const ElementCreationOptionsOrString& aOptions,
                           ErrorResult& rv)
 {
   rv = nsContentUtils::CheckQName(aTagName, false);
   if (rv.Failed()) {
     return nullptr;
@@ -5640,28 +5654,48 @@ nsDocument::CreateElement(const nsAStrin
 
   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
   nsAutoString lcTagName;
   if (needsLowercase) {
     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
   }
 
   const nsString* is = nullptr;
+  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
   if (aOptions.IsElementCreationOptions()) {
+    const ElementCreationOptions& options =
+      aOptions.GetAsElementCreationOptions();
     // Throw NotFoundError if 'is' is not-null and definition is null
-    is = CheckCustomElementName(aOptions.GetAsElementCreationOptions(),
-      needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
+    is = CheckCustomElementName(options,
+                                needsLowercase ? lcTagName : aTagName,
+                                mDefaultElementType, rv);
     if (rv.Failed()) {
       return nullptr;
     }
+
+    // Check 'pseudo' and throw an exception if it's not one allowed
+    // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
+    if (options.mPseudo.WasPassed()) {
+      pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
+      if (rv.Failed() ||
+          pseudoType == CSSPseudoElementType::NotPseudo ||
+          !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) {
+        rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+        return nullptr;
+      }
+    }
   }
 
   RefPtr<Element> elem = CreateElem(
     needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
 
+  if (pseudoType != CSSPseudoElementType::NotPseudo) {
+    elem->SetPseudoElementType(pseudoType);
+  }
+
   return elem.forget();
 }
 
 NS_IMETHODIMP
 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
                             const nsAString& aQualifiedName,
                             nsIDOMElement** aReturn)
 {
--- a/dom/notification/test/browser/browser_permission_dismiss.js
+++ b/dom/notification/test/browser/browser_permission_dismiss.js
@@ -1,14 +1,15 @@
 "use strict";
 
 const ORIGIN_URI = Services.io.newURI("http://mochi.test:8888");
 const PERMISSION_NAME = "desktop-notification";
 const PROMPT_ALLOW_BUTTON = -1;
-const PROMPT_BLOCK_BUTTON = 0;
+const PROMPT_NOT_NOW_BUTTON = 0;
+const PROMPT_NEVER_BUTTON = 1;
 const TEST_URL = "http://mochi.test:8888/browser/dom/notification/test/browser/notification.html";
 
 /**
  * Clicks the specified web-notifications prompt button.
  *
  * @param {Number} aButtonIndex Number indicating which button to click.
  *                              See the constants in this file.
  * @note modified from toolkit/components/passwordmgr/test/browser/head.js
@@ -16,20 +17,26 @@ const TEST_URL = "http://mochi.test:8888
 function clickDoorhangerButton(aButtonIndex) {
   let popup = PopupNotifications.getNotification("web-notifications");
   let notifications = popup.owner.panel.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   ok(true, notifications.length + " notification(s)");
   let notification = notifications[0];
 
   if (aButtonIndex == PROMPT_ALLOW_BUTTON) {
-    ok(true, "Triggering main action");
+    ok(true, "Triggering main action (allow the permission)");
     notification.button.doCommand();
+  } else if (aButtonIndex == PROMPT_NEVER_BUTTON) {
+    ok(true, "Triggering secondary action (deny the permission permanently)");
+    // The menuitems in the dropdown are accessible as direct children of the panel,
+    // because they are injected into a <children> node in the XBL binding.
+    // The "never" button is the first menuitem in the dropdown.
+    notification.querySelector("menuitem").doCommand();
   } else {
-    ok(true, "Triggering secondary action");
+    ok(true, "Triggering secondary action (deny the permission temporarily)");
     notification.secondaryButton.doCommand();
   }
 }
 
 /**
  * Opens a tab which calls `Notification.requestPermission()` with a callback
  * argument, calls the `task` function while the permission prompt is open,
  * and verifies that the expected permission is set.
@@ -79,19 +86,32 @@ add_task(function* test_requestPermissio
   ok(!PopupNotifications.getNotification("web-notifications"),
      "Should remove the doorhanger notification icon if granted");
 
   is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME),
      Services.perms.ALLOW_ACTION,
      "Check permission in perm. manager");
 });
 
-add_task(function* test_requestPermission_denied() {
+add_task(function* test_requestPermission_denied_temporarily() {
   yield tabWithRequest(function() {
-    clickDoorhangerButton(PROMPT_BLOCK_BUTTON);
+    clickDoorhangerButton(PROMPT_NOT_NOW_BUTTON);
+  }, "default");
+
+  ok(!PopupNotifications.getNotification("web-notifications"),
+     "Should remove the doorhanger notification icon if denied");
+
+  is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME),
+     Services.perms.UNKNOWN_ACTION,
+     "Check permission in perm. manager");
+});
+
+add_task(function* test_requestPermission_denied_permanently() {
+  yield tabWithRequest(function*() {
+    yield clickDoorhangerButton(PROMPT_NEVER_BUTTON);
   }, "denied");
 
   ok(!PopupNotifications.getNotification("web-notifications"),
      "Should remove the doorhanger notification icon if denied");
 
   is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME),
      Services.perms.DENY_ACTION,
      "Check permission in perm. manager");
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -12,16 +12,19 @@ interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible", "prerender" };
 
 /* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */
 dictionary ElementCreationOptions {
   DOMString is;
+
+  [ChromeOnly]
+  DOMString pseudo;
 };
 
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
   [Pure, Throws, BinaryName="documentURIFromJS", NeedsCallerType]
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -726,16 +726,35 @@ ElementForStyleContext(nsIContent* aPare
     MOZ_ASSERT(f);
     while (f->GetType() != nsGkAtoms::numberControlFrame) {
       f = f->GetParent();
       MOZ_ASSERT(f);
     }
     return f->GetContent()->AsElement();
   }
 
+  Element* frameElement = aFrame->GetContent()->AsElement();
+  if (frameElement->IsNativeAnonymous() &&
+      nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(aPseudoType)) {
+    // NAC-implemented pseudos use the closest non-NAC element as their
+    // element to inherit from.
+    //
+    // FIXME(heycam): In theory we shouldn't need to limit this only to
+    // JS-created pseudo-implementing NAC, as all pseudo-implementing
+    // should use the closest non-native anonymous ancestor element as
+    // its originating element.  But removing that part of the condition
+    // reveals some bugs in style resultion with display:contents and
+    // XBL.  See bug 1345809.
+    Element* originatingElement =
+      nsContentUtils::GetClosestNonNativeAnonymousAncestor(frameElement);
+    if (originatingElement) {
+      return originatingElement;
+    }
+  }
+
   if (aParentContent) {
     return aParentContent->AsElement();
   }
 
   MOZ_ASSERT(aFrame->GetContent()->GetParent(),
              "should not have got here for the root element");
   return aFrame->GetContent()->GetParent()->AsElement();
 }
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5091,18 +5091,29 @@ nsCSSFrameConstructor::ResolveStyleConte
                                            LazyComputeBehavior::Assert,
                                            aState->mTreeMatchContext);
       } else {
         result = styleSet->ResolveStyleFor(aContent->AsElement(),
                                            aParentStyleContext,
                                            LazyComputeBehavior::Assert);
       }
     } else {
+      MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree());
+      if (!aOriginatingElementOrNull) {
+        // For pseudo-implementing NAC created by JS using the ChromeOnly
+        // document.createElement(..., { pseudo: ... }) API, we find the
+        // originating element by lookup the tree until we find a non-NAC
+        // ancestor.  (These are the correct semantics for C++-generated pseudo-
+        // implementing NAC as well, but for those cases we already have a
+        // correct originating element passed in.)
+        MOZ_ASSERT(nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType));
+        aOriginatingElementOrNull =
+          nsContentUtils::GetClosestNonNativeAnonymousAncestor(aContent->AsElement());
+      }
       MOZ_ASSERT(aOriginatingElementOrNull);
-      MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree());
       result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull,
                                                    pseudoType,
                                                    aParentStyleContext,
                                                    aContent->AsElement());
     }
   } else {
     MOZ_ASSERT(!aOriginatingElementOrNull);
     NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -260,32 +260,33 @@ public:
     mPoint = aPoint;
 
     // Store the presentation context. The timer will be
     // stopped by the selection if the prescontext is destroyed.
     mPresContext = aPresContext;
 
     mContent = nsIPresShell::GetCapturingContent();
 
-    if (!mTimer)
-    {
+    if (!mTimer) {
       nsresult result;
       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
-
-      if (NS_FAILED(result))
+      mTimer->SetTarget(
+        mPresContext->Document()->EventTargetFor(TaskCategory::Other));
+
+      if (NS_FAILED(result)) {
         return result;
+      }
     }
 
     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
   }
 
   nsresult Stop()
   {
-    if (mTimer)
-    {
+    if (mTimer) {
       mTimer->Cancel();
       mTimer = nullptr;
     }
 
     mContent = nullptr;
     return NS_OK;
   }
 
@@ -303,38 +304,40 @@ public:
   }
 
   NS_IMETHOD Notify(nsITimer *timer) override
   {
     if (mSelection && mPresContext)
     {
       AutoWeakFrame frame =
         mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
-      if (!frame)
+      if (!frame) {
         return NS_OK;
+      }
       mContent = nullptr;
 
       nsPoint pt = mPoint -
         frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
       mFrameSelection->HandleDrag(frame, pt);
-      if (!frame.IsAlive())
+      if (!frame.IsAlive()) {
         return NS_OK;
+      }
 
       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
       mSelection->DoAutoScroll(frame, pt);
     }
     return NS_OK;
   }
 
 protected:
   virtual ~nsAutoScrollTimer()
   {
-   if (mTimer) {
-     mTimer->Cancel();
-   }
+    if (mTimer) {
+      mTimer->Cancel();
+    }
   }
 
 private:
   nsFrameSelection *mFrameSelection;
   Selection* mSelection;
   nsPresContext *mPresContext;
   // relative to mPresContext's root frame
   nsPoint mPoint;
@@ -1719,28 +1722,30 @@ nsFrameSelection::HandleDrag(nsIFrame *a
 }
 
 nsresult
 nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
                                        nsPoint   aPoint,
                                        uint32_t  aDelay)
 {
   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
-  if (!mDomSelections[index])
+  if (!mDomSelections[index]) {
     return NS_ERROR_NULL_POINTER;
+  }
 
   return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
 }
 
 void
 nsFrameSelection::StopAutoScrollTimer()
 {
   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
-  if (!mDomSelections[index])
+  if (!mDomSelections[index]) {
     return;
+  }
 
   mDomSelections[index]->StopAutoScrollTimer();
 }
 
 /**
 hard to go from nodes to frames, easy the other way!
  */
 nsresult
@@ -4820,53 +4825,56 @@ Selection::SetTextRangeStyle(nsIDOMRange
 
 nsresult
 Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
                                 uint32_t aDelay)
 {
   NS_PRECONDITION(aFrame, "Need a frame");
 
   nsresult result;
-  if (!mFrameSelection)
+  if (!mFrameSelection) {
     return NS_OK;//nothing to do
-
-  if (!mAutoScrollTimer)
-  {
+  }
+
+  if (!mAutoScrollTimer) {
     mAutoScrollTimer = new nsAutoScrollTimer();
 
     result = mAutoScrollTimer->Init(mFrameSelection, this);
 
-    if (NS_FAILED(result))
+    if (NS_FAILED(result)) {
       return result;
+    }
   }
 
   result = mAutoScrollTimer->SetDelay(aDelay);
 
-  if (NS_FAILED(result))
+  if (NS_FAILED(result)) {
     return result;
+  }
 
   return DoAutoScroll(aFrame, aPoint);
 }
 
 nsresult
 Selection::StopAutoScrollTimer()
 {
   if (mAutoScrollTimer) {
     return mAutoScrollTimer->Stop();
   }
-  return NS_OK; 
+  return NS_OK;
 }
 
 nsresult
 Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
 {
   NS_PRECONDITION(aFrame, "Need a frame");
 
-  if (mAutoScrollTimer)
+  if (mAutoScrollTimer) {
     (void)mAutoScrollTimer->Stop();
+  }
 
   nsPresContext* presContext = aFrame->PresContext();
   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
   nsRootPresContext* rootPC = presContext->GetRootPresContext();
   if (!rootPC)
     return NS_OK;
   nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
   AutoWeakFrame weakRootFrame(rootmostFrame);
--- a/layout/reftests/bugs/reftest-stylo.list
+++ b/layout/reftests/bugs/reftest-stylo.list
@@ -382,17 +382,17 @@ fails == 315920-12b.html 315920-12b.html
 fails == 315920-12c.html 315920-12c.html
 fails == 315920-13a.html 315920-13a.html
 == 315920-13b.html 315920-13b.html
 fails == 315920-14.html 315920-14.html
 == 315920-15.html 315920-15.html
 fails == 315920-16.html 315920-16.html
 fails == 315920-17.html 315920-17.html
 fails == 315920-18a.html 315920-18a.html
-fails == 315920-18b.html 315920-18b.html
+== 315920-18b.html 315920-18b.html
 fails == 315920-18c.html 315920-18c.html
 fails == 315920-18d.html 315920-18d.html
 fails == 315920-18e.html 315920-18e.html
 fails == 315920-18f.html 315920-18f.html
 fails == 315920-18g.html 315920-18g.html
 == 315920-18h.html 315920-18h.html
 == 315920-18i.html 315920-18i.html
 == 315920-19.html 315920-19.html
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -31,16 +31,20 @@
 // Flag that indicate the pseudo-element supports a user action pseudo-class
 // following it, such as :active or :hover.  This would normally correspond
 // to whether the pseudo-element is tree-like, but we don't support these
 // pseudo-classes on ::before and ::after generated content yet.  See
 // http://dev.w3.org/csswg/selectors4/#pseudo-elements.
 #define CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE  (1<<3)
 // Is content prevented from parsing selectors containing this pseudo-element?
 #define CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY               (1<<4)
+// Can we use the ChromeOnly document.createElement(..., { pseudo: "::foo" })
+// API for creating pseudo-implementing native anonymous content in JS with this
+// pseudo-element?
+#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC           (1<<5)
 
 namespace mozilla {
 
 // The total count of CSSPseudoElement is less than 256,
 // so use uint8_t as its underlying type.
 typedef uint8_t CSSPseudoElementTypeBase;
 enum class CSSPseudoElementType : CSSPseudoElementTypeBase {
   // If the actual pseudo-elements stop being first here, change
@@ -95,16 +99,21 @@ public:
   static bool PseudoElementSupportsStyleAttribute(const Type aType) {
     MOZ_ASSERT(aType < Type::Count);
     return PseudoElementHasFlags(aType,
                                  CSS_PSEUDO_ELEMENT_SUPPORTS_STYLE_ATTRIBUTE);
   }
 
   static bool PseudoElementSupportsUserActionState(const Type aType);
 
+  static bool PseudoElementIsJSCreatedNAC(Type aType)
+  {
+    return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC);
+  }
+
   static bool IsEnabled(Type aType, EnabledState aEnabledState)
   {
     return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) ||
            (aEnabledState & EnabledState::eInUASheets);
   }
 
 private:
   // Does the given pseudo-element have all of the flags given?
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -861,28 +861,31 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
   }
 
   if (!mStyleContext || mStyleContext->HasPseudoElementData()) {
 #ifdef DEBUG
     if (mStyleContext) {
       // We want to check that going through this path because of
       // HasPseudoElementData is rare, because it slows us down a good
       // bit.  So check that we're really inside something associated
-      // with a pseudo-element that contains elements.
+      // with a pseudo-element that contains elements.  (We also allow
+      // the element to be NAC, just in case some chrome JS calls
+      // getComputedStyle on a NAC-implemented pseudo.)
       nsStyleContext* topWithPseudoElementData = mStyleContext;
       while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) {
         topWithPseudoElementData = topWithPseudoElementData->GetParent();
       }
       CSSPseudoElementType pseudo = topWithPseudoElementData->GetPseudoType();
       nsIAtom* pseudoAtom = nsCSSPseudoElements::GetPseudoAtom(pseudo);
       nsAutoString assertMsg(
         NS_LITERAL_STRING("we should be in a pseudo-element that is expected to contain elements ("));
       assertMsg.Append(nsDependentString(pseudoAtom->GetUTF16String()));
       assertMsg.Append(')');
-      NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo),
+      NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo) ||
+                   mContent->IsNativeAnonymous(),
                    NS_LossyConvertUTF16toASCII(assertMsg).get());
     }
 #endif
     // Need to resolve a style context
     RefPtr<nsStyleContext> resolvedStyleContext =
       nsComputedDOMStyle::GetStyleContextForElement(mContent->AsElement(),
                                                     mPseudo,
                                                     mPresShell,
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -273,16 +273,19 @@ to mochitest command.
     * test_initial_storage.html `align-` [6]
     * ... `justify-` [6]
     * test_value_storage.html `align-` [57]
     * ... `justify-` [46]
 * @page support
   * test_bug887741_at-rules_in_declaration_lists.html [1]
   * test_page_parser.html [30]
   * test_rule_insertion.html `@page` [4]
+* Stylesheet cloning is somehow busted bug 1348481
+  * test_selectors.html `cloned correctly` [150]
+  * ... `matched clone` [195]
 * Unsupported prefixed values
   * moz-prefixed gradient functions bug 1337655
     * test_value_storage.html `-moz-linear-gradient` [322]
     * ... `-moz-radial-gradient` [309]
     * ... `-moz-repeating-` [298]
   * webkit-prefixed gradient functions servo/servo#15441
     * test_value_storage.html `-webkit-gradient` [225]
     * ... `-webkit-linear-gradient` [40]
@@ -349,19 +352,19 @@ to mochitest command.
     * test_inherit_storage.html `for property 'border-image-` [5]
     * test_initial_storage.html `for property 'border-image-` [10]
     * test_value_storage.html `(for 'border-image-` [60]
   * -moz-alt-content parsing is wrong: servo/servo#15726
     * test_property_syntax_errors.html `-moz-alt-content` [4]
   * {transform,perspective}-origin fail to parse 'center left' and 'center right' servo/servo#15750
     * test_value_storage.html `'center left'` [8]
     * ... `'center right'` [8]
-* Incorrect parsing
   * mask shorthand servo/servo#15772
     * test_property_syntax_errors.html `mask'` [76]
+  * :-moz-any with combinators in the value bug 1348487 [7]
 * Incorrect serialization
   * border-radius and -moz-outline-radius shorthand servo/servo#15169
     * test_priority_preservation.html `border-radius` [4]
     * test_value_storage.html `border-radius:` [156]
     * ... `-moz-outline-radius:` [76]
     * test_shorthand_property_getters.html `should condense to shortest possible` [6]
   * transform property servo/servo#15194
     * test_value_storage.html `'transform` [104]
@@ -383,25 +386,43 @@ to mochitest command.
     * test_shorthand_property_getters.html `should serialize to 4-value` [2]
   * test_variables.html `--weird`: name of custom property is not escaped properly servo/servo#15399 [1]
   * ... `got "--`: CSS-wide keywords in custom properties servo/servo#15401 [3]
   * image-layer values should omit some of its parts when they are initial servo/servo#15951
     * test_shorthand_property_getters.html `background` [2]
   * counter-{reset,increment} doesn't serialize none servo/servo#15977
     * test_value_storage.html `counter-reset` [2]
     * test_value_storage.html `counter-increment` [2]
+  * :not(*) doesn't serialize properly servo/servo#16017
+    * test_selectors.html `:not()` [8]
+    * ... `:not(html|)` [1]
+  * "*|a" gets serialized as "a" when it should not servo/servo#16020
+    * test_selectors.html `reserialization of *|a` [6]
+* Unsupported pseudo-elements or anon boxes
+  * :-moz-tree bits bug 1348488
+    * test_selectors.html `:-moz-tree` [10]
+  * :-moz-placeholder bug 1348490
+    * test_selectors.html `:-moz-placeholder` [1]
+  * ::-moz-color-swatch bug 1348492
+    * test_selectors.html `::-moz-color-swatch` [1]
 * Unsupported pseudo-classes
   * :default ##easy##
     * test_bug302186.html [24]
   * test_bug98997.html: pseudo-class :empty and :-moz-only-whitespace bug 1337068 [75]
+  * :-moz-locale-dir
+    * test_selectors.html `:-moz-locale-dir` [15]
+  * :-moz-lwtheme-*
+    * test_selectors.html `:-moz-lwtheme` [3]
+  * :-moz-window-inactive bug 1348489
+    * test_selectors.html `:-moz-window-inactive` [2]
   * :-moz-{first,last}-node
-    * test_selectors.html `:-moz-` [4]
-    * ... `unexpected rule index` [2]
-  * :placeholder-shown
-    * test_selectors.html `TypeError` [1]
+    * test_selectors.html `:-moz-` [13]
+    * ... `unexpected rule index` [5]
+  * :dir
+    * test_selectors.html `:dir` [10]
 * issues arround font shorthand servo/servo#15032 servo/servo#15036
   * test_bug377947.html [2]
   * test_value_storage.html `'font'` [144]
   * test_shorthand_property_getters.html `font shorthand` [2]
   * test_system_font_serialization.html [10]
 * test_value_storage.html `font-size: calc(`: clamp negative value servo/servo#15296 [3]
 * rounding issue
   * test_value_storage.html `33.5833px` [2]
--- a/layout/style/test/test_selectors.html
+++ b/layout/style/test/test_selectors.html
@@ -52,17 +52,17 @@ function run() {
         }
         if (!namespaces) {
             namespaces = "";
         }
         style_text.data = namespaces + selector + "{ z-index: " + zi + " }";
 
         var idx = style_text.parentNode.sheet.cssRules.length - 1;
         if (namespaces == "") {
-            is(idx, 0, "unexpected rule index");
+            is(idx, 0, "unexpected rule index for " + selector);
         }
         if (idx < 0 ||
             style_text.parentNode.sheet.cssRules[idx].type !=
                 CSSRule.STYLE_RULE)
         {
             ok(false, "selector " + selector + " could not be parsed");
             return;
         }
@@ -101,19 +101,24 @@ function run() {
             var e = should_not_match[i];
             is(ifwin.getComputedStyle(e).zIndex, "auto",
                "element in " + body_contents + " did not match " + ser1 +
                " which is the reserialization of " + selector);
         }
 
         // But when we serialize the serialized result, we should get
         // the same text.
-        var ser2 = style_text.parentNode.sheet.cssRules[idx].selectorText;
-        is(ser2, ser1, "parse+serialize of selector \"" + selector +
-                       "\" is idempotent");
+        isnot(style_text.parentNode.sheet.cssRules[idx], undefined,
+              "parse of selector \"" + ser1 + "\" failed");
+
+        if (style_text.parentNode.sheet.cssRules[idx] !== undefined) {
+            var ser2 = style_text.parentNode.sheet.cssRules[idx].selectorText;
+            is(ser2, ser1, "parse+serialize of selector \"" + selector +
+                           "\" is idempotent");
+        }
 
         ifdoc.body.innerHTML = "";
         style_text.data = "";
 
         // And now test that when we clone the style sheet, we end up
         // with the same selector (serializes to same string, and
         // matches the same things).
         zi = ++gCounter;
@@ -1184,16 +1189,17 @@ function run() {
     test_balanced_unparseable(":-moz-any()");
     test_balanced_unparseable(":-moz-any(div p)");
     test_balanced_unparseable(":-moz-any(div ~ p)");
     test_balanced_unparseable(":-moz-any(div~p)");
     test_balanced_unparseable(":-moz-any(div + p)");
     test_balanced_unparseable(":-moz-any(div+p)");
     test_balanced_unparseable(":-moz-any(div > p)");
     test_balanced_unparseable(":-moz-any(div>p)");
+    test_balanced_unparseable(":-moz-any('foo')");
     test_parseable(":-moz-any(div, p)");
     test_parseable(":-moz-any( div , p  )");
     test_parseable(":-moz-any(div,p)");
     test_parseable(":-moz-any(div)");
     test_parseable(":-moz-any(div,p,:link,span:focus)");
     test_parseable(":-moz-any(:active,:focus)");
     test_parseable(":-moz-any(:active,:link:focus)");
     test_balanced_unparseable(":-moz-any(div,:nonexistentpseudo)");
@@ -1242,16 +1248,19 @@ function run() {
     // CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (none of which are
     // unprefixed).
     test_parseable("::-moz-color-swatch:hover");
     test_balanced_unparseable("::-moz-color-swatch:not(.foo)");
     test_balanced_unparseable("::-moz-color-swatch:first-child");
     test_balanced_unparseable("::-moz-color-swatch:hover#foo");
     test_balanced_unparseable(".foo::after:not(.bar) ~ h3");
 
+    test_parseable(":-moz-broken");
+    test_parseable(":-moz-loading");
+
     run_deferred_tests();
 }
 
 var deferred_tests = [];
 
 function defer_clonedoc_tests(docurl, onloadfunc)
 {
     deferred_tests.push( { docurl: docurl, onloadfunc: onloadfunc } );
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -29,17 +29,17 @@ cfg-if = "0.1.0"
 cssparser = "0.12"
 encoding = {version = "0.2", optional = true}
 euclid = "0.11"
 fnv = "1.0"
 heapsize = "0.3.0"
 heapsize_derive = {version = "0.1", optional = true}
 html5ever-atoms = {version = "0.2", optional = true}
 lazy_static = "0.2"
-log = "0.3.5"
+log = "0.3"
 matches = "0.1"
 nsstring_vendor = {path = "gecko_bindings/nsstring_vendor", optional = true}
 num-integer = "0.1.32"
 num-traits = "0.1.32"
 ordered-float = "0.4"
 owning_ref = "0.2.2"
 parking_lot = "0.3.3"
 pdqsort = "0.1.0"
@@ -55,11 +55,12 @@ servo_url = {path = "../url"}
 time = "0.1"
 unicode-segmentation = "1.0"
 
 [target.'cfg(windows)'.dependencies]
 kernel32-sys = "0.2"
 
 [build-dependencies]
 lazy_static = "0.2"
+log = "0.3"
 bindgen = { version = "0.22", optional = true }
 regex = {version = "0.2", optional = true}
 walkdir = "1.0"
--- a/servo/components/style/build.rs
+++ b/servo/components/style/build.rs
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #[cfg(feature = "gecko")]
 #[macro_use]
 extern crate lazy_static;
 #[cfg(feature = "bindgen")]
 extern crate bindgen;
 #[cfg(feature = "bindgen")]
+extern crate log;
+#[cfg(feature = "bindgen")]
 extern crate regex;
 extern crate walkdir;
 
 use std::env;
 use std::path::Path;
 use std::process::{Command, exit};
 use walkdir::WalkDir;
 
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -512,16 +512,59 @@ mod bindings {
             fixups.push(Fixup {
                 pat: format!("root::{}", ty.gecko),
                 rep: format!("::gecko_bindings::structs::{}", gecko_name)
             });
         }
         write_binding_file(builder, structs_file(build_type), &fixups);
     }
 
+    pub fn setup_logging() {
+        use log;
+        use std::fs;
+
+        struct BuildLogger {
+            file: Option<Mutex<fs::File>>,
+            filter: String,
+        }
+
+        impl log::Log for BuildLogger {
+            fn enabled(&self, meta: &log::LogMetadata) -> bool {
+                self.file.is_some() && meta.target().contains(&self.filter)
+            }
+
+            fn log(&self, record: &log::LogRecord) {
+                if !self.enabled(record.metadata()) {
+                    return;
+                }
+
+                let mut file = self.file.as_ref().unwrap().lock().unwrap();
+                let _ =
+                    writeln!(file, "{} - {} - {} @ {}:{}",
+                             record.level(),
+                             record.target(),
+                             record.args(),
+                             record.location().file(),
+                             record.location().line());
+            }
+        }
+
+        log::set_logger(|log_level| {
+            log_level.set(log::LogLevelFilter::Debug);
+            Box::new(BuildLogger {
+                file: env::var("STYLO_BUILD_LOG").ok().and_then(|path| {
+                    fs::File::create(path).ok().map(Mutex::new)
+                }),
+                filter: env::var("STYLO_BUILD_FILTER").ok()
+                    .unwrap_or_else(|| "bindgen".to_owned()),
+            })
+        })
+        .expect("Failed to set logger.");
+    }
+
     pub fn generate_bindings() {
         let mut builder = Builder::get_initial_builder(BuildType::Release)
             .disable_name_namespacing()
             .with_codegen_config(CodegenConfig {
                 functions: true,
                 ..CodegenConfig::nothing()
             })
             .header(add_include("mozilla/ServoBindings.h"))
@@ -706,16 +749,18 @@ mod bindings {
     use std::fs;
     use std::path::{Path, PathBuf};
     use super::common::*;
 
     lazy_static! {
         static ref BINDINGS_PATH: PathBuf = Path::new(file!()).parent().unwrap().join("gecko_bindings");
     }
 
+    pub fn setup_logging() {}
+
     pub fn generate_structs(build_type: BuildType) {
         let file = structs_file(build_type);
         let source = BINDINGS_PATH.join(file);
         println!("cargo:rerun-if-changed={}", source.display());
         fs::copy(source, OUTDIR_PATH.join(file)).unwrap();
     }
 
     pub fn generate_bindings() {
@@ -726,16 +771,17 @@ mod bindings {
 }
 
 pub fn generate() {
     use self::common::*;
     use std::fs;
     use std::thread;
     println!("cargo:rerun-if-changed=build_gecko.rs");
     fs::create_dir_all(&*OUTDIR_PATH).unwrap();
+    bindings::setup_logging();
     let threads = vec![
         thread::spawn(|| bindings::generate_structs(BuildType::Debug)),
         thread::spawn(|| bindings::generate_structs(BuildType::Release)),
         thread::spawn(|| bindings::generate_bindings()),
     ];
     for t in threads.into_iter() {
         t.join().unwrap();
     }
--- a/servo/components/style/element_state.rs
+++ b/servo/components/style/element_state.rs
@@ -4,17 +4,17 @@
 
 //! States elements can be in.
 
 #![deny(missing_docs)]
 
 bitflags! {
     #[doc = "Event-based element states."]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub flags ElementState: u16 {
+    pub flags ElementState: u32 {
         #[doc = "The mouse is down on this element. \
                  https://html.spec.whatwg.org/multipage/#selector-active \
                  FIXME(#7333): set/unset this when appropriate"]
         const IN_ACTIVE_STATE = 0x01,
         #[doc = "This element has focus. \
                  https://html.spec.whatwg.org/multipage/#selector-focus"]
         const IN_FOCUS_STATE = 0x02,
         #[doc = "The mouse is hovering over this element. \
@@ -38,12 +38,26 @@ bitflags! {
         #[doc = "https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class"]
         const IN_FULLSCREEN_STATE = 0x200,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-valid"]
         const IN_VALID_STATE = 0x400,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-invalid"]
         const IN_INVALID_STATE = 0x800,
         #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-valid"]
         const IN_MOZ_UI_VALID_STATE = 0x1000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken"]
+        const IN_BROKEN_STATE = 0x2000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-user-disabled"]
+        const IN_USER_DISABLED_STATE = 0x4000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-suppressed"]
+        const IN_SUPPRESSED_STATE = 0x8000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading"]
+        const IN_LOADING_STATE = 0x10000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-blocked"]
+        const IN_HANDLER_BLOCKED_STATE = 0x20000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-disabled"]
+        const IN_HANDLER_DISABLED_STATE = 0x40000,
+        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-crashed"]
+        const IN_HANDLER_CRASHED_STATE = 0x80000,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-read-write"]
-        const IN_READ_WRITE_STATE = 0x2000,
+        const IN_READ_WRITE_STATE = 0x100000,
     }
 }
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -44,16 +44,26 @@ macro_rules! apply_non_ts_list {
                 ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
                 ("checked", Checked, checked, IN_CHECKED_STATE, _),
                 ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
                 ("placeholder-shown", PlaceholderShown, placeholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
                 ("target", Target, target, IN_TARGET_STATE, _),
                 ("valid", Valid, valid, IN_VALID_STATE, _),
                 ("invalid", Invalid, invalid, IN_INVALID_STATE, _),
                 ("-moz-ui-valid", MozUIValid, mozUIValid, IN_MOZ_UI_VALID_STATE, _),
+                ("-moz-broken", MozBroken, mozBroken, IN_BROKEN_STATE, _),
+                ("-moz-user-disabled", MozUserDisabled, mozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_INTERNAL),
+                ("-moz-suppressed", MozSuppressed, mozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_INTERNAL),
+                ("-moz-loading", MozLoading, mozLoading, IN_LOADING_STATE, _),
+                ("-moz-handler-blocked", MozHandlerBlocked, mozHandlerBlocked, IN_HANDLER_BLOCKED_STATE,
+                    PSEUDO_CLASS_INTERNAL),
+                ("-moz-handler-disabled", MozHandlerDisabled, mozHandlerDisabled, IN_HANDLER_DISABLED_STATE,
+                    PSEUDO_CLASS_INTERNAL),
+                ("-moz-handler-crashed", MozHandlerCrashed, mozHandlerCrashed, IN_HANDLER_CRASHED_STATE,
+                    PSEUDO_CLASS_INTERNAL),
                 ("read-write", ReadWrite, _, IN_READ_WRITE_STATE, _),
                 ("read-only", ReadOnly, _, IN_READ_WRITE_STATE, _),
 
                 ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_INTERNAL),
                 ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_INTERNAL),
             ],
             string: [
                 ("-moz-system-metric", MozSystemMetric, mozSystemMetric, _, PSEUDO_CLASS_INTERNAL),
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -133,17 +133,17 @@ impl ::selectors::MatchAttr for GeckoEle
                                                   value.as_ptr())
         }
     }
 }
 
 impl ElementSnapshot for GeckoElementSnapshot {
     fn state(&self) -> Option<ElementState> {
         if self.has_any(Flags::State) {
-            Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState as u16 }))
+            Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState as u32 }))
         } else {
             None
         }
     }
 
     #[inline]
     fn has_attrs(&self) -> bool {
         self.has_any(Flags::Attributes)
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -442,17 +442,17 @@ impl<'le> TElement for GeckoElement<'le>
 
     fn get_transition_rule(&self, pseudo: Option<&PseudoElement>)
                            -> Option<Arc<RwLock<PropertyDeclarationBlock>>> {
         get_animation_rule(self, pseudo, CascadeLevel::Transitions)
     }
 
     fn get_state(&self) -> ElementState {
         unsafe {
-            ElementState::from_bits_truncate(Gecko_ElementState(self.0))
+            ElementState::from_bits_truncate(Gecko_ElementState(self.0) as u32)
         }
     }
 
     #[inline]
     fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool {
         unsafe {
             bindings::Gecko_HasAttr(self.0,
                                     namespace.0.as_ptr(),
@@ -675,17 +675,24 @@ impl<'le> ::selectors::Element for Gecko
             NonTSPseudoClass::Checked |
             NonTSPseudoClass::ReadWrite |
             NonTSPseudoClass::Fullscreen |
             NonTSPseudoClass::Indeterminate |
             NonTSPseudoClass::PlaceholderShown |
             NonTSPseudoClass::Target |
             NonTSPseudoClass::Valid |
             NonTSPseudoClass::Invalid |
-            NonTSPseudoClass::MozUIValid => {
+            NonTSPseudoClass::MozUIValid |
+            NonTSPseudoClass::MozBroken |
+            NonTSPseudoClass::MozUserDisabled |
+            NonTSPseudoClass::MozSuppressed |
+            NonTSPseudoClass::MozLoading |
+            NonTSPseudoClass::MozHandlerBlocked |
+            NonTSPseudoClass::MozHandlerDisabled |
+            NonTSPseudoClass::MozHandlerCrashed => {
                 self.get_state().contains(pseudo_class.state_flag())
             },
             NonTSPseudoClass::ReadOnly => {
                 !self.get_state().contains(pseudo_class.state_flag())
             }
 
             NonTSPseudoClass::MozTableBorderNonzero |
             NonTSPseudoClass::MozBrowserFrame => unsafe {
--- a/servo/components/style/properties/longhand/padding.mako.rs
+++ b/servo/components/style/properties/longhand/padding.mako.rs
@@ -8,16 +8,16 @@
 
 % for side in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
     ${helpers.predefined_type("padding-%s" % side[0], "LengthOrPercentage",
-                               "computed::LengthOrPercentage::Length(Au(0))",
-                               "parse_non_negative",
-                               alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
-                               needs_context=False,
-                               animatable=True,
-                               logical = side[1],
-                               spec = spec)}
+                              "computed::LengthOrPercentage::Length(Au(0))",
+                              "parse_non_negative",
+                              alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
+                              needs_context=False,
+                              animatable=True,
+                              logical = side[1],
+                              spec = spec)}
 % endfor
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -317,8 +317,94 @@
     ${helpers.predefined_type("grid-auto-%ss" % kind,
                               "TrackSize",
                               "Default::default()",
                               animatable=False,
                               spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                               products="gecko",
                               boxed=True)}
 % endfor
+
+<%helpers:longhand name="grid-auto-flow"
+        spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
+        products="none"
+        animatable="False">
+    use std::fmt;
+    use style_traits::ToCss;
+    use values::HasViewportPercentage;
+    use values::computed::ComputedValueAsSpecified;
+
+    pub type SpecifiedValue = computed_value::T;
+
+    pub mod computed_value {
+        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub enum AutoFlow {
+            Row,
+            Column,
+        }
+
+        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub struct T {
+            pub autoflow: AutoFlow,
+            pub dense: bool,
+        }
+    }
+
+    no_viewport_percentage!(SpecifiedValue);
+    impl ComputedValueAsSpecified for SpecifiedValue {}
+
+    impl ToCss for computed_value::T {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            dest.write_str(match self.autoflow {
+                computed_value::AutoFlow::Column => "column",
+                computed_value::AutoFlow::Row => "row"
+            })?;
+
+            if self.dense { dest.write_str(" dense")?; }
+            Ok(())
+        }
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::T {
+            autoflow: computed_value::AutoFlow::Row,
+            dense: false
+        }
+    }
+
+    /// [ row | column ] || dense
+    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        use self::computed_value::AutoFlow;
+
+        let mut value = None;
+        let mut dense = false;
+
+        while !input.is_exhausted() {
+            match_ignore_ascii_case! { &input.expect_ident()?,
+                "row" if value.is_none() => {
+                    value = Some(AutoFlow::Row);
+                    continue
+                },
+                "column" if value.is_none() => {
+                    value = Some(AutoFlow::Column);
+                    continue
+                },
+                "dense" if !dense => {
+                    dense = true;
+                    continue
+                },
+                _ => return Err(())
+            }
+        }
+
+        if value.is_some() || dense {
+            Ok(computed_value::T {
+                autoflow: value.unwrap_or(AutoFlow::Row),
+                dense: dense,
+            })
+        } else {
+            Err(())
+        }
+    }
+</%helpers:longhand>
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -364,19 +364,16 @@ impl NoCalcLength {
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Length {
     /// The internal length type that cannot parse `calc`
     NoCalc(NoCalcLength),
     /// A calc expression.
     ///
     /// https://drafts.csswg.org/css-values/#calc-notation
-    ///
-    /// TODO(emilio): We have more `Calc` variants around, we should only use
-    /// one.
     Calc(Box<CalcLengthOrPercentage>, AllowedNumericType),
 }
 
 impl From<NoCalcLength> for Length {
     #[inline]
     fn from(len: NoCalcLength) -> Self {
         Length::NoCalc(len)
     }
--- a/servo/tests/unit/style/parsing/position.rs
+++ b/servo/tests/unit/style/parsing/position.rs
@@ -137,8 +137,33 @@ fn test_vertical_position() {
     assert!(parse(VerticalPosition::parse, "left 20px").is_err());
     assert!(parse(VerticalPosition::parse, "20px right").is_err());
     assert!(parse(VerticalPosition::parse, "left center").is_err());
     assert!(parse(VerticalPosition::parse, "bottom top").is_err());
     assert!(parse(VerticalPosition::parse, "left top").is_err());
     assert!(parse(VerticalPosition::parse, "left right").is_err());
     assert!(parse(VerticalPosition::parse, "20px 30px").is_err());
 }
+
+#[test]
+fn test_grid_auto_flow() {
+    use style::properties::longhands::grid_auto_flow;
+
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "row dense", "row dense");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "dense row", "row dense");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "column dense", "column dense");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "dense column", "column dense");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "dense", "row dense");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "row", "row");
+    assert_roundtrip_with_context!(grid_auto_flow::parse, "column", "column");
+
+    // Neither row, column or dense can be repeated
+    assert!(parse(grid_auto_flow::parse, "dense dense").is_err());
+    assert!(parse(grid_auto_flow::parse, "row row").is_err());
+    assert!(parse(grid_auto_flow::parse, "column column").is_err());
+    assert!(parse(grid_auto_flow::parse, "row dense dense").is_err());
+    assert!(parse(grid_auto_flow::parse, "column dense dense").is_err());
+
+    // Only row, column, dense idents are allowed
+    assert!(parse(grid_auto_flow::parse, "dense 1").is_err());
+    assert!(parse(grid_auto_flow::parse, "column 'dense'").is_err());
+    assert!(parse(grid_auto_flow::parse, "column 2px dense").is_err());
+}
--- a/toolkit/components/telemetry/.flake8
+++ b/toolkit/components/telemetry/.flake8
@@ -1,5 +1,5 @@
 [flake8]
 # See http://pep8.readthedocs.io/en/latest/intro.html#configuration
-ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402, E128, E501, E713, E202, W602, E127, W601
+ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402, E128, E501, E202, W602, E127, W601
 max-line-length = 99
 filename = *.py, +.lint
--- a/toolkit/components/telemetry/gen-event-enum.py
+++ b/toolkit/components/telemetry/gen-event-enum.py
@@ -35,17 +35,17 @@ def main(output, *filenames):
     if len(filenames) > 1:
         raise Exception('We don\'t support loading from more than one file.')
     events = parse_events.load_events(filenames[0])
 
     grouped = dict()
     index = 0
     for e in events:
         category = e.category
-        if not category in grouped:
+        if category not in grouped:
             grouped[category] = []
         grouped[category].append((index, e))
         index += len(e.enum_labels)
 
     # Write the enum file.
     print(banner, file=output)
     print(file_header, file=output)
 
--- a/toolkit/components/telemetry/histogram_tools.py
+++ b/toolkit/components/telemetry/histogram_tools.py
@@ -129,17 +129,17 @@ symbol that should guard C/C++ definitio
             'linear': 'LINEAR',
             'exponential': 'EXPONENTIAL',
         }
         table_dispatch(self.kind(), table,
                        lambda k: self._set_nsITelemetry_kind(k))
         datasets = {'opt-in': 'DATASET_RELEASE_CHANNEL_OPTIN',
                     'opt-out': 'DATASET_RELEASE_CHANNEL_OPTOUT'}
         value = definition.get('releaseChannelCollection', 'opt-in')
-        if not value in datasets:
+        if value not in datasets:
             raise DefinitionException, "unknown release channel collection policy for " + name
         self._dataset = "nsITelemetry::" + datasets[value]
 
     def name(self):
         """Return the name of the histogram."""
         return self._name
 
     def description(self):
@@ -362,24 +362,24 @@ associated with the histogram.  Returns 
                 definition["keyed"] = True
 
         def nice_type_name(t):
             if t is basestring:
                 return "string"
             return t.__name__
 
         for key, key_type in type_checked_fields.iteritems():
-            if not key in definition:
+            if key not in definition:
                 continue
             if not isinstance(definition[key], key_type):
                 raise ValueError, ('value for key "{0}" in Histogram "{1}" '
                         'should be {2}').format(key, name, nice_type_name(key_type))
 
         for key, key_type in type_checked_list_fields.iteritems():
-            if not key in definition:
+            if key not in definition:
                 continue
             if not all(isinstance(x, key_type) for x in definition[key]):
                 raise ValueError, ('all values for list "{0}" in Histogram "{1}" '
                         'should be {2}').format(key, name, nice_type_name(key_type))
 
     @staticmethod
     def check_keys(name, definition, allowed_keys):
         for key in definition.iterkeys():
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -164,17 +164,17 @@ class EventData:
             string_check(self.identifier, field='objects', value=obj,
                          min_length=1, max_length=MAX_OBJECT_NAME_LENGTH,
                          regex=IDENTIFIER_PATTERN)
 
         # Check release_channel_collection
         rcc_key = 'release_channel_collection'
         rcc = definition.get(rcc_key, 'opt-in')
         allowed_rcc = ["opt-in", "opt-out"]
-        if not rcc in allowed_rcc:
+        if rcc not in allowed_rcc:
             raise ValueError, "%s: value for %s should be one of: %s" %\
                               (self.identifier, rcc_key, ", ".join(allowed_rcc))
 
         # Check record_in_processes.
         record_in_processes = definition.get('record_in_processes')
         for proc in record_in_processes:
             if not utils.is_valid_process_name(proc):
                 raise ValueError(self.identifier + ': unknown value in record_in_processes: ' + proc)
@@ -185,17 +185,17 @@ class EventData:
             raise ValueError, "%s: number of extra_keys exceeds limit %d" %\
                               (self.identifier, MAX_EXTRA_KEYS_COUNT)
         for key in extra_keys.iterkeys():
             string_check(self.identifier, field='extra_keys', value=key,
                          min_length=1, max_length=MAX_EXTRA_KEY_NAME_LENGTH,
                          regex=IDENTIFIER_PATTERN)
 
         # Check expiry.
-        if not 'expiry_version' in definition and not 'expiry_date' in definition:
+        if 'expiry_version' not in definition and 'expiry_date' not in definition:
             raise KeyError, "%s: event is missing an expiration - either expiry_version or expiry_date is required" %\
                             (self.identifier)
         expiry_date = definition.get('expiry_date')
         if expiry_date and isinstance(expiry_date, basestring) and expiry_date != 'never':
             if not re.match(DATE_PATTERN, expiry_date):
                 raise ValueError, "%s: event has invalid expiry_date, it should be either 'never' or match this format: %s" %\
                                   (self.identifier, DATE_PATTERN)
             # Parse into date.
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -510,17 +510,18 @@
         </xul:vbox>
       </xul:hbox>
       <xul:hbox class="popup-notification-button-container">
         <children includes="button"/>
         <xul:button anonid="secondarybutton"
                     class="popup-notification-button"
                     xbl:inherits="oncommand=secondarybuttoncommand,label=secondarybuttonlabel,accesskey=secondarybuttonaccesskey,hidden=secondarybuttonhidden"/>
         <xul:toolbarseparator xbl:inherits="hidden=dropmarkerhidden"/>
-        <xul:button type="menu"
+        <xul:button anonid="menubutton"
+                    type="menu"
                     class="popup-notification-button popup-notification-dropmarker"
                     xbl:inherits="onpopupshown=dropmarkerpopupshown,hidden=dropmarkerhidden">
           <xul:menupopup anonid="menupopup"
                          position="after_end"
                          xbl:inherits="oncommand=menucommand">
             <children/>
           </xul:menupopup>
         </xul:button>
@@ -543,14 +544,17 @@
         document.getAnonymousElementByAttribute(this, "anonid", "closebutton");
       </field>
       <field name="button" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "button");
       </field>
       <field name="secondaryButton" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton");
       </field>
+      <field name="menubutton" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "menubutton");
+      </field>
       <field name="menupopup" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "menupopup");
       </field>
     </implementation>
   </binding>
 </bindings>