Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 18 May 2016 12:02:42 -0400
changeset 297935 c4449eab07d39e20ea315603f1b1863eeed7dcfe
parent 297753 47b83c8478a06374aa9306122a778654b912f713 (current diff)
parent 297934 cf774623fa4738bf43cbb9bb135a48e118362b2e (diff)
child 297936 4cfa7a2cefa7701377c73c468ddf5671e2e73b05
child 297992 c901fbc06bf0003cbd85e42f22b62eea6b8b7e7f
push id19274
push userryanvm@gmail.com
push dateWed, 18 May 2016 16:14:35 +0000
treeherderfx-team@4cfa7a2cefa7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
Merge inbound to m-c. a=merge CLOSED TREE
.eslintignore
mobile/android/chrome/content/browser.js
mobile/android/components/extensions/extension.svg
testing/firefox-ui/tests/puppeteer/mn-restartless-unsigned.xpi
testing/web-platform/meta/IndexedDB/interfaces.worker.js.ini
testing/web-platform/meta/html/semantics/forms/constraints/form-validation-validate.html.ini
testing/web-platform/meta/html/semantics/selectors/pseudo-classes/dir.html.ini
testing/web-platform/meta/html/semantics/selectors/pseudo-classes/dir01.html.ini
testing/web-platform/meta/html/semantics/tabular-data/the-table-element/table-rows.html.ini
testing/web-platform/meta/web-animations/animatable/animate.html.ini
testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini
testing/web-platform/meta/web-animations/animation/constructor.html.ini
testing/web-platform/meta/web-animations/keyframe-effect/constructor.html.ini
testing/web-platform/meta/web-animations/keyframe-effect/setKeyframes.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/015.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/016.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/017.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/018.html.ini
testing/web-platform/tests/web-animations/animatable/animate.html
testing/web-platform/tests/web-animations/animation-effect-timing/delay.html
testing/web-platform/tests/web-animations/animation-effect-timing/direction.html
testing/web-platform/tests/web-animations/animation-effect-timing/duration.html
testing/web-platform/tests/web-animations/animation-effect-timing/easing.html
testing/web-platform/tests/web-animations/animation-effect-timing/endDelay.html
testing/web-platform/tests/web-animations/animation-effect-timing/fill.html
testing/web-platform/tests/web-animations/animation-effect-timing/getAnimations.html
testing/web-platform/tests/web-animations/animation-effect-timing/getComputedStyle.html
testing/web-platform/tests/web-animations/animation-effect-timing/iterationStart.html
testing/web-platform/tests/web-animations/animation-effect-timing/iterations.html
testing/web-platform/tests/web-animations/animation-timeline/document-timeline.html
testing/web-platform/tests/web-animations/animation-timeline/idlharness.html
testing/web-platform/tests/web-animations/animation/cancel.html
testing/web-platform/tests/web-animations/animation/constructor.html
testing/web-platform/tests/web-animations/animation/finish.html
testing/web-platform/tests/web-animations/animation/finished.html
testing/web-platform/tests/web-animations/animation/id.html
testing/web-platform/tests/web-animations/animation/oncancel.html
testing/web-platform/tests/web-animations/animation/onfinish.html
testing/web-platform/tests/web-animations/animation/pause.html
testing/web-platform/tests/web-animations/animation/play.html
testing/web-platform/tests/web-animations/animation/playState.html
testing/web-platform/tests/web-animations/animation/playbackRate.html
testing/web-platform/tests/web-animations/animation/ready.html
testing/web-platform/tests/web-animations/animation/reverse.html
testing/web-platform/tests/web-animations/animation/startTime.html
testing/web-platform/tests/web-animations/document/getAnimations.html
testing/web-platform/tests/web-animations/keyframe-effect/constructor.html
testing/web-platform/tests/web-animations/keyframe-effect/effect-easing.html
testing/web-platform/tests/web-animations/keyframe-effect/getComputedTiming.html
testing/web-platform/tests/web-animations/keyframe-effect/processing-a-keyframes-argument.html
testing/web-platform/tests/web-animations/keyframe-effect/setKeyframes.html
testing/web-platform/tests/web-animations/keyframe-effect/setTarget.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -35,16 +35,18 @@ netwerk/**
 nsprpub/**
 other-licenses/**
 parser/**
 probes/**
 python/**
 rdf/**
 startupcache/**
 testing/**
+!testing/eslint-plugin-mozilla/
+testing/eslint-plugin-mozilla/node_modules/**
 tools/**
 uriloader/**
 view/**
 widget/**
 xpcom/**
 xpfe/**
 xulrunner/**
 
--- a/Makefile.in
+++ b/Makefile.in
@@ -281,20 +281,22 @@ ifeq (,$(filter-out Linux SunOS,$(OS_ARC
 MAKE_SYM_STORE_ARGS := -c --vcs-info
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 MAKE_SYM_STORE_PATH := $(DIST)/bin
 endif
 MAKE_SYM_STORE_ARGS += --install-manifest=$(DEPTH)/_build_manifests/install/dist_include,$(DIST)/include
 
 SYM_STORE_SOURCE_DIRS := $(topsrcdir)
 
+ifdef MOZ_CRASHREPORTER
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 
 SYMBOL_INDEX_NAME = \
   $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)-$(CPU_ARCH)-symbols.txt
+endif
 
 buildsymbols:
 ifdef MOZ_CRASHREPORTER
 	echo building symbol store
 	$(RM) -r $(DIST)/crashreporter-symbols
 	$(RM) '$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip'
 	$(RM) '$(DIST)/$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 	$(NSINSTALL) -D $(DIST)/crashreporter-symbols
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -22,16 +22,17 @@
 
 using namespace mozilla::a11y;
 
 ApplicationAccessible::ApplicationAccessible() :
   AccessibleWrap(nullptr, nullptr)
 {
   mType = eApplicationType;
   mAppInfo = do_GetService("@mozilla.org/xre/app-info;1");
+  MOZ_ASSERT(mAppInfo, "no application info");
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(ApplicationAccessible, Accessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessible
 
 ENameValueFlag
--- a/accessible/generic/ApplicationAccessible.h
+++ b/accessible/generic/ApplicationAccessible.h
@@ -55,39 +55,45 @@ public:
   // ActionAccessible
   virtual KeyBinding AccessKey() const override;
 
   // ApplicationAccessible
   void Init();
 
   void AppName(nsAString& aName) const
   {
+    MOZ_ASSERT(mAppInfo, "no application info");
+
     if (mAppInfo) {
       nsAutoCString cname;
       mAppInfo->GetName(cname);
       AppendUTF8toUTF16(cname, aName);
     }
   }
 
   void AppVersion(nsAString& aVersion) const
   {
+    MOZ_ASSERT(mAppInfo, "no application info");
+
     if (mAppInfo) {
       nsAutoCString cversion;
       mAppInfo->GetVersion(cversion);
       AppendUTF8toUTF16(cversion, aVersion);
     }
   }
 
   void PlatformName(nsAString& aName) const
   {
     aName.AssignLiteral("Gecko");
   }
 
   void PlatformVersion(nsAString& aVersion) const
   {
+    MOZ_ASSERT(mAppInfo, "no application info");
+
     if (mAppInfo) {
       nsAutoCString cversion;
       mAppInfo->GetPlatformVersion(cversion);
       AppendUTF8toUTF16(cversion, aVersion);
     }
   }
 
 protected:
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -532,22 +532,22 @@ DocAccessible::RelativeBounds(nsIFrame**
 
   return bounds;
 }
 
 // DocAccessible protected member
 nsresult
 DocAccessible::AddEventListeners()
 {
-  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
+  nsCOMPtr<nsIDocShell> docShell(mDocumentNode->GetDocShell());
 
   // We want to add a command observer only if the document is content and has
   // an editor.
-  if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
-    nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
+  if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
+    nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
     if (commandManager)
       commandManager->AddCommandObserver(this, "obs_documentCreated");
   }
 
   SelectionMgr()->AddDocSelectionListener(mPresShell);
 
   // Add document observer.
   mDocumentNode->AddObserver(this);
@@ -562,22 +562,22 @@ DocAccessible::RemoveEventListeners()
   // Remove scroll position listener
   RemoveScrollListener();
 
   NS_ASSERTION(mDocumentNode, "No document during removal of listeners.");
 
   if (mDocumentNode) {
     mDocumentNode->RemoveObserver(this);
 
-    nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
-    NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
+    nsCOMPtr<nsIDocShell> docShell(mDocumentNode->GetDocShell());
+    NS_ASSERTION(docShell, "doc should support nsIDocShellTreeItem.");
 
-    if (docShellTreeItem) {
-      if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
-        nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
+    if (docShell) {
+      if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
+        nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
         if (commandManager) {
           commandManager->RemoveCommandObserver(this, "obs_documentCreated");
         }
       }
     }
   }
 
   if (mScrollWatchTimer) {
--- a/accessible/tests/mochitest/name/test_general.xul
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -40,17 +40,17 @@
 
       // Multiple relations. The value of 'aria-labelledby' contains the IDs
       // of elements. Gets the name from text nodes of those elements.
       testName("btn_labelledby_texts", "text1 text2");
 
       // Trick cases. Self and recursive referencing.
       testName("rememberHistoryDays", "Remember 3 days");
       testName("historyDays", "Remember 3 days");
-      testName("rememberAfter", null); // XUL labels doesn't allow name from subtree
+      testName("rememberAfter", "days");
 
       // Anonymous content (see name.xbl#third)
       var anonBtn = getAccessible("labelledby_box_anon").lastChild;
       testName(anonBtn, "It's a cool button");
 
       //////////////////////////////////////////////////////////////////////////
       // Name from subtree (single relation labelled_by).
       
@@ -168,16 +168,21 @@
       // the visible children (bug 443081)
       testName("lb_opt1_children_hidden", "i am visible");
 
 
       //////////////////////////////////////////////////////////////////////////
       // Name from aria-labelledby: menuitem label+ listitem label
       testName("li_labelledby", "Show an Alert The moment the event starts");
 
+      //////////////////////////////////////////////////////////////////////////
+      // groupbox labeling from caption label or its sub tree
+      testName("groupbox", "Some caption");
+      testName("groupbox2", "Some caption");
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
@@ -335,16 +340,26 @@
   <box id="box_children" role="button">14</box>
 
   <!-- name from children, hidden children -->
   <vbox role="listbox" tabindex="0">
     <hbox id="lb_opt1_children_hidden" role="option" tabindex="0">
       <description>i am visible</description>
       <description style="display:none">i am hidden</description>
     </hbox>
+
+    <!-- Name from label or sub tree -->
+    <groupbox id="groupbox">
+      <caption label="Some caption" />
+      <checkbox label="some checkbox label" />
+    </groupbox>
+    <groupbox id="groupbox2">
+      <caption><label>Some caption</label></caption>
+      <checkbox label="some checkbox label" />
+    </groupbox>
   </vbox>
 
   <!-- bug 441991; create name from other menuitem label listitem's own label -->
   <hbox>
     <listbox>
     <listitem id="li_labelledby"
               label="The moment the event starts"
               aria-labelledby="menuitem-DISPLAY li_labelledby"/>
--- a/accessible/xul/XULElementAccessibles.cpp
+++ b/accessible/xul/XULElementAccessibles.cpp
@@ -64,17 +64,17 @@ XULLabelAccessible::Shutdown()
 ENameValueFlag
 XULLabelAccessible::NativeName(nsString& aName)
 {
   // if the value attr doesn't exist, the screen reader must get the accessible text
   // from the accessible text interface or from the children
   if (mValueTextLeaf)
     return mValueTextLeaf->Name(aName);
 
-  return eNameOK;
+  return Accessible::NativeName(aName);
 }
 
 role
 XULLabelAccessible::NativeRole()
 {
   return roles::LABEL;
 }
 
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -352,17 +352,17 @@
     <key id="key_showAllTabs" command="Browser:ShowAllTabs" keycode="VK_TAB" modifiers="control,shift"/>
 
     <key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />
 
     <key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;" modifiers="accel,shift"/>
     <key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
 #ifdef XP_MACOSX
     <key id="key_sanitize_mac" command="Tools:Sanitize" keycode="VK_BACK" modifiers="accel,shift"/>
-    <key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" modifiers="accel"/>
+    <key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" modifiers="accel" reserved="true"/>
 #elifdef XP_UNIX
     <key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" command="cmd_quitApplication" modifiers="accel"/>
 #endif
 
 #ifdef FULL_BROWSER_WINDOW
     <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
 #endif
     <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -653,17 +653,22 @@
                      removable="false">
         <menupopup id="alltabs-popup"
                    position="after_end">
           <menuitem id="alltabs_undoCloseTab"
                     class="menuitem-iconic"
                     key="key_undoCloseTab"
                     label="&undoCloseTab.label;"
                     observes="History:UndoCloseTab"/>
-          <menuseparator id="alltabs-popup-separator"/>
+          <menuseparator id="alltabs-popup-separator-1"/>
+          <menu id="alltabs_containersTab"
+                label="&newUserContext.label;">
+            <menupopup id="alltabs_containersMenuTab" />
+          </menu>
+          <menuseparator id="alltabs-popup-separator-2"/>
         </menupopup>
       </toolbarbutton>
 
 #if !defined(MOZ_WIDGET_GTK) && !defined(MOZ_WIDGET_QT)
       <hbox class="private-browsing-indicator" skipintoolbarset="true"/>
 #endif
 #ifdef CAN_DRAW_IN_TITLEBAR
       <hbox class="titlebar-placeholder" type="caption-buttons"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6591,16 +6591,25 @@
             addEndImage().setAttribute("soundplaying", "true");
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="popupshowing">
       <![CDATA[
+        if (event.target.getAttribute('id') == "alltabs_containersMenuTab") {
+          createUserContextMenu(event);
+          return;
+        }
+
+        let containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
+        document.getElementById("alltabs-popup-separator-1").hidden = !containersEnabled;
+        document.getElementById("alltabs_containersTab").hidden = !containersEnabled;
+
         document.getElementById("alltabs_undoCloseTab").disabled =
           SessionStore.getClosedTabCount(window) == 0;
 
         var tabcontainer = gBrowser.tabContainer;
 
         // Listen for changes in the tab bar.
         tabcontainer.addEventListener("TabAttrModified", this, false);
         tabcontainer.addEventListener("TabClose", this, false);
@@ -6611,16 +6620,20 @@
           if (!tabs[i].pinned)
             this._createTabMenuItem(tabs[i]);
         }
         this._updateTabsVisibilityStatus();
       ]]></handler>
 
       <handler event="popuphidden">
       <![CDATA[
+        if (event.target.getAttribute('id') == "alltabs_containersMenuTab") {
+          return;
+        }
+
         // clear out the menu popup and remove the listeners
         for (let i = this.childNodes.length - 1; i > 0; i--) {
           let menuItem = this.childNodes[i];
           if (menuItem.tab) {
             menuItem.tab.mCorrespondingMenuitem = null;
             this.removeChild(menuItem);
           }
         }
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -270,8 +270,75 @@ add_task(function*() {
     is(rect.left, expectedX, "step " + (stepIndex + 1) + " x");
     is(rect.top, expectedY, "step " + (stepIndex + 1) + " y");
 
     yield hideSelectPopup(selectPopup);
   }
 
   yield BrowserTestUtils.removeTab(tab);
 });
+
+// Test that we get the right events when a select popup is changed.
+add_task(function* test_event_order() {
+  const URL = "data:text/html," + escape(PAGECONTENT_SMALL);
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: URL,
+  }, function*(browser) {
+    let menulist = document.getElementById("ContentSelectDropdown");
+    let selectPopup = menulist.menupopup;
+
+    yield openSelectPopup(selectPopup, true, "#one");
+
+    let eventsPromise = ContentTask.spawn(browser, null, function*() {
+      // According to https://html.spec.whatwg.org/#the-select-element,
+      // we want to fire input, change, and then click events on the
+      // <select> (in that order) when it has changed.
+      let expected = [
+        {
+          type: "input",
+          cancelable: false,
+        },
+        {
+          type: "change",
+          cancelable: false,
+        },
+        {
+          type: "mousedown",
+          cancelable: true,
+        },
+        {
+          type: "mouseup",
+          cancelable: true,
+        },
+        {
+          type: "click",
+          cancelable: true,
+        },
+      ];
+
+      return new Promise((resolve) => {
+        function onEvent(event) {
+          select.removeEventListener(event.type, onEvent);
+          let expectation = expected.shift();
+          Assert.equal(event.type, expectation.type,
+                       "Expected the right event order");
+          Assert.ok(event.bubbles, "All of these events should bubble");
+          Assert.equal(event.cancelable, expectation.cancelable,
+                       "Cancellation property should match");
+          if (!expected.length) {
+            resolve();
+          }
+        }
+
+        let select = content.document.getElementById("one");
+        for (let expectation of expected) {
+          select.addEventListener(expectation.type, onEvent);
+        }
+      });
+    });
+
+    EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
+    yield hideSelectPopup(selectPopup, false);
+    yield eventsPromise;
+  });
+});
+
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -4,16 +4,52 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 XPCOMUtils.defineLazyGetter(this, "History", () => {
   Cu.import("resource://gre/modules/PlacesUtils.jsm");
   return PlacesUtils.history;
 });
 
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+const {
+  normalizeTime,
+} = ExtensionUtils;
+
+/*
+ * Converts a nsINavHistoryResultNode into a plain object
+ *
+ * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode
+ */
+function convertNavHistoryResultNode(node) {
+  return {
+    id: node.pageGuid,
+    url: node.uri,
+    title: node.title,
+    lastVisitTime: PlacesUtils.toTime(node.time),
+    visitCount: node.accessCount,
+  };
+}
+
+/*
+ * Converts a nsINavHistoryContainerResultNode into an array of objects
+ *
+ * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryContainerResultNode
+ */
+function convertNavHistoryContainerResultNode(container) {
+  let results = [];
+  container.containerOpen = true;
+  for (let i = 0; i < container.childCount; i++) {
+    let node = container.getChild(i);
+    results.push(convertNavHistoryResultNode(node));
+  }
+  container.containerOpen = false;
+  return results;
+}
+
 extensions.registerSchemaAPI("history", "history", (extension, context) => {
   return {
     history: {
       deleteAll: function() {
         return History.clear();
       },
       deleteRange: function(filter) {
         let newFilter = {
@@ -23,11 +59,34 @@ extensions.registerSchemaAPI("history", 
         // History.removeVisitsByFilter returns a boolean, but our API should return nothing
         return History.removeVisitsByFilter(newFilter).then(() => undefined);
       },
       deleteUrl: function(details) {
         let url = details.url;
         // History.remove returns a boolean, but our API should return nothing
         return History.remove(url).then(() => undefined);
       },
+      search: function(query) {
+        let beginTime = (query.startTime == null) ?
+                          PlacesUtils.toPRTime(Date.now() - 24 * 60 * 60 * 1000) :
+                          PlacesUtils.toPRTime(normalizeTime(query.startTime));
+        let endTime = (query.endTime == null) ?
+                        Number.MAX_VALUE :
+                        PlacesUtils.toPRTime(normalizeTime(query.endTime));
+        if (beginTime > endTime) {
+          return Promise.reject({message: "The startTime cannot be after the endTime"});
+        }
+
+        let options = History.getNewQueryOptions();
+        options.sortingMode = options.SORT_BY_DATE_DESCENDING;
+        options.maxResults = query.maxResults || 100;
+
+        let historyQuery = History.getNewQuery();
+        historyQuery.searchTerms = query.text;
+        historyQuery.beginTime = beginTime;
+        historyQuery.endTime = endTime;
+        let queryResult = History.executeQuery(historyQuery, options).root;
+        let results = convertNavHistoryContainerResultNode(queryResult);
+        return Promise.resolve(results);
+      },
     },
   };
 });
--- a/browser/components/extensions/schemas/history.json
+++ b/browser/components/extensions/schemas/history.json
@@ -85,48 +85,60 @@
             "type": "string",
             "description": "The visit ID of the referrer."
           },
           "transition": {
             "$ref": "TransitionType",
             "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
           }
         }
+      },
+      {
+        "id": "HistoryTime",
+        "description": "A time specified as a Date object, a number or string representing milliseconds since the epoch, or an ISO 8601 string",
+        "choices": [
+          {
+            "type": "string",
+            "pattern": "^[1-9]\\d*$"
+          },
+          {
+            "$ref": "extensionTypes.Date"
+          }
+        ]
       }
     ],
     "functions": [
       {
         "name": "search",
-        "unsupported": true,
         "type": "function",
         "description": "Searches the history for the last visit time of each page matching the query.",
         "async": "callback",
         "parameters": [
           {
             "name": "query",
             "type": "object",
             "properties": {
               "text": {
                 "type": "string",
                 "description": "A free-text query to the history service.  Leave empty to retrieve all pages."
               },
               "startTime": {
-                "type": "number",
+                "$ref": "HistoryTime",
                 "optional": true,
-                "description": "Limit results to those visited after this date, represented in milliseconds since the epoch. If not specified, this defaults to 24 hours in the past."
+                "description": "Limit results to those visited after this date. If not specified, this defaults to 24 hours in the past."
               },
               "endTime": {
-                "type": "number",
+                "$ref": "HistoryTime",
                 "optional": true,
-                "description": "Limit results to those visited before this date, represented in milliseconds since the epoch."
+                "description": "Limit results to those visited before this date."
               },
               "maxResults": {
                 "type": "integer",
                 "optional": true,
-                "minimum": 0,
+                "minimum": 1,
                 "description": "The maximum number of results to retrieve.  Defaults to 100."
               }
             }
           },
           {
             "name": "callback",
             "type": "function",
             "parameters": [
--- a/browser/components/extensions/test/browser/browser_ext_history.js
+++ b/browser/components/extensions/test/browser/browser_ext_history.js
@@ -44,17 +44,17 @@ add_task(function* test_delete() {
   yield extension.awaitMessage("ready");
 
   let visits = [];
 
   // Add 5 visits for one uri and 3 visits for 3 others
   for (let i = 0; i < 8; ++i) {
     let baseUri = "http://mozilla.com/test_history/";
     let uri = (i > 4) ? `${baseUri}${i}/` : baseUri;
-    let dbDate = (Number(REFERENCE_DATE) + 3600 * 1000 * i) * 1000;
+    let dbDate = PlacesUtils.toPRTime(Number(REFERENCE_DATE) + 3600 * 1000 * i);
 
     let visit = {
       uri,
       title: "visit " + i,
       visitDate: dbDate,
     };
     visits.push(visit);
   }
@@ -66,30 +66,30 @@ add_task(function* test_delete() {
   let testUrl = visits[6].uri.spec;
   ok(yield PlacesTestUtils.isPageInDB(testUrl), "expected url found in history database");
 
   extension.sendMessage("delete-url", testUrl);
   yield extension.awaitMessage("url-deleted");
   is(yield PlacesTestUtils.isPageInDB(testUrl), false, "expected url not found in history database");
 
   let filter = {
-    startTime: visits[1].visitDate / 1000,
-    endTime: visits[3].visitDate / 1000,
+    startTime: PlacesUtils.toTime(visits[1].visitDate),
+    endTime: PlacesUtils.toTime(visits[3].visitDate),
   };
 
   extension.sendMessage("delete-range", filter);
   yield extension.awaitMessage("range-deleted");
 
   ok(yield PlacesTestUtils.isPageInDB(visits[0].uri), "expected uri found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 2, "2 visits for uri found in history database");
   ok(yield PlacesTestUtils.isPageInDB(visits[5].uri), "expected uri found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 1, "1 visit for uri found in history database");
 
-  filter.startTime = visits[0].visitDate / 1000;
-  filter.endTime = visits[5].visitDate / 1000;
+  filter.startTime = PlacesUtils.toTime(visits[0].visitDate);
+  filter.endTime = PlacesUtils.toTime(visits[5].visitDate);
 
   extension.sendMessage("delete-range", filter);
   yield extension.awaitMessage("range-deleted");
 
   is(yield PlacesTestUtils.isPageInDB(visits[0].uri), false, "expected uri not found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 0, "0 visits for uri found in history database");
   is(yield PlacesTestUtils.isPageInDB(visits[5].uri), false, "expected uri not found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 0, "0 visits for uri found in history database");
@@ -97,8 +97,95 @@ add_task(function* test_delete() {
   ok(yield PlacesTestUtils.isPageInDB(visits[7].uri), "expected uri found in history database");
 
   extension.sendMessage("delete-all");
   yield extension.awaitMessage("urls-deleted");
   is(PlacesUtils.history.hasHistoryEntries, false, "history is empty");
 
   yield extension.unload();
 });
+
+add_task(function* test_search() {
+  const SINGLE_VISIT_URL = "http://example.com/";
+  const DOUBLE_VISIT_URL = "http://example.com/2/";
+  const MOZILLA_VISIT_URL = "http://mozilla.com/";
+
+  function background() {
+    browser.test.onMessage.addListener(msg => {
+      browser.history.search({text: ""}).then(results => {
+        browser.test.sendMessage("empty-search", results);
+        return browser.history.search({text: "mozilla.com"});
+      }).then(results => {
+        browser.test.sendMessage("text-search", results);
+        return browser.history.search({text: "example.com", maxResults: 1});
+      }).then(results => {
+        browser.test.sendMessage("max-results-search", results);
+        return browser.history.search({text: "", startTime: Date.now()});
+      }).then(results => {
+        browser.test.assertEq(0, results.length, "no results returned for late start time");
+        return browser.history.search({text: "", endTime: 0});
+      }).then(results => {
+        browser.test.assertEq(0, results.length, "no results returned for early end time");
+        return browser.history.search({text: "", startTime: Date.now(), endTime: 0});
+      }).then(results => {
+        browser.test.fail("history.search rejects with startTime that is after the endTime");
+      }, error => {
+        browser.test.assertEq(
+          error.message,
+          "The startTime cannot be after the endTime",
+          "history.search rejects with startTime that is after the endTime");
+      }).then(() => {
+        browser.test.notifyPass("search");
+      });
+    });
+
+    browser.test.sendMessage("ready");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["history"],
+    },
+    background: `(${background})()`,
+  });
+
+  function findResult(url, results) {
+    return results.find(r => r.url === url);
+  }
+
+  function checkResult(results, url, expectedCount) {
+    let result = findResult(url, results);
+    isnot(result, null, `history.search result was found for ${url}`);
+    is(result.visitCount, expectedCount, `history.search reports ${expectedCount} visit(s)`);
+    is(result.title, `test visit for ${url}`, "title for search result is correct");
+  }
+
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
+  yield PlacesTestUtils.clearHistory();
+
+  yield PlacesTestUtils.addVisits([
+    {uri: makeURI(MOZILLA_VISIT_URL)},
+    {uri: makeURI(DOUBLE_VISIT_URL)},
+    {uri: makeURI(SINGLE_VISIT_URL)},
+    {uri: makeURI(DOUBLE_VISIT_URL)},
+  ]);
+
+  extension.sendMessage("check-history");
+
+  let results = yield extension.awaitMessage("empty-search");
+  is(results.length, 3, "history.search returned 3 results");
+  checkResult(results, SINGLE_VISIT_URL, 1);
+  checkResult(results, DOUBLE_VISIT_URL, 2);
+  checkResult(results, MOZILLA_VISIT_URL, 1);
+
+  results = yield extension.awaitMessage("text-search");
+  is(results.length, 1, "history.search returned 1 result");
+  checkResult(results, MOZILLA_VISIT_URL, 1);
+
+  results = yield extension.awaitMessage("max-results-search");
+  is(results.length, 1, "history.search returned 1 result");
+  checkResult(results, DOUBLE_VISIT_URL, 2);
+
+  yield extension.awaitFinish("search");
+  yield extension.unload();
+  yield PlacesTestUtils.clearHistory();
+});
--- a/browser/components/newtab/tests/browser/browser.ini
+++ b/browser/components/newtab/tests/browser/browser.ini
@@ -7,9 +7,10 @@ support-files =
   newtabmessages_prefs.html
   newtabmessages_preview.html
   newtabmessages_search.html
 
 [browser_PreviewProvider.js]
 [browser_remotenewtab_pageloads.js]
 [browser_newtab_overrides.js]
 [browser_newtabmessages.js]
+skip-if = true # Bug 1271177, bug 1262719
 [browser_newtabwebchannel.js]
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -815,17 +815,17 @@ var SessionStoreInternal = {
         event.initEvent("SSTabRestoring", true, false);
         tab.dispatchEvent(event);
         break;
       case "SessionStore:restoreTabContentStarted":
         if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
           // If a load not initiated by sessionstore was started in a
           // previously pending tab. Mark the tab as no longer pending.
           this.markTabAsRestoring(tab);
-        } else {
+        } else if (!data.isRemotenessUpdate) {
           // If the user was typing into the URL bar when we crashed, but hadn't hit
           // enter yet, then we just need to write that value to the URL bar without
           // loading anything. This must happen after the load, as the load will clear
           // userTypedValue.
           let tabData = TabState.collect(tab);
           if (tabData.userTypedValue && !tabData.userTypedClear && !browser.userTypedValue) {
             browser.userTypedValue = tabData.userTypedValue;
             win.URLBarSetURI();
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -184,17 +184,17 @@ var MessageListener = {
 
     // We need to pass the value of didStartLoad back to SessionStore.jsm.
     let didStartLoad = gContentRestore.restoreTabContent(loadArguments, isRemotenessUpdate, () => {
       // Tell SessionStore.jsm that it may want to restore some more tabs,
       // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
       sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
     });
 
-    sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch, didStartLoad});
+    sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch, isRemotenessUpdate});
 
     if (!didStartLoad) {
       // Pretend that the load succeeded so that event handlers fire correctly.
       sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
     }
   },
 
   flush({id}) {
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -218,10 +218,12 @@ skip-if = os == "mac"
 
 [browser_911547.js]
 [browser_send_async_message_oom.js]
 [browser_multiple_navigateAndRestore.js]
 run-if = e10s
 [browser_async_window_flushing.js]
 [browser_forget_async_closings.js]
 [browser_newtab_userTypedValue.js]
+[browser_parentProcessRestoreHash.js]
+run-if = e10s
 [browser_sessionStoreContainer.js]
 [browser_1234021.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
@@ -0,0 +1,95 @@
+"use strict";
+
+const SELFCHROMEURL =
+  "chrome://mochitests/content/browser/browser/" +
+  "components/sessionstore/test/browser_parentProcessRestoreHash.js";
+
+const Cm = Components.manager;
+
+const TESTCLASSID = "78742c04-3630-448c-9be3-6c5070f062de";
+
+const TESTURL = "about:testpageforsessionrestore#foo";
+
+
+let TestAboutPage = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+  getURIFlags: function(aURI) {
+    // No CAN_ or MUST_LOAD_IN_CHILD means this loads in the parent:
+    return Ci.nsIAboutModule.ALLOW_SCRIPT |
+           Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
+           Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
+  },
+
+  newChannel: function(aURI, aLoadInfo) {
+    // about: page inception!
+    let newURI = Services.io.newURI(SELFCHROMEURL, null, null);
+    let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
+                                                            aLoadInfo);
+    channel.originalURI = aURI;
+    return channel;
+  },
+
+  createInstance: function(outer, iid) {
+    if (outer != null) {
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    }
+    return this.QueryInterface(iid);
+  },
+
+  register: function() {
+    Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
+      Components.ID(TESTCLASSID), "Only here for a test",
+      "@mozilla.org/network/protocol/about;1?what=testpageforsessionrestore", this);
+  },
+
+  unregister: function() {
+    Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(
+      Components.ID(TESTCLASSID), this);
+  }
+};
+
+
+/**
+ * Test that switching from a remote to a parent process browser
+ * correctly clears the userTypedValue
+ */
+add_task(function* () {
+  TestAboutPage.register();
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/", true, true);
+  ok(tab.linkedBrowser.isRemoteBrowser, "Browser should be remote");
+
+  let resolveLocationChangePromise;
+  let locationChangePromise = new Promise(r => resolveLocationChangePromise = r);
+  let wpl = {
+    onStateChange(wpl, request, state, status) {
+      let location = request.QueryInterface(Ci.nsIChannel).originalURI;
+      // Ignore about:blank loads.
+      let docStop = Ci.nsIWebProgressListener.STATE_STOP |
+                    Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+      if (location.spec == "about:blank" || (state & docStop == docStop)) {
+        return;
+      }
+      is(location.spec, TESTURL, "Got the expected URL");
+      resolveLocationChangePromise();
+    },
+  };
+  gBrowser.addProgressListener(wpl);
+
+  gURLBar.value = TESTURL;
+  gURLBar.select();
+  EventUtils.sendKey("return");
+
+  yield locationChangePromise;
+
+  ok(!tab.linkedBrowser.isRemoteBrowser, "Browser should no longer be remote");
+
+  is(gURLBar.textValue, TESTURL, "URL bar visible value should be correct.");
+  is(gURLBar.value, TESTURL, "URL bar value should be correct.");
+  is(gURLBar.getAttribute("pageproxystate"), "valid", "URL bar is in valid page proxy state");
+
+  ok(!tab.linkedBrowser.userTypedValue, "No userTypedValue should be on the browser.");
+
+  yield BrowserTestUtils.removeTab(tab);
+  gBrowser.removeProgressListener(wpl);
+  TestAboutPage.unregister();
+});
--- a/browser/modules/FormSubmitObserver.jsm
+++ b/browser/modules/FormSubmitObserver.jsm
@@ -99,23 +99,23 @@ FormSubmitObserver.prototype =
   {
     // We are going to handle invalid form submission attempt by focusing the
     // first invalid element and show the corresponding validation message in a
     // panel attached to the element.
     if (!aInvalidElements.length) {
       return;
     }
 
-    // Insure that this is the FormSubmitObserver associated with the form
+    // Insure that this is the FormSubmitObserver associated with the
     // element / window this notification is about.
-    if (this._content != aFormElement.ownerDocument.defaultView.top.document.defaultView) {
+    let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
+    if (this._content != element.ownerDocument.defaultView.top.document.defaultView) {
       return;
     }
 
-    let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
     if (!(element instanceof HTMLInputElement ||
           element instanceof HTMLTextAreaElement ||
           element instanceof HTMLSelectElement ||
           element instanceof HTMLButtonElement)) {
       return;
     }
 
     // Update validation message before showing notification
--- a/build/autoconf/pkg.m4
+++ b/build/autoconf/pkg.m4
@@ -1,23 +1,21 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
 # defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
 # also defines GSTUFF_PKG_ERRORS on error
+# PKG_CONFIG is set by Python configure, if it is empty here it could not
+# be found.
 AC_DEFUN([PKG_CHECK_MODULES],
 [succeeded=no
 
   if test -z "$PKG_CONFIG"; then
-    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
-  fi
-
-  if test "$PKG_CONFIG" = "no" ; then
      echo "*** The pkg-config script could not be found. Make sure it is"
      echo "*** in your path, or set the PKG_CONFIG environment variable"
      echo "*** to the full path to pkg-config."
      echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
   else
      PKG_CONFIG_MIN_VERSION=0.9.0
      if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
         AC_MSG_CHECKING(for $2)
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -6,22 +6,16 @@
 ifneq (,$(filter automation/%,$(MAKECMDGOALS)))
 ifeq (4.0,$(firstword $(sort 4.0 $(MAKE_VERSION))))
 MAKEFLAGS += --output-sync=target
 else
 .NOTPARALLEL:
 endif
 endif
 
-include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
-include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
-
-# Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
-DIST_FILES =
-
 # Helper variables to convert from MOZ_AUTOMATION_* variables to the
 # corresponding the make target
 tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols
 tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check
 tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check
 tier_MOZ_AUTOMATION_INSTALLER = installer
 tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer
 tier_MOZ_AUTOMATION_PACKAGE = package
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -679,16 +679,35 @@ set_config('NIGHTLY_BUILD', delayed_geta
 set_define('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly'))
 add_old_configure_assignment('NIGHTLY_BUILD',
                              delayed_getattr(milestone, 'is_nightly'))
 set_config('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 set_define('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 add_old_configure_assignment('RELEASE_BUILD',
                              delayed_getattr(milestone, 'is_release'))
 
+# The app update channel is 'default' when not supplied. The value is used in
+# the application's confvars.sh (and is made available to a project specific
+# moz.configure).
+option('--enable-update-channel',
+       nargs=1,
+       help='Select application update channel',
+       default='default')
+
+@depends('--enable-update-channel')
+def update_channel(channel):
+    if channel[0] == '':
+        return 'default'
+    return channel[0].lower()
+
+set_config('MOZ_UPDATE_CHANNEL', update_channel)
+set_define('MOZ_UPDATE_CHANNEL', update_channel)
+add_old_configure_assignment('MOZ_UPDATE_CHANNEL', update_channel)
+
+
 # A template providing a shorthand for setting a variable. The created
 # option will only be settable with imply_option.
 # It is expected that a project-specific moz.configure will call imply_option
 # to set a value other than the default.
 # If required, the set_as_define and set_for_old_configure arguments
 # will additionally cause the variable to be set using set_define and
 # add_old_configure_assignment. util.configure would be an appropriate place for
 # this, but it uses add_old_configure_assignment, which is defined in this file.
@@ -765,9 +784,13 @@ def js_option(*args, **kwargs):
     @depends(opt.option, build_project)
     def js_option(value, build_project):
         if build_project != 'js':
             return value.format(opt.option)
 
     add_old_configure_arg(js_option)
 
 
+include('pkg.configure')
+# Make this assignment here rather than in pkg.configure to avoid
+# requiring this file in unit tests.
+add_old_configure_assignment('PKG_CONFIG', pkg_config)
 include(include_project_configure)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -243,27 +243,25 @@ def old_configure_options(*options):
     '--enable-startupcache',
     '--enable-stdcxx-compat',
     '--enable-strip',
     '--enable-synth-pico',
     '--enable-synth-speechd',
     '--enable-system-cairo',
     '--enable-system-extension-dirs',
     '--enable-system-ffi',
-    '--enable-system-hunspell',
     '--enable-system-pixman',
     '--enable-system-sqlite',
     '--enable-tasktracer',
     '--enable-tests',
     '--enable-thread-sanitizer',
     '--enable-trace-logging',
     '--enable-tree-freetype',
     '--enable-ui-locale',
     '--enable-universalchardet',
-    '--enable-update-channel',
     '--enable-updater',
     '--enable-url-classifier',
     '--enable-valgrind',
     '--enable-verify-mar',
     '--enable-warnings-as-errors',
     '--enable-webapp-runtime',
     '--enable-webrtc',
     '--enable-websms-backend',
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/pkg.configure
@@ -0,0 +1,86 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+pkg_config = check_prog('PKG_CONFIG', ('pkg-config',), allow_missing=True)
+
+@depends_if(pkg_config)
+@checking('for pkg-config version')
+@imports('subprocess')
+def pkg_config_version(pkg_config):
+    return Version(check_cmd_output(pkg_config, '--version').rstrip())
+
+# Locates the given module using pkg-config.
+# - `var` determines the name of variables to set when the package is found.
+#   <var>_CFLAGS and <var>_LIBS are set with corresponding values.
+# - `package_desc` package name and version requirement string, list of
+#   strings describing packages to locate, or depends function that will
+#   resolve to such a string or list of strings.
+# - `condition` a depends function that will determine whether to perform
+#   any checks (default is to always perform checks).
+# - `allow_missing` If set, failure to fulfill the package description
+#   will not result in an error or logged message, and any error message
+#   will be returned to the caller.
+#   Returns `True` when the package description is fulfilled.
+@template
+@imports(_from='mozbuild.configure', _import='DependsFunction')
+def pkg_check_modules(var, package_desc,
+                      condition=depends('--help')(lambda _: True),
+                      allow_missing=False):
+    if isinstance(package_desc, (tuple, list)):
+        package_desc = ' '.join(package_desc)
+    if not isinstance(package_desc, DependsFunction):
+        package_desc = depends('--help')(lambda _: package_desc)
+
+    @depends_when(pkg_config, pkg_config_version, when=condition)
+    def check_pkg_config(pkg_config, version):
+        min_version = '0.9.0'
+        if pkg_config is None:
+            die("*** The pkg-config script could not be found. Make sure it is\n"
+                "*** in your path, or set the PKG_CONFIG environment variable\n"
+                "*** to the full path to pkg-config.")
+        if version < min_version:
+            die("*** Your version of pkg-config is too old. You need version %s or newer.",
+                min_version)
+
+    @depends_when(pkg_config, package_desc, when=condition)
+    @imports('subprocess')
+    @imports('sys')
+    @imports(_from='mozbuild.configure.util', _import='LineIO')
+    def package(pkg_config, package_desc):
+        # package_desc may start as a depends function, so we can't use
+        # @checking here.
+        log.info("checking for %s... " % package_desc)
+        with log.queue_debug():
+            try:
+                subprocess.check_output([pkg_config, '--errors-to-stdout',
+                                         '--print-errors', package_desc])
+                log.info("yes")
+                return True
+            except subprocess.CalledProcessError as e:
+                log.info("no")
+                log_writer = log.warning if allow_missing else log.error
+                with LineIO(lambda l: log_writer(l)) as o:
+                    o.write(e.output)
+                if not allow_missing:
+                    sys.exit(1)
+
+    @depends_when(pkg_config, package_desc, when=package)
+    @checking('%s_CFLAGS' % var, callback=lambda t: ' '.join(t))
+    def pkg_cflags(pkg_config, package_desc):
+        flags = check_cmd_output(pkg_config, '--cflags', package_desc)
+        return tuple(flags.split())
+
+    @depends_when(pkg_config, package_desc, when=package)
+    @checking('%s_LIBS' % var, callback=lambda t: ' '.join(t))
+    def pkg_libs(pkg_config, package_desc):
+        libs = check_cmd_output(pkg_config, '--libs', package_desc)
+        # Remove evil flags like -Wl,--export-dynamic
+        return tuple(libs.replace('-Wl,--export-dynamic', '').split())
+
+    set_config('%s_CFLAGS' % var, pkg_cflags)
+    set_config('%s_LIBS' % var, pkg_libs)
+
+    return package
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -5,25 +5,22 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # yasm detection
 # ==============================================================
 yasm = check_prog('YASM', ['yasm'], allow_missing=True)
 
 @depends_if(yasm)
 @checking('yasm version')
-@imports('subprocess')
 def yasm_version(yasm):
-    try:
-        version = Version(subprocess.check_output(
-            [yasm, '--version']
-        ).splitlines()[0].split()[1])
-        return version
-    except subprocess.CalledProcessError as e:
-        die('Failed to get yasm version: %s', e.message)
+    version = check_cmd_output(
+        yasm, '--version',
+        onerror=lambda: die('Failed to get yasm version.')
+    ).splitlines()[0].split()[1]
+    return version
 
 # Until we move all the yasm consumers out of old-configure.
 # bug 1257904
 add_old_configure_assignment('_YASM_MAJOR_VERSION',
                              delayed_getattr(yasm_version, 'major'))
 add_old_configure_assignment('_YASM_MINOR_VERSION',
                              delayed_getattr(yasm_version, 'minor'))
 
@@ -169,17 +166,16 @@ set_config('TOOLCHAIN_PREFIX', toolchain
 add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
 
 
 # Compilers
 # ==============================================================
 @imports('os')
 @imports('subprocess')
 @imports(_from='mozbuild.configure.util', _import='LineIO')
-@imports(_from='mozbuild.shellutil', _import='quote')
 @imports(_from='tempfile', _import='mkstemp')
 def try_preprocess(compiler, language, source):
     suffix = {
         'C': '.c',
         'C++': '.cpp',
     }[language]
 
     fd, path = mkstemp(prefix='conftest.', suffix=suffix)
@@ -187,32 +183,18 @@ def try_preprocess(compiler, language, s
         source = source.encode('ascii', 'replace')
 
         log.debug('Creating `%s` with content:', path)
         with LineIO(lambda l: log.debug('| %s', l)) as out:
             out.write(source)
 
         os.write(fd, source)
         os.close(fd)
-
         cmd = compiler + ['-E', path]
-        log.debug('Executing: `%s`', quote(*cmd))
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        stdout, stderr = proc.communicate()
-        retcode = proc.wait()
-        if retcode == 0:
-            return stdout
-
-        log.debug('The command returned non-zero exit status %d.', retcode)
-        for out, desc in ((stdout, 'output'), (stderr, 'error output')):
-            if out:
-                log.debug('Its %s was:', desc)
-                with LineIO(lambda l: log.debug('| %s', l)) as o:
-                    o.write(out)
+        return check_cmd_output(*cmd)
     finally:
         os.remove(path)
 
 
 @imports(_from='mozbuild.configure.constants', _import='CompilerType')
 @imports(_from='textwrap', _import='dedent')
 def get_compiler_info(compiler, language):
     '''Returns information about the given `compiler` (command line in the
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -13,16 +13,47 @@ def die(*args):
 
 @imports(_from='mozbuild.configure', _import='ConfigureError')
 def configure_error(message):
     '''Raise a programming error and terminate configure.
     Primarily for use in moz.configure templates to sanity check
     their inputs from moz.configure usage.'''
     raise ConfigureError(message)
 
+# A wrapper to obtain a process' output that returns the output generated
+# by running the given command if it exits normally, and streams that
+# output to log.debug and calls die or the given error callback if it
+# does not.
+@imports('subprocess')
+@imports('sys')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def check_cmd_output(*args, **kwargs):
+    onerror = kwargs.pop('onerror', None)
+
+    with log.queue_debug():
+        log.debug('Executing: `%s`', quote(*args))
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        stdout, stderr = proc.communicate()
+        retcode = proc.wait()
+        if retcode == 0:
+            return stdout
+
+        log.debug('The command returned non-zero exit status %d.',
+                  retcode)
+        for out, desc in ((stdout, 'output'), (stderr, 'error output')):
+            if out:
+                log.debug('Its %s was:', desc)
+                with LineIO(lambda l: log.debug('| %s', l)) as o:
+                    o.write(out)
+        if onerror:
+            return onerror()
+        die('Command `%s` failed with exit status %d.' %
+            (quote(*args), retcode))
 
 @imports('os')
 def is_absolute_or_relative(path):
     if os.altsep and os.altsep in path:
         return True
     return os.sep in path
 
 
@@ -121,8 +152,24 @@ def delayed_getattr(func, key):
 def depends_if(*args):
     def decorator(func):
         @depends(*args)
         def wrapper(*args):
             if any(arg for arg in args):
                 return func(*args)
         return wrapper
     return decorator
+
+# Like @depends_if, but a distinguished value passed as a keyword argument
+# "when" is truth tested instead of every argument. This value is not passed
+# to the function if it is called.
+@template
+def depends_when(*args, **kwargs):
+    if not len(kwargs) == 1 and kwargs.get('when'):
+        die('depends_when requires a single keyword argument, "when"')
+    when = kwargs['when']
+    def decorator(fn):
+        @depends(when, *args)
+        def wrapper(val, *args):
+            if val:
+                return fn(*args)
+        return wrapper
+    return decorator
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2497,16 +2497,20 @@ nsDocShell::GetFullscreenAllowed(bool* a
   if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
     *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
     return NS_OK;
   }
 
   // Assume false until we determine otherwise...
   *aFullscreenAllowed = false;
 
+  // If it is sandboxed, fullscreen is not allowed.
+  if (mSandboxFlags & SANDBOXED_FULLSCREEN) {
+    return NS_OK;
+  }
   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   if (!win) {
     return NS_OK;
   }
   nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
   if (frameElement && !frameElement->IsXULElement()) {
     // We do not allow document inside any containing element other
     // than iframe to enter fullscreen.
@@ -2516,16 +2520,20 @@ nsDocShell::GetFullscreenAllowed(bool* a
     // If any ancestor iframe does not have allowfullscreen attribute
     // set, then fullscreen is not allowed.
     if (!frameElement->HasAttr(kNameSpaceID_None,
                                nsGkAtoms::allowfullscreen) &&
         !frameElement->HasAttr(kNameSpaceID_None,
                                nsGkAtoms::mozallowfullscreen)) {
       return NS_OK;
     }
+    nsIDocument* doc = frameElement->GetUncomposedDoc();
+    if (!doc || !doc->FullscreenEnabledInternal()) {
+      return NS_OK;
+    }
   }
 
   // If we have no parent then we're the root docshell; no ancestor of the
   // original docshell doesn't have a allowfullscreen attribute, so
   // report fullscreen as allowed.
   RefPtr<nsDocShell> parent = GetParentDocshell();
   if (!parent) {
     *aFullscreenAllowed = true;
@@ -2951,17 +2959,17 @@ nsDocShell::GetSessionStorageForPrincipa
                                           bool aCreate,
                                           nsIDOMStorage** aStorage)
 {
   nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
   if (!manager) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> domWin = do_GetInterface(GetAsSupports(this));
+  nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
 
   if (aCreate) {
     return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
                                   aDocumentURI, mInPrivateBrowsing, aStorage);
   }
 
   return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
                              mInPrivateBrowsing, aStorage);
@@ -9968,17 +9976,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   // If this docshell is owned by a frameloader, make sure to cancel
   // possible frameloader initialization before loading a new page.
   nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
   if (parent) {
-    nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
+    nsCOMPtr<nsIDocument> doc = parent->GetDocument();
     if (doc) {
       doc->TryCancelFrameLoaderInitialization(this);
     }
   }
 
   // Before going any further vet loads initiated by external programs.
   if (aLoadType == LOAD_NORMAL_EXTERNAL) {
     // Disallow external chrome: loads targetted at content windows
@@ -14460,8 +14468,15 @@ nsDocShell::GetScriptableTabChild(nsITab
 
 already_AddRefed<nsITabChild>
 nsDocShell::GetTabChild()
 {
   nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
   nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
   return tc.forget();
 }
+
+nsICommandManager*
+nsDocShell::GetCommandManager()
+{
+  NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
+  return mCommandManager;
+}
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -37,16 +37,17 @@ interface nsIScriptGlobalObject;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 interface nsITabChild;
+interface nsICommandManager;
 native TabChildRef(already_AddRefed<nsITabChild>);
 
 typedef unsigned long nsLoadFlags;
 
 [scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
@@ -1100,9 +1101,11 @@ interface nsIDocShell : nsIDocShellTreeI
    */
   readonly attribute nsIEditingSession editingSession;
 
   /**
    * The tab child for this docshell.
    */
   [binaryname(ScriptableTabChild)] readonly attribute nsITabChild tabChild;
   [noscript,notxpcom,nostdcall] TabChildRef GetTabChild();
+
+  [noscript,nostdcall,notxpcom] nsICommandManager GetCommandManager();
 };
--- a/dom/animation/test/css-animations/file_animation-currenttime.html
+++ b/dom/animation/test/css-animations/file_animation-currenttime.html
@@ -20,436 +20,225 @@
     </style>
     <script src="../testcommon.js"></script>
   </head>
   <body>
     <script type="text/javascript">
 
 'use strict';
 
-// TODO: add equivalent tests without an animation-delay, but first we need to
-// change the timing of animationstart dispatch. (Right now the animationstart
-// event will fire before the ready Promise is resolved if there is no
-// animation-delay.)
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
+// TODO: We should separate this test(Testing for CSS Animation events /
+// Testing for currentTime of Web Animation).
+// e.g:
+//  CSS Animation events test :
+//    - check the firing an event using Animation.currentTime
+//  The current Time of Web Animation test :
+//    - check an current time value on several situation(init / processing..)
+//    - Based on W3C Spec, check the behavior of setting current time.
 
 // TODO: Once the computedTiming property is implemented, add checks to the
 // checker helpers to ensure that computedTiming's properties are updated as
 // expected.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
 
-
 const CSS_ANIM_EVENTS =
   ['animationstart', 'animationiteration', 'animationend'];
-const ANIM_DELAY_MS = 1000 * MS_PER_SEC;
-const ANIM_DUR_MS = 1000 * MS_PER_SEC;
-const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
-
-/**
- * These helpers get the value that the currentTime needs to be set to, to put
- * an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
- * the middle of various phases or points through the active duration.
- */
-function currentTimeForBeforePhase(timeline) {
-  return ANIM_DELAY_MS / 2;
-}
-function currentTimeForActivePhase(timeline) {
-  return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
-}
-function currentTimeForAfterPhase(timeline) {
-  return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
-}
-function currentTimeForStartOfActiveInterval(timeline) {
-  return ANIM_DELAY_MS;
-}
-function currentTimeForFiftyPercentThroughActiveInterval(timeline) {
-  return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
-}
-function currentTimeForEndOfActiveInterval(timeline) {
-  return ANIM_DELAY_MS + ANIM_DUR_MS;
-}
-
-
-// Expected computed 'margin-left' values at points during the active interval:
-// When we assert_between_inclusive using these values we could in theory cause
-// intermittent failure due to very long delays between paints, but since the
-// active duration is 1000s long, a delay would need to be around 100s to cause
-// that. If that's happening then there are likely other issues that should be
-// fixed, so a failure to make us look into that seems like a good thing.
-const UNANIMATED_POSITION = 10;
-const INITIAL_POSITION = 100;
-const TEN_PCT_POSITION = 110;
-const FIFTY_PCT_POSITION = 150;
-const END_POSITION = 200;
-
-// The terms used for the naming of the following helper functions refer to
-// terms used in the Web Animations specification for specific phases of an
-// animation. The terms can be found here:
-//
-//   https://w3c.github.io/web-animations/#animation-effect-phases-and-states
-//
-// Note the distinction between the "animation start time" which occurs before
-// the start delay and the start of the active interval which occurs after it.
-
-// Called when currentTime is set to zero (the beginning of the start delay).
-function checkStateOnSettingCurrentTimeToZero(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the start delay');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" at the start of the start delay');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'at the beginning of the start delay');
-}
-
-// Called when the ready Promise's callbacks should happen
-function checkStateOnReadyPromiseResolved(animation)
-{
-  // the 0.0001 here is for rounding error
-  assert_less_than_equal(animation.currentTime,
-    animation.timeline.currentTime - animation.startTime + 0.0001,
-    'Animation.currentTime should be less than the local time ' +
-    'equivalent of the timeline\'s currentTime on the first paint tick ' +
-    'after animation creation');
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" on the first paint ' +
-    'tick after animation creation');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" on the first paint tick after animation creation');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'by an animation with a delay on ready Promise resolve');
-}
-
-// Called when currentTime is set to the time the active interval starts.
-function checkStateAtActiveIntervalStartTime(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the active interval');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" at the start of the active interval');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
-    'the computed value of margin-left should be close to the value at the ' +
-    'beginning of the animation');
-}
-
-function checkStateAtFiftyPctOfActiveInterval(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, FIFTY_PCT_POSITION,
-    'the computed value of margin-left should be half way through the ' +
-    'animation at the midpoint of the active interval');
-}
-
-// Called when currentTime is set to the time the active interval ends.
-function checkStateAtActiveIntervalEndTime(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  assert_equals(animation.playState, 'finished',
-    'Animation.playState should be "finished" at the end of ' +
-    'the active interval');
-
-  assert_equals(animation.effect.target.style.animationPlayState, "running",
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"finished" at the end of the active interval');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-    'the computed value of margin-left should be unaffected ' +
-    'by the animation at the end of the active duration when the ' +
-    'animation-fill-mode is none');
-}
 
 test(function(t)
 {
   var div = addDiv(t, {'class': 'animated-div'});
-
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = "anim 100s";
   var animation = div.getAnimations()[0];
 
   // Animations shouldn't start until the next paint tick, so:
   assert_equals(animation.currentTime, 0,
     'Animation.currentTime should be zero when an animation ' +
     'is initially created');
 
-  assert_equals(animation.playState, "pending",
-    'Animation.playState should be "pending" when an animation ' +
-    'is initially created');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" when an animation is initially created');
-
-  // XXX Ideally we would have a test to check the ready Promise is initially
-  // unresolved, but currently there is no Web API to do that. Waiting for the
-  // ready Promise with a timeout doesn't work because the resolved callback
-  // will be called (async) regardless of whether the Promise was resolved in
-  // the past or is resolved in the future.
-
-  // So that animation is running instead of paused when we set currentTime:
+  // Make sure the animation is running before we set the current time.
   animation.startTime = animation.timeline.currentTime;
 
-  assert_approx_equals(animation.currentTime, 0, 0.0001, // rounding error
+  animation.currentTime = 50 * MS_PER_SEC;
+  assert_times_equal(animation.currentTime, 50 * MS_PER_SEC,
     'Check setting of currentTime actually works');
-
-  checkStateOnSettingCurrentTimeToZero(animation);
 }, 'Sanity test to check round-tripping assigning to new animation\'s ' +
    'currentTime');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
-    checkStateOnReadyPromiseResolved(animation);
+    // the 0.0001 here is for rounding error
+    assert_less_than_equal(animation.currentTime,
+      animation.timeline.currentTime - animation.startTime + 0.0001,
+      'Animation.currentTime should be less than the local time ' +
+      'equivalent of the timeline\'s currentTime on the first paint tick ' +
+      'after animation creation');
 
-    animation.currentTime =
-      currentTimeForStartOfActiveInterval(animation.timeline);
+    animation.currentTime = 100 * MS_PER_SEC;
     return eventWatcher.wait_for('animationstart');
   }).then(function() {
-    checkStateAtActiveIntervalStartTime(animation);
-
-    animation.currentTime =
-      currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
-    checkStateAtFiftyPctOfActiveInterval(animation);
-
-    animation.currentTime =
-      currentTimeForEndOfActiveInterval(animation.timeline);
+    animation.currentTime = 200 * MS_PER_SEC;
     return eventWatcher.wait_for('animationend');
-  }).then(function() {
-    checkStateAtActiveIntervalEndTime(animation);
   });
 }, 'Skipping forward through animation');
 
-
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
-
-  // So that animation is running instead of paused when we set currentTime:
-  animation.startTime = animation.timeline.currentTime;
-
-  animation.currentTime = currentTimeForEndOfActiveInterval(animation.timeline);
-
+  animation.currentTime = 200 * MS_PER_SEC;
   var previousTimelineTime = animation.timeline.currentTime;
 
-  // Skipping over the active interval will dispatch an 'animationstart' then
-  // an 'animationend' event. We need to wait for these events before we start
-  // testing going backwards since EventWatcher will fail the test if it gets
-  // an event that we haven't told it about.
-  var retPromise =  eventWatcher.wait_for(['animationstart',
-                                           'animationend']).then(function() {
+  return eventWatcher.wait_for(['animationstart',
+                                'animationend']).then(function() {
     assert_true(document.timeline.currentTime - previousTimelineTime <
-                  ANIM_DUR_MS,
+                100 * MS_PER_SEC,
                 'Sanity check that seeking worked rather than the events ' +
                 'firing after normal playback through the very long ' +
                 'animation duration');
 
-    // Now we can start the tests for skipping backwards, but first we check
-    // that after the events we're still in the same end time state:
-    checkStateAtActiveIntervalEndTime(animation);
-
-    animation.currentTime =
-      currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
-
-    // Despite going backwards from after the end of the animation (to being
-    // in the active interval), we now expect an 'animationstart' event
-    // because the animation should go from being inactive to active.
-    //
-    // Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
-    // causing computed style to be updated and the 'animationstart' event to
-    // be dispatched synchronously. We need to call wait_for first
-    // otherwise eventWatcher will assert that the event was unexpected.
-    var promise = eventWatcher.wait_for('animationstart');
-    checkStateAtFiftyPctOfActiveInterval(animation);
-    return promise;
+    animation.currentTime = 150 * MS_PER_SEC;
+    return eventWatcher.wait_for('animationstart');
   }).then(function() {
-    animation.currentTime =
-      currentTimeForStartOfActiveInterval(animation.timeline);
-    checkStateAtActiveIntervalStartTime(animation);
-
     animation.currentTime = 0;
-    // Despite going backwards from just after the active interval starts to
-    // the animation start time, we now expect an animationend event
-    // because we went from inside to outside the active interval.
     return eventWatcher.wait_for('animationend');
-  }).then(function() {
-    checkStateOnReadyPromiseResolved(animation);
   });
-
-  // This must come after we've set up the Promise chain, since requesting
-  // computed style will force events to be dispatched.
-  checkStateAtActiveIntervalEndTime(animation);
-
-  return retPromise;
 }, 'Skipping backwards through animation');
 
 // Next we have multiple tests to check that redundant currentTime changes do
 // NOT dispatch events. It's impossible to distinguish between events not being
 // dispatched and events just taking an incredibly long time to dispatch
 // without waiting an infinitely long time. Obviously we don't want to do that
 // (block this test from finishing forever), so instead we just listen for
 // events until two animation frames (i.e. requestAnimationFrame callbacks)
 // have happened, then assume that no events will ever be dispatched for the
 // redundant changes if no events were detected in that time.
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
-  animation.currentTime = currentTimeForActivePhase(animation.timeline);
-  animation.currentTime = currentTimeForBeforePhase(animation.timeline);
+  animation.currentTime = 150 * MS_PER_SEC;
+  animation.currentTime = 50 * MS_PER_SEC;
 
   return waitForAnimationFrames(2);
 }, 'Redundant change, before -> active, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
-  animation.currentTime = currentTimeForAfterPhase(animation.timeline);
-  animation.currentTime = currentTimeForBeforePhase(animation.timeline);
+  animation.currentTime = 250 * MS_PER_SEC;
+  animation.currentTime = 50 * MS_PER_SEC;
 
   return waitForAnimationFrames(2);
 }, 'Redundant change, before -> after, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise = eventWatcher.wait_for('animationstart').then(function() {
-    animation.currentTime = currentTimeForBeforePhase(animation.timeline);
-    animation.currentTime = currentTimeForActivePhase(animation.timeline);
+    animation.currentTime = 50 * MS_PER_SEC;
+    animation.currentTime = 150 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.currentTime = currentTimeForActivePhase(animation.timeline);
+  animation.currentTime = 150 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, active -> before, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
-  var retPromise =  eventWatcher.wait_for('animationstart').then(function() {
-    animation.currentTime = currentTimeForAfterPhase(animation.timeline);
-    animation.currentTime = currentTimeForActivePhase(animation.timeline);
+  var retPromise = eventWatcher.wait_for('animationstart').then(function() {
+    animation.currentTime = 250 * MS_PER_SEC;
+    animation.currentTime = 150 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.currentTime = currentTimeForActivePhase(animation.timeline);
+  animation.currentTime = 150 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, active -> after, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise =  eventWatcher.wait_for(['animationstart',
                                            'animationend']).then(function() {
-    animation.currentTime = currentTimeForBeforePhase(animation.timeline);
-    animation.currentTime = currentTimeForAfterPhase(animation.timeline);
+    animation.currentTime = 50 * MS_PER_SEC;
+    animation.currentTime = 250 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.currentTime = currentTimeForAfterPhase(animation.timeline);
+  animation.currentTime = 250 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, after -> before, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise =  eventWatcher.wait_for(['animationstart',
                                            'animationend']).then(function() {
-    animation.currentTime = currentTimeForActivePhase(animation.timeline);
-    animation.currentTime = currentTimeForAfterPhase(animation.timeline);
+    animation.currentTime = 150 * MS_PER_SEC;
+    animation.currentTime = 250 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.currentTime = currentTimeForAfterPhase(animation.timeline);
+  animation.currentTime = 250 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, after -> active, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s"
   var animation = div.getAnimations()[0];
 
   animation.pause();
-  animation.currentTime = currentTimeForAfterPhase(animation.timeline);
+  animation.currentTime = 150 * MS_PER_SEC;
 
   return eventWatcher.wait_for(['animationstart',
                                 'animationend']).then(function() {
-    animation.currentTime = currentTimeForActivePhase(animation.timeline);
+    animation.currentTime = 50 * MS_PER_SEC;
     return eventWatcher.wait_for('animationstart');
   });
 }, 'Seeking finished -> paused dispatches animationstart');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s";
 
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
     var exception;
     try {
       animation.currentTime = null;
     } catch (e) {
@@ -479,36 +268,33 @@ promise_test(function(t) {
   }).then(function() {
     assert_equals(animation.currentTime, pauseTime,
       'Animation.currentTime is unchanged after pausing');
   });
 }, 'Animation.currentTime after pausing');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = "anim 100s";
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
     // just before animation ends:
-    animation.currentTime = ANIM_DELAY_MS + ANIM_DUR_MS - 1;
-
+    animation.currentTime = 100 * MS_PER_SEC - 1;
     return waitForAnimationFrames(2);
   }).then(function() {
-    assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
+    assert_equals(animation.currentTime, 100 * MS_PER_SEC,
       'Animation.currentTime should not continue to increase after the ' +
       'animation has finished');
   });
 }, 'Animation.currentTime clamping');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = "anim 100s";
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
     // play backwards:
     animation.playbackRate = -1;
 
     // just before animation ends (at the "start"):
     animation.currentTime = 1;
@@ -519,28 +305,28 @@ promise_test(function(t) {
       'Animation.currentTime should not continue to decrease after an ' +
       'animation running in reverse has finished and currentTime is zero');
   });
 }, 'Animation.currentTime clamping for reversed animation');
 
 test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   div.style.animation = 'anim 100s';
-
   var animation = div.getAnimations()[0];
   animation.cancel();
+
   assert_equals(animation.currentTime, null,
                 'The currentTime of a cancelled animation should be null');
 }, 'Animation.currentTime after cancelling');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   div.style.animation = 'anim 100s';
+  var animation = div.getAnimations()[0];
 
-  var animation = div.getAnimations()[0];
   return animation.ready.then(function() {
     animation.finish();
 
     // Initiate a pause then abort it
     animation.pause();
     animation.play();
 
     // Wait to return to running state
--- a/dom/animation/test/css-animations/file_animation-starttime.html
+++ b/dom/animation/test/css-animations/file_animation-starttime.html
@@ -20,228 +20,116 @@
     </style>
     <script src="../testcommon.js"></script>
   </head>
   <body>
     <script type="text/javascript">
 
 'use strict';
 
-// TODO: add equivalent tests without an animation-delay, but first we need to
-// change the timing of animationstart dispatch. (Right now the animationstart
-// event will fire before the ready Promise is resolved if there is no
-// animation-delay.)
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
+// TODO: We should separate this test(Testing for CSS Animation events /
+// Testing for start time of Web Animation).
+// e.g:
+//  CSS Animation events test:
+//    - check the firing an event after setting an Animation.startTime
+//  The start time of Web Animation test:
+//    - check an start time value on several situation(init / processing..)
+//    - Based on W3C Spec, check the behavior of setting current time.
 
 // TODO: Once the computedTiming property is implemented, add checks to the
 // checker helpers to ensure that computedTiming's properties are updated as
 // expected.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
 
-
 const CSS_ANIM_EVENTS =
   ['animationstart', 'animationiteration', 'animationend'];
-const ANIM_DELAY_MS = 1000 * MS_PER_SEC; // 1000s
-const ANIM_DUR_MS = 1000 * MS_PER_SEC; // 1000s
-const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
-
-/**
- * These helpers get the value that the startTime needs to be set to, to put an
- * animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
- * middle of various phases or points through the active duration.
- */
-function startTimeForBeforePhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS / 2;
-}
-function startTimeForActivePhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
-}
-function startTimeForAfterPhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
-}
-function startTimeForStartOfActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS;
-}
-function startTimeForFiftyPercentThroughActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
-}
-function startTimeForEndOfActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
-}
-
-
-// Expected computed 'margin-left' values at points during the active interval:
-// When we assert_between_inclusive using these values we could in theory cause
-// intermittent failure due to very long delays between paints, but since the
-// active duration is 1000s long, a delay would need to be around 100s to cause
-// that. If that's happening then there are likely other issues that should be
-// fixed, so a failure to make us look into that seems like a good thing.
-const UNANIMATED_POSITION = 10;
-const INITIAL_POSITION = 100;
-const TEN_PCT_POSITION = 110;
-const FIFTY_PCT_POSITION = 150;
-const END_POSITION = 200;
-
-// The terms used for the naming of the following helper functions refer to
-// terms used in the Web Animations specification for specific phases of an
-// animation. The terms can be found here:
-//
-//   https://w3c.github.io/web-animations/#animation-effect-phases-and-states
-//
-// Note the distinction between the "animation start time" which occurs before
-// the start delay and the start of the active interval which occurs after it.
-
-// Called when the ready Promise's callbacks should happen
-function checkStateOnReadyPromiseResolved(animation)
-{
-  assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
-    'Animation.startTime should be less than the timeline\'s ' +
-    'currentTime on the first paint tick after animation creation');
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" on the first paint ' +
-    'tick after animation creation');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" on the first paint tick after animation creation');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'by an animation with a delay on ready Promise resolve');
-}
-
-// Called when startTime is set to the time the active interval starts.
-function checkStateAtActiveIntervalStartTime(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the active interval');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" at the start of the active interval');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
-    'the computed value of margin-left should be close to the value at the ' +
-    'beginning of the animation');
-}
-
-function checkStateAtFiftyPctOfActiveInterval(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, FIFTY_PCT_POSITION,
-    'the computed value of margin-left should be half way through the ' +
-    'animation at the midpoint of the active interval');
-}
-
-// Called when startTime is set to the time the active interval ends.
-function checkStateAtActiveIntervalEndTime(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
-
-  assert_equals(animation.playState, 'finished',
-    'Animation.playState should be "finished" at the end of ' +
-    'the active interval');
-
-  assert_equals(animation.effect.target.style.animationPlayState, "running",
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"finished" at the end of the active interval');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-    'the computed value of margin-left should be unaffected ' +
-    'by the animation at the end of the active duration when the ' +
-    'animation-fill-mode is none');
-}
 
 test(function(t)
 {
   var div = addDiv(t, { 'style': 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
+
   assert_equals(animation.startTime, null, 'startTime is unresolved');
 }, 'startTime of a newly created (play-pending) animation is unresolved');
 
 test(function(t)
 {
   var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
   var animation = div.getAnimations()[0];
   assert_equals(animation.startTime, null, 'startTime is unresolved');
 }, 'startTime of a newly created (pause-pending) animation is unresolved');
 
 promise_test(function(t)
 {
   var div = addDiv(t, { 'style': 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
+
   return animation.ready.then(function() {
     assert_true(animation.startTime > 0,
                 'startTime is resolved when running');
   });
 }, 'startTime is resolved when running');
 
 promise_test(function(t)
 {
   var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
   var animation = div.getAnimations()[0];
+
   return animation.ready.then(function() {
     assert_equals(animation.startTime, null,
                   'startTime is unresolved when paused');
   });
 }, 'startTime is unresolved when paused');
 
 promise_test(function(t)
 {
   var div = addDiv(t, { 'style': 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
+
   return animation.ready.then(function() {
     div.style.animationPlayState = 'paused';
     getComputedStyle(div).animationPlayState;
+
     assert_not_equals(animation.startTime, null,
                       'startTime is resolved when pause-pending');
 
     div.style.animationPlayState = 'running';
     getComputedStyle(div).animationPlayState;
+
     assert_not_equals(animation.startTime, null,
                       'startTime is preserved when a pause is aborted');
   });
 }, 'startTime while pause-pending and play-pending');
 
 promise_test(function(t) {
   var div = addDiv(t, { 'style': 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
   // Seek to end to put us in the finished state
   animation.currentTime = 100 * MS_PER_SEC;
+
   return animation.ready.then(function() {
     // Call play() which puts us back in the running state
     animation.play();
+
     assert_equals(animation.startTime, null, 'startTime is unresolved');
   });
 }, 'startTime while play-pending from finished state');
 
 test(function(t) {
   var div = addDiv(t, { 'style': 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
   animation.finish();
   // Call play() which puts us back in the running state
   animation.play();
+
   assert_equals(animation.startTime, null, 'startTime is unresolved');
 }, 'startTime while play-pending from finished state using finish()');
 
 promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: anim 1000s' });
+  var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
   assert_equals(animation.startTime, null, 'The initial startTime is null');
   var initialTimelineTime = document.timeline.currentTime;
 
   return animation.ready.then(function() {
     assert_true(animation.startTime > initialTimelineTime,
                 'After the animation has started, startTime is greater than ' +
@@ -262,243 +150,208 @@ promise_test(function(t) {
                   'After actually pausing, the startTime of an animation ' +
                   'is null');
   });
 }, 'Pausing should make the startTime become null');
 
 test(function(t)
 {
   var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = 'anim 100s 100s';
   var animation = div.getAnimations()[0];
   var currentTime = animation.timeline.currentTime;
   animation.startTime = currentTime;
-  assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
+
+  assert_times_equal(animation.startTime, currentTime,
     'Check setting of startTime actually works');
 }, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
    'startTime');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = 'anim 100s 100s';
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
-    checkStateOnReadyPromiseResolved(animation);
+    assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
+      'Animation.startTime should be less than the timeline\'s ' +
+      'currentTime on the first paint tick after animation creation');
 
-    animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
     return eventWatcher.wait_for('animationstart');
   }).then(function() {
-    checkStateAtActiveIntervalStartTime(animation);
-
-    animation.startTime =
-      startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
-    checkStateAtFiftyPctOfActiveInterval(animation);
-
-    animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
     return eventWatcher.wait_for('animationend');
-  }).then(function() {
-    checkStateAtActiveIntervalEndTime(animation);
   });
 }, 'Skipping forward through animation');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = 'anim 100s 100s';
   var animation = div.getAnimations()[0];
-
-  animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
-
+  animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
   var previousTimelineTime = animation.timeline.currentTime;
 
-  // Skipping over the active interval will dispatch an 'animationstart' then
-  // an 'animationend' event. We need to wait for these events before we start
-  // testing going backwards since EventWatcher will fail the test if it gets
-  // an event that we haven't told it about.
   return eventWatcher.wait_for(['animationstart',
                                 'animationend']).then(function() {
     assert_true(document.timeline.currentTime - previousTimelineTime <
-                  ANIM_DUR_MS,
+                  100 * MS_PER_SEC,
                 'Sanity check that seeking worked rather than the events ' +
                 'firing after normal playback through the very long ' +
                 'animation duration');
 
-    // Now we can start the tests for skipping backwards, but first we check
-    // that after the events we're still in the same end time state:
-    checkStateAtActiveIntervalEndTime(animation);
-
-    animation.startTime =
-      startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
     // Despite going backwards from after the end of the animation (to being
     // in the active interval), we now expect an 'animationstart' event
     // because the animation should go from being inactive to active.
-    //
-    // Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
-    // causing computed style to be updated and the 'animationstart' event to
-    // be dispatched synchronously. We need to call wait_for first
-    // otherwise eventWatcher will assert that the event was unexpected.
-    var promise = eventWatcher.wait_for('animationstart');
-    checkStateAtFiftyPctOfActiveInterval(animation);
-    return promise;
+    return eventWatcher.wait_for('animationstart');
   }).then(function() {
-    animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
-    checkStateAtActiveIntervalStartTime(animation);
+    animation.startTime = animation.timeline.currentTime;
 
-    animation.startTime = animation.timeline.currentTime;
     // Despite going backwards from just after the active interval starts to
     // the animation start time, we now expect an animationend event
     // because we went from inside to outside the active interval.
     return eventWatcher.wait_for('animationend');
   }).then(function() {
-    checkStateOnReadyPromiseResolved(animation);
-  })
-
-  // This must come after we've set up the Promise chain, since requesting
-  // computed style will force events to be dispatched.
-  checkStateAtActiveIntervalEndTime(animation);
+    assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
+      'Animation.startTime should be less than the timeline\'s ' +
+      'currentTime on the first paint tick after animation creation');
+  });
 }, 'Skipping backwards through animation');
 
-
 // Next we have multiple tests to check that redundant startTime changes do NOT
 // dispatch events. It's impossible to distinguish between events not being
 // dispatched and events just taking an incredibly long time to dispatch
 // without waiting an infinitely long time. Obviously we don't want to do that
 // (block this test from finishing forever), so instead we just listen for
 // events until two animation frames (i.e. requestAnimationFrame callbacks)
 // have happened, then assume that no events will ever be dispatched for the
 // redundant changes if no events were detected in that time.
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
-  animation.startTime = startTimeForActivePhase(animation.timeline);
-  animation.startTime = startTimeForBeforePhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
+  animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
 
   return waitForAnimationFrames(2);
 }, 'Redundant change, before -> active, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
-  animation.startTime = startTimeForAfterPhase(animation.timeline);
-  animation.startTime = startTimeForBeforePhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
+  animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
 
   return waitForAnimationFrames(2);
 }, 'Redundant change, before -> after, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise =  eventWatcher.wait_for('animationstart').then(function() {
-    animation.startTime = startTimeForBeforePhase(animation.timeline);
-    animation.startTime = startTimeForActivePhase(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
+    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.startTime = startTimeForActivePhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, active -> before, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise = eventWatcher.wait_for('animationstart').then(function() {
-    animation.startTime = startTimeForAfterPhase(animation.timeline);
-    animation.startTime = startTimeForActivePhase(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
+    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.startTime = startTimeForActivePhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, active -> after, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise = eventWatcher.wait_for(['animationstart',
                                           'animationend']).then(function() {
-    animation.startTime = startTimeForBeforePhase(animation.timeline);
-    animation.startTime = startTimeForAfterPhase(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
+    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
   });
   // get us into the initial state:
-  animation.startTime = startTimeForAfterPhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, after -> before, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = ANIM_PROPERTY_VAL;
+  div.style.animation = "anim 100s 100s";
   var animation = div.getAnimations()[0];
 
   var retPromise = eventWatcher.wait_for(['animationstart',
                                           'animationend']).then(function() {
-    animation.startTime = startTimeForActivePhase(animation.timeline);
-    animation.startTime = startTimeForAfterPhase(animation.timeline);
+    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
+    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
 
     return waitForAnimationFrames(2);
+
   });
   // get us into the initial state:
-  animation.startTime = startTimeForAfterPhase(animation.timeline);
+  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
 
   return retPromise;
 }, 'Redundant change, after -> active, then back');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = ANIM_PROPERTY_VAL;
-
+  div.style.animation = 'anim 100s 100s';
   var animation = div.getAnimations()[0];
-
   var storedCurrentTime;
 
   return animation.ready.then(function() {
     storedCurrentTime = animation.currentTime;
     animation.startTime = null;
     return animation.ready;
   }).then(function() {
     assert_equals(animation.currentTime, storedCurrentTime,
       'Test that hold time is correct');
   });
 }, 'Setting startTime to null');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   div.style.animation = 'anim 100s';
-
   var animation = div.getAnimations()[0];
 
   return animation.ready.then(function() {
     var savedStartTime = animation.startTime;
 
     assert_not_equals(animation.startTime, null,
       'Animation.startTime not null on ready Promise resolve');
 
@@ -510,18 +363,18 @@ promise_test(function(t) {
     assert_equals(animation.playState, 'paused',
       'Animation.playState is "paused" after pause() call');
   });
 }, 'Animation.startTime after pausing');
 
 promise_test(function(t) {
   var div = addDiv(t, {'class': 'animated-div'});
   div.style.animation = 'anim 100s';
+  var animation = div.getAnimations()[0];
 
-  var animation = div.getAnimations()[0];
   return animation.ready.then(function() {
     animation.cancel();
     assert_equals(animation.startTime, null,
                   'The startTime of a cancelled animation should be null');
   });
 }, 'Animation.startTime after cancelling');
 
 done();
--- a/dom/animation/test/css-transitions/file_animation-currenttime.html
+++ b/dom/animation/test/css-transitions/file_animation-currenttime.html
@@ -14,22 +14,16 @@
     </style>
     <script src="../testcommon.js"></script>
   </head>
   <body>
     <script type="text/javascript">
 
 'use strict';
 
-// TODO: add equivalent tests without an animation-delay, but first we need to
-// change the timing of animationstart dispatch. (Right now the animationstart
-// event will fire before the ready Promise is resolved if there is no
-// animation-delay.)
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
-
 // TODO: Once the computedTiming property is implemented, add checks to the
 // checker helpers to ensure that computedTiming's properties are updated as
 // expected.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
 
 
 const ANIM_DELAY_MS = 1000000; // 1000s
 const ANIM_DUR_MS = 1000000; // 1000s
--- a/dom/animation/test/css-transitions/file_animation-starttime.html
+++ b/dom/animation/test/css-transitions/file_animation-starttime.html
@@ -14,22 +14,16 @@
     </style>
     <script src="../testcommon.js"></script>
   </head>
   <body>
     <script type="text/javascript">
 
 'use strict';
 
-// TODO: add equivalent tests without an animation-delay, but first we need to
-// change the timing of animationstart dispatch. (Right now the animationstart
-// event will fire before the ready Promise is resolved if there is no
-// animation-delay.)
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
-
 // TODO: Once the computedTiming property is implemented, add checks to the
 // checker helpers to ensure that computedTiming's properties are updated as
 // expected.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
 
 
 const ANIM_DELAY_MS = 1000000; // 1000s
 const ANIM_DUR_MS = 1000000; // 1000s
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -10,16 +10,30 @@
  * failures in asynchronous test. For example, the short duration animation
  * might be finished when animation.ready has been fulfilled because of slow
  * platforms or busyness of the main thread.
  * Setting short duration to cancel its animation does not matter but
  * if you don't want to cancel the animation, consider using longer duration.
  */
 const MS_PER_SEC = 1000;
 
+/* The recommended minimum precision to use for time values[1].
+ *
+ * [1] https://w3c.github.io/web-animations/#precision-of-time-values
+ */
+var TIME_PRECISION = 0.0005; // ms
+
+/*
+ * Allow implementations to substitute an alternative method for comparing
+ * times based on their precision requirements.
+ */
+function assert_times_equal(actual, expected, description) {
+  assert_approx_equals(actual, expected, TIME_PRECISION, description);
+}
+
 /**
  * Appends a div to the document body and creates an animation on the div.
  * NOTE: This function asserts when trying to create animations with durations
  * shorter than 100s because the shorter duration may cause intermittent
  * failures.  If you are not sure how long it is suitable, use 100s; it's
  * long enough but shorter than our test framework timeout (330s).
  * If you really need to use shorter durations, use animate() function directly.
  *
--- a/dom/base/IframeSandboxKeywordList.h
+++ b/dom/base/IframeSandboxKeywordList.h
@@ -3,18 +3,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/. */
 
 /* NOTE: no include guard; this file is meant to maybe be included multiple
    times.  It has a list of the sandbox keywords we support, with their
    corresponding sandbox flags. */
 
-#include "nsSandboxFlags.h"
-
 // Each entry has the sandbox keyword as a string, the corresponding nsGkAtoms
 // atom name, and the corresponding sandbox flags.
 SANDBOX_KEYWORD("allow-same-origin", allowsameorigin,  SANDBOXED_ORIGIN)
 SANDBOX_KEYWORD("allow-forms", allowforms,  SANDBOXED_FORMS)
 SANDBOX_KEYWORD("allow-scripts", allowscripts,
 		SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
 SANDBOX_KEYWORD("allow-top-navigation", allowtopnavigation,
 		SANDBOXED_TOPLEVEL_NAVIGATION)
--- a/dom/base/ImportManager.cpp
+++ b/dom/base/ImportManager.cpp
@@ -591,18 +591,20 @@ ImportLoader::OnStartRequest(nsIRequest*
                                   DocumentFlavorHTML);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
 
   // The imported document must know which master document it belongs to.
   mDocument = do_QueryInterface(importDoc);
   nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
   mDocument->SetMasterDocument(master);
 
-  // We want to inherit the sandbox flags from the master document.
+  // We want to inherit the sandbox flags and fullscreen enabled flag
+  // from the master document.
   mDocument->SetSandboxFlags(master->GetSandboxFlags());
+  mDocument->SetFullscreenEnabled(master->FullscreenEnabledInternal());
 
   // We have to connect the blank document we created with the channel we opened,
   // and create its own LoadGroup for it.
   nsCOMPtr<nsIStreamListener> listener;
   nsCOMPtr<nsILoadGroup> loadGroup;
   channel->GetLoadGroup(getter_AddRefs(loadGroup));
   nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1341,42 +1341,16 @@ nsContentUtils::GetParserService()
     if (NS_FAILED(rv)) {
       sParserService = nullptr;
     }
   }
 
   return sParserService;
 }
 
-/**
- * A helper function that parses a sandbox attribute (of an <iframe> or
- * a CSP directive) and converts it to the set of flags used internally.
- *
- * @param sandboxAttr   the sandbox attribute
- * @return              the set of flags (0 if sandboxAttr is null)
- */
-uint32_t
-nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
-{
-  // No sandbox attribute, no sandbox flags.
-  if (!sandboxAttr) { return 0; }
-
-  //  Start off by setting all the restriction flags.
-  uint32_t out = SANDBOX_ALL_FLAGS;
-
-// Macro for updating the flag according to the keywords
-#define SANDBOX_KEYWORD(string, atom, flags)                             \
-  if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
-
-#include "IframeSandboxKeywordList.h"
-
-  return out;
-#undef SANDBOX_KEYWORD
-}
-
 nsIBidiKeyboard*
 nsContentUtils::GetBidiKeyboard()
 {
   if (!sBidiKeyboard) {
     nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
     if (NS_FAILED(rv)) {
       sBidiKeyboard = nullptr;
     }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -903,25 +903,16 @@ public:
   /**
    * Get the localized string named |aKey| in properties file |aFile|.
    */
   static nsresult GetLocalizedString(PropertiesFile aFile,
                                      const char* aKey,
                                      nsXPIDLString& aResult);
 
   /**
-   * A helper function that parses a sandbox attribute (of an <iframe> or
-   * a CSP directive) and converts it to the set of flags used internally.
-   *
-   * @param sandboxAttr   the sandbox attribute
-   * @return              the set of flags (0 if sandboxAttr is null)
-   */
-  static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
-
-  /**
    * Helper function that generates a UUID.
    */
   static nsresult GenerateUUIDInPlace(nsID& aUUID);
 
   static bool PrefetchEnabled(nsIDocShell* aDocShell);
 
   /**
    * Fill (with the parameters given) the localized string named |aKey| in
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -1,31 +1,37 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 
 #include "nsDOMMutationObserver.h"
 
+#include "mozilla/AnimationTarget.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/OwningNonNull.h"
 
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/KeyframeEffect.h"
 
 #include "nsContentUtils.h"
+#include "nsCSSPseudoElements.h"
 #include "nsError.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTextFragment.h"
 #include "nsThreadUtils.h"
 
+using mozilla::Maybe;
+using mozilla::NonOwningAnimationTarget;
 using mozilla::dom::TreeOrderComparator;
 using mozilla::dom::Animation;
+using mozilla::dom::Element;
 
 AutoTArray<RefPtr<nsDOMMutationObserver>, 4>*
   nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
 
 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
 
 uint32_t nsDOMMutationObserver::sMutationLevel = 0;
 uint64_t nsDOMMutationObserver::sCount = 0;
@@ -388,24 +394,24 @@ nsAnimationReceiver::RecordAnimationMuta
     return;
   }
 
   Maybe<NonOwningAnimationTarget> animationTarget = effect->GetTarget();
   if (!animationTarget) {
     return;
   }
 
-  dom::Element* elem = animationTarget->mElement;
+  Element* elem = animationTarget->mElement;
   if (!Animations() || !(Subtree() || elem == Target()) ||
       elem->ChromeOnlyAccess()) {
     return;
   }
 
   // Record animations targeting to a pseudo element only when subtree is true.
-  if (animationTarget->mPseudoType != CSSPseudoElementType::NotPseudo &&
+  if (animationTarget->mPseudoType != mozilla::CSSPseudoElementType::NotPseudo &&
       !Subtree()) {
     return;
   }
 
   if (nsAutoAnimationMutationBatch::IsBatching()) {
     switch (aMutationType) {
       case eAnimationMutation_Added:
         nsAutoAnimationMutationBatch::AnimationAdded(aAnimation, elem);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -492,16 +492,29 @@ nsDOMWindowUtils::SetResolutionAndScaleT
   }
 
   presShell->SetResolutionAndScaleTo(aResolution);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::SetRestoreResolution(float aResolution)
+{
+  nsIPresShell* presShell = GetPresShell();
+  if (!presShell) {
+    return NS_ERROR_FAILURE;
+  }
+
+  presShell->SetRestoreResolution(aResolution);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetResolution(float* aResolution)
 {
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     return NS_ERROR_FAILURE;
   }
 
   *aResolution = presShell->GetResolution();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1447,16 +1447,17 @@ nsIDocument::nsIDocument()
     // unless we get a window, and in that case the docshell value will get
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
     mFontFaceSetDirty(true),
     mGetUserFontSetCalled(false),
     mPostedFlushUserFontSet(false),
+    mFullscreenEnabled(false),
     mPartID(0),
     mDidFireDOMContentLoaded(true),
     mHasScrollLinkedEffect(false),
     mUserHasInteracted(false)
 {
   SetInDocument();
 
   PR_INIT_CLIST(&mDOMMediaQueryLists);
@@ -2490,17 +2491,17 @@ WarnIfSandboxIneffective(nsIDocShell* aD
     if (!parentChannel) {
       return;
     }
     nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
     if (NS_FAILED(rv)) {
       return;
     }
 
-    nsCOMPtr<nsIDocument> parentDocument = do_GetInterface(parentDocShell);
+    nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument();
     nsCOMPtr<nsIURI> iframeUri;
     parentChannel->GetURI(getter_AddRefs(iframeUri));
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Iframe Sandbox"),
                                     parentDocument,
                                     nsContentUtils::eSECURITY_PROPERTIES,
                                     "BothAllowScriptsAndSameOriginPresent",
                                     nullptr, 0, iframeUri);
@@ -2574,23 +2575,25 @@ nsDocument::StartDocumentLoad(const char
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       mIsSrcdocDocument = true;
     }
   }
 
   // If this document is being loaded by a docshell, copy its sandbox flags
-  // to the document. These are immutable after being set here.
+  // to the document, and store the fullscreen enabled flag. These are
+  // immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
+    mFullscreenEnabled = docShell->GetFullscreenAllowed();
   }
 
   // The CSP directive upgrade-insecure-requests not only applies to the
   // toplevel document, but also to nested documents. Let's propagate that
   // flag from the parent to the nested document.
   nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
   if (treeItem) {
     nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
@@ -11763,30 +11766,19 @@ GetFullscreenError(nsIDocument* aDoc, bo
     // in a Runnable, so don't use GetMozFullScreenEnabled() from a
     // Runnable!
     return nullptr;
   }
 
   if (!nsContentUtils::IsFullScreenApiEnabled()) {
     return "FullscreenDeniedDisabled";
   }
-  if (!aDoc->IsVisible()) {
-    return "FullscreenDeniedHidden";
-  }
-  if (HasFullScreenSubDocument(aDoc)) {
-    return "FullscreenDeniedSubDocFullScreen";
-  }
-
-  // Ensure that all containing elements are <iframe> and have
-  // allowfullscreen attribute set.
-  nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
-  if (!docShell || !docShell->GetFullscreenAllowed()) {
+  if (!aDoc->FullscreenEnabledInternal()) {
     return "FullscreenDeniedContainerNotAllowed";
   }
-
   return nullptr;
 }
 
 bool
 nsDocument::FullscreenElementReadyCheck(Element* aElement,
                                         bool aWasCallerChrome)
 {
   NS_ASSERTION(aElement,
@@ -11805,16 +11797,24 @@ nsDocument::FullscreenElementReadyCheck(
   if (!GetWindow()) {
     DispatchFullscreenError("FullscreenDeniedLostWindow");
     return false;
   }
   if (const char* msg = GetFullscreenError(this, aWasCallerChrome)) {
     DispatchFullscreenError(msg);
     return false;
   }
+  if (!IsVisible()) {
+    DispatchFullscreenError("FullscreenDeniedHidden");
+    return false;
+  }
+  if (HasFullScreenSubDocument(this)) {
+    DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
+    return false;
+  }
   if (GetFullscreenElement() &&
       !nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) {
     // If this document is full-screen, only grant full-screen requests from
     // a descendant of the current full-screen element.
     DispatchFullscreenError("FullscreenDeniedNotDescendant");
     return false;
   }
   if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -220,16 +220,21 @@ nsFrameLoader::LoadFrame()
     }
   }
 
   nsIDocument* doc = mOwnerContent->OwnerDoc();
   if (doc->IsStaticDocument()) {
     return NS_OK;
   }
 
+  if (doc->IsLoadedAsInteractiveData()) {
+    // XBL bindings doc shouldn't load sub-documents.
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI();
   const nsAFlatCString &doc_charset = doc->GetDocumentCharacterSet();
   const char *charset = doc_charset.IsEmpty() ? nullptr : doc_charset.get();
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri);
 
   // If the URI was malformed, try to recover by loading about:blank.
@@ -1913,23 +1918,26 @@ nsFrameLoader::MaybeCreateDocShell()
     return NS_OK;
   }
   NS_ENSURE_STATE(!mDestroyCalled);
 
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->OwnerDoc();
+
+  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
+
   if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (doc->IsResourceDoc() || !doc->IsActive()) {
-    // Don't allow subframe loads in resource documents, nor
-    // in non-active documents.
+  if (!doc->IsActive()) {
+    // Don't allow subframe loads in non-active documents.
+    // (See bug 610571 comment 5.)
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
   nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(docShell);
   NS_ENSURE_STATE(parentAsWebNav);
 
   // Create the docshell...
@@ -2090,20 +2098,19 @@ nsFrameLoader::MaybeCreateDocShell()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
 
   if (OwnerIsMozBrowserOrAppFrame()) {
     // For inproc frames, set the docshell properties.
-    nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
     nsAutoString name;
     if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
-      item->SetName(name);
+      docShell->SetName(name);
     }
     mDocShell->SetFullscreenAllowed(
       mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
       mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen));
     bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
     if (isPrivate) {
       bool nonBlank;
       mDocShell->GetHasLoadedNonBlankURI(&nonBlank);
@@ -2246,19 +2253,17 @@ nsresult
 nsFrameLoader::GetWindowDimensions(nsIntRect& aRect)
 {
   // Need to get outer window position here
   nsIDocument* doc = mOwnerContent->GetComposedDoc();
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
 
-  if (doc->IsResourceDoc()) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
 
   nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
   if (!parentAsItem) {
@@ -2404,18 +2409,21 @@ nsFrameLoader::TryRemoteBrowser()
   //XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
   //         element isn't in document, only in shadow dom, but that will change
   //         https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
   nsIDocument* doc = mOwnerContent->GetComposedDoc();
   if (!doc) {
     return false;
   }
 
-  if (doc->IsResourceDoc()) {
-    // Don't allow subframe loads in external reference documents
+  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
+
+  if (!doc->IsActive()) {
+    // Don't allow subframe loads in non-active documents.
+    // (See bug 610571 comment 5.)
     return false;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
   if (!parentWin) {
     return false;
   }
 
@@ -3223,17 +3231,18 @@ nsFrameLoader::StartPersistence(uint64_t
   if (!aRecv) {
     return NS_ERROR_INVALID_POINTER;
   }
 
   if (mRemoteBrowser) {
     return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
   }
 
-  nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(mDocShell);
+  nsCOMPtr<nsIDocument> rootDoc =
+    mDocShell ? mDocShell->GetDocument() : nullptr;
   nsCOMPtr<nsIDocument> foundDoc;
   if (aOuterWindowID) {
     foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
   } else {
     foundDoc = rootDoc;
   }
 
   if (!foundDoc) {
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -9,16 +9,17 @@
 #include "DOMMediaStream.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsClassHashtable.h"
+#include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPrincipal.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 
 using mozilla::dom::BlobImpl;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2567,16 +2567,21 @@ public:
   // Not const because all the full-screen goop is not const
   virtual bool FullscreenEnabled() = 0;
   virtual Element* GetFullscreenElement() = 0;
   bool Fullscreen()
   {
     return !!GetFullscreenElement();
   }
   void ExitFullscreen();
+  bool FullscreenEnabledInternal() const { return mFullscreenEnabled; }
+  void SetFullscreenEnabled(bool aEnabled)
+  {
+    mFullscreenEnabled = aEnabled;
+  }
   Element* GetMozPointerLockElement();
   void MozExitPointerLock()
   {
     UnlockPointer(this);
   }
   bool Hidden() const
   {
     return mVisibilityState != mozilla::dom::VisibilityState::Visible;
@@ -3026,16 +3031,20 @@ protected:
   bool mFontFaceSetDirty : 1;
 
   // Has GetUserFontSet() been called?
   bool mGetUserFontSetCalled : 1;
 
   // Do we currently have an event posted to call FlushUserFontSet?
   bool mPostedFlushUserFontSet : 1;
 
+  // Whether fullscreen is enabled for this document. This corresponds
+  // to the "fullscreen enabled flag" in the HTML spec.
+  bool mFullscreenEnabled : 1;
+
   enum Type {
     eUnknown, // should never be used
     eHTML,
     eXHTML,
     eGenericXML,
     eSVG,
     eXUL
   };
--- a/dom/base/nsSandboxFlags.h
+++ b/dom/base/nsSandboxFlags.h
@@ -77,22 +77,17 @@ const unsigned long SANDBOXED_AUTOMATIC_
  * access the origin's data.
  */
 // We don't have an explicit representation of this one, apparently?
 // const unsigned long SANDBOXED_STORAGE_AREA_URLS = 0x200;
 
 /**
  * This flag prevents content from using the requestFullscreen() method.
  */
-// We don't implement this yet.  See represent this as a sandbox flag; instead it's an explicit check for
-// the "allowfullscreen" attribute on the <iframe> that includes us.
-// XXXbz This is wrong in two ways: It can change during the life of the
-// document, and it doesn't get correctly propagated to popups.  See
-// https://bugzilla.mozilla.org/show_bug.cgi?id=1270648
-// const unsigned long SANDBOXED_FULLSCREEN = 0x400;
+const unsigned long SANDBOXED_FULLSCREEN = 0x400;
 
 /**
  * This flag blocks the document from changing document.domain.
  */
 const unsigned long SANDBOXED_DOMAIN = 0x800;
 
 /**
  * This flag prevents content from using window.alert(), window.confirm(),
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1885,16 +1885,21 @@ DOMInterfaces = {
         'register': False
         },
 
 'TestInterfaceWithPromiseConstructorArg' : {
         'headerFile': 'TestBindingHeader.h',
         'register': False,
         },
 
+'TestSecureContextInterface' : {
+        # Keep this in sync with TestExampleInterface
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
 }
 
 # These are temporary, until they've been converted to use new DOM bindings
 def addExternalIface(iface, nativeType=None, headerFile=None,
                      notflattened=False):
     if iface in DOMInterfaces:
         raise Exception('Interface declared both as WebIDL and External interface')
     domInterface = {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1867,32 +1867,35 @@ def getAvailableInTestFunc(obj):
 class MemberCondition:
     """
     An object representing the condition for a member to actually be
     exposed.  Any of the arguments can be None.  If not
     None, they should have the following types:
 
     pref: The name of the preference.
     func: The name of the function.
+    secureContext: A bool indicating whether a secure context is required.
     available: A string indicating where we should be available.
     checkAnyPermissions: An integer index for the anypermissions_* to use.
     checkAllPermissions: An integer index for the allpermissions_* to use.
     nonExposedGlobals: A set of names of globals.  Can be empty, in which case
                        it's treated the same way as None.
     """
-    def __init__(self, pref=None, func=None, available=None,
+    def __init__(self, pref=None, func=None, secureContext=False, available=None,
                  checkAnyPermissions=None, checkAllPermissions=None,
                  nonExposedGlobals=None):
         assert pref is None or isinstance(pref, str)
         assert func is None or isinstance(func, str)
+        assert isinstance(secureContext, bool)
         assert available is None or isinstance(available, str)
         assert checkAnyPermissions is None or isinstance(checkAnyPermissions, int)
         assert checkAllPermissions is None or isinstance(checkAllPermissions, int)
         assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
         self.pref = pref
+        self.secureContext = secureContext
 
         def toFuncPtr(val):
             if val is None:
                 return "nullptr"
             return "&" + val
         self.func = toFuncPtr(func)
         self.available = toFuncPtr(available)
         if checkAnyPermissions is None:
@@ -1909,26 +1912,28 @@ class MemberCondition:
             self.nonExposedGlobals = " | ".join(
                 map(lambda g: "GlobalNames::%s" % g,
                     sorted(nonExposedGlobals)))
         else:
             self.nonExposedGlobals = "0"
 
     def __eq__(self, other):
         return (self.pref == other.pref and self.func == other.func and
+                self.secureContext == other.secureContext and
                 self.available == other.available and
                 self.checkAnyPermissions == other.checkAnyPermissions and
                 self.checkAllPermissions == other.checkAllPermissions and
                 self.nonExposedGlobals == other.nonExposedGlobals)
 
     def __ne__(self, other):
         return not self.__eq__(other)
 
     def hasDisablers(self):
         return (self.pref is not None or
+                self.secureContext or
                 self.func != "nullptr" or
                 self.available != "nullptr" or
                 self.checkAnyPermissions != "nullptr" or
                 self.checkAllPermissions != "nullptr" or
                 self.nonExposedGlobals != "0")
 
 
 class PropertyDefiner:
@@ -2007,16 +2012,17 @@ class PropertyDefiner:
                 # of some sort.
                 nonExposureSet.difference_update(interface.getWorkerExposureSet())
 
         return MemberCondition(
             PropertyDefiner.getStringAttr(interfaceMember,
                                           "Pref"),
             PropertyDefiner.getStringAttr(interfaceMember,
                                           "Func"),
+            interfaceMember.getExtendedAttribute("SecureContext") is not None,
             getAvailableInTestFunc(interfaceMember),
             descriptor.checkAnyPermissionsIndicesForMembers.get(interfaceMember.identifier.name),
             descriptor.checkAllPermissionsIndicesForMembers.get(interfaceMember.identifier.name),
             nonExposureSet)
 
     def generatePrefableArray(self, array, name, specFormatter, specTerminator,
                               specType, getCondition, getDataTuple, doIdArrays):
         """
@@ -2053,17 +2059,17 @@ class PropertyDefiner:
 
         specs = []
         disablers = []
         prefableSpecs = []
 
         disablersTemplate = dedent(
             """
             static PrefableDisablers %s_disablers%d = {
-              true, %s, %s, %s, %s, %s
+              true, %s, %s, %s, %s, %s, %s
             };
             """)
         prefableWithDisablersTemplate = '  { &%s_disablers%d, &%s_specs[%d] }'
         prefableWithoutDisablersTemplate = '  { nullptr, &%s_specs[%d] }'
         prefCacheTemplate = '&%s[%d].disablers->enabled'
 
         def switchToCondition(props, condition):
             # Remember the info about where our pref-controlled
@@ -2073,16 +2079,17 @@ class PropertyDefiner:
                     (condition.pref,
                      prefCacheTemplate % (name, len(prefableSpecs))))
             # Set up pointers to the new sets of specs inside prefableSpecs
             if condition.hasDisablers():
                 prefableSpecs.append(prefableWithDisablersTemplate %
                                      (name, len(specs), name, len(specs)))
                 disablers.append(disablersTemplate %
                                  (name, len(specs),
+                                  toStringBool(condition.secureContext),
                                   condition.nonExposedGlobals,
                                   condition.func,
                                   condition.available,
                                   condition.checkAnyPermissions,
                                   condition.checkAllPermissions))
             else:
                 prefableSpecs.append(prefableWithoutDisablersTemplate %
                                      (name, len(specs)))
@@ -3326,16 +3333,18 @@ class CGConstructorEnabled(CGAbstractMet
             assert isinstance(pref, list) and len(pref) == 1
             conditions.append('Preferences::GetBool("%s")' % pref[0])
         if iface.getExtendedAttribute("ChromeOnly"):
             conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()")
         func = iface.getExtendedAttribute("Func")
         if func:
             assert isinstance(func, list) and len(func) == 1
             conditions.append("%s(aCx, aObj)" % func[0])
+        if iface.getExtendedAttribute("SecureContext"):
+            conditions.append("mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)")
         availableIn = getAvailableInTestFunc(iface)
         if availableIn:
             conditions.append("%s(aCx, aObj)" % availableIn)
         checkAnyPermissions = self.descriptor.checkAnyPermissionsIndex
         if checkAnyPermissions is not None:
             conditions.append("CheckAnyPermissions(aCx, aObj, anypermissions_%i)" % checkAnyPermissions)
         checkAllPermissions = self.descriptor.checkAllPermissionsIndex
         if checkAllPermissions is not None:
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -765,16 +765,20 @@ class Descriptor(DescriptorProvider):
 
         """
         return (self.interface.isExternal() or self.concrete or
                 self.interface.hasInterfacePrototypeObject() or
                 any((m.isAttr() or m.isMethod()) and m.isStatic() for m in self.interface.members) or
                 self.interface.parent)
 
     def hasThreadChecks(self):
+        # isExposedConditionally does not necessarily imply thread checks
+        # (since at least [SecureContext] is independent of them), but we're
+        # only used to decide whether to include nsThreadUtils.h, so we don't
+        # worry about that.
         return ((self.isExposedConditionally() and
                  not self.interface.isExposedInWindow()) or
                 self.interface.isExposedInSomeButNotAllWorkers())
 
     def isExposedConditionally(self):
         return (self.interface.isExposedConditionally() or
                 self.interface.isExposedInSomeButNotAllWorkers())
 
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -26,16 +26,49 @@ class nsCycleCollectionParticipant;
 
 // We use these flag bits for the new bindings.
 #define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
 #define JSCLASS_IS_DOMIFACEANDPROTOJSCLASS JSCLASS_USERBIT2
 
 namespace mozilla {
 namespace dom {
 
+/**
+ * Returns true if code running in the given JSContext is allowed to access
+ * [SecureContext] API on the given JSObject.
+ *
+ * [SecureContext] API exposure is restricted to use by code in a Secure
+ * Contexts:
+ *
+ *   https://w3c.github.io/webappsec-secure-contexts/
+ *
+ * Since we want [SecureContext] exposure to depend on the privileges of the
+ * running code (rather than the privileges of an object's creator), this
+ * function checks to see whether the given JSContext's Compartment is flagged
+ * as a Secure Context.  That allows us to make sure that system principal code
+ * (which is marked as a Secure Context) can access Secure Context API on an
+ * object in a different compartment, regardless of whether the other
+ * compartment is a Secure Context or not.
+ *
+ * Checking the JSContext's Compartment doesn't work for expanded principal
+ * globals accessing a Secure Context web page though (e.g. those used by frame
+ * scripts).  To handle that we fall back to checking whether the JSObject came
+ * from a Secure Context.
+ *
+ * Note: We'd prefer this function to live in BindingUtils.h, but we need to
+ * call it in this header, and BindingUtils.h includes us (i.e. we'd have a
+ * circular dependency between headers if it lived there).
+ */
+inline bool
+IsSecureContextOrObjectIsFromSecureContext(JSContext* aCx, JSObject* aObj)
+{
+  return JS::CompartmentCreationOptionsRef(js::GetContextCompartment(aCx)).secureContext() ||
+         JS::CompartmentCreationOptionsRef(js::GetObjectCompartment(aObj)).secureContext();
+}
+
 typedef bool
 (* ResolveOwnProperty)(JSContext* cx, JS::Handle<JSObject*> wrapper,
                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                        JS::MutableHandle<JS::PropertyDescriptor> desc);
 
 typedef bool
 (* EnumerateOwnProperties)(JSContext* cx, JS::Handle<JSObject*> wrapper,
                            JS::Handle<JSObject*> obj,
@@ -93,16 +126,19 @@ struct PrefableDisablers {
     if (nonExposedGlobals &&
         IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
                            nonExposedGlobals)) {
       return false;
     }
     if (!enabled) {
       return false;
     }
+    if (secureContext && !IsSecureContextOrObjectIsFromSecureContext(cx, obj)) {
+      return false;
+    }
     if (enabledFunc &&
         !enabledFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
       return false;
     }
     if (availableFunc &&
         !availableFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
       return false;
     }
@@ -118,16 +154,19 @@ struct PrefableDisablers {
     }
     return true;
   }
 
   // A boolean indicating whether this set of specs is enabled. Not const
   // because it will change at runtime if the corresponding pref is changed.
   bool enabled;
 
+  // A boolean indicating whether a Secure Context is required.
+  const bool secureContext;
+
   // Bitmask of global names that we should not be exposed in.
   const uint16_t nonExposedGlobals;
 
   // A function pointer to a function that can say the property is disabled
   // even if "enabled" is set to true.  If the pointer is null the value of
   // "enabled" is used as-is unless availableFunc overrides.
   const PropertyEnabled enabledFunc;
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -590,26 +590,37 @@ class IDLPartialInterface(IDLObject):
         assert isinstance(name, IDLUnresolvedIdentifier)
 
         IDLObject.__init__(self, location)
         self.identifier = name
         self.members = members
         # propagatedExtendedAttrs are the ones that should get
         # propagated to our non-partial interface.
         self.propagatedExtendedAttrs = []
+        self._haveSecureContextExtendedAttribute = False
         self._nonPartialInterface = nonPartialInterface
         self._finished = False
         nonPartialInterface.addPartialInterface(self)
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             if identifier in ["Constructor", "NamedConstructor"]:
                 self.propagatedExtendedAttrs.append(attr)
+            elif identifier == "SecureContext":
+                self._haveSecureContextExtendedAttribute = True
+                # This gets propagated to all our members.
+                for member in self.members:
+                    if member.getExtendedAttribute("SecureContext"):
+                        raise WebIDLError("[SecureContext] specified on both a "
+                                          "partial interface member and on the "
+                                          "partial interface itself",
+                                          [member.location, attr.location])
+                    member.addExtendedAttributes([attr])
             elif identifier == "Exposed":
                 # This just gets propagated to all our members.
                 for member in self.members:
                     if len(member._exposureGlobalNames) != 0:
                         raise WebIDLError("[Exposed] specified on both a "
                                           "partial interface member and on the "
                                           "partial interface itself",
                                           [member.location, attr.location])
@@ -618,16 +629,26 @@ class IDLPartialInterface(IDLObject):
                 raise WebIDLError("Unknown extended attribute %s on partial "
                                   "interface" % identifier,
                                   [attr.location])
 
     def finish(self, scope):
         if self._finished:
             return
         self._finished = True
+        if (not self._haveSecureContextExtendedAttribute and
+            self._nonPartialInterface.getExtendedAttribute("SecureContext")):
+            # This gets propagated to all our members.
+            for member in self.members:
+                if member.getExtendedAttribute("SecureContext"):
+                    raise WebIDLError("[SecureContext] specified on both a "
+                                      "partial interface member and on the "
+                                      "non-partial interface",
+                                      [member.location, self._nonPartialInterface.location])
+                member.addExtendedAttributes([IDLExtendedAttribute(self._nonPartialInterface.location, ("SecureContext",))])
         # Need to make sure our non-partial interface gets finished so it can
         # report cases when we only have partial interfaces.
         self._nonPartialInterface.finish(scope)
 
     def validate(self):
         pass
 
 
@@ -838,16 +859,27 @@ class IDLInterface(IDLObjectWithScope, I
                 not self.getExtendedAttribute("NoInterfaceObject")):
                 raise WebIDLError("Interface %s does not have "
                                   "[NoInterfaceObject] but inherits from "
                                   "interface %s which does" %
                                   (self.identifier.name,
                                    self.parent.identifier.name),
                                   [self.location, self.parent.location])
 
+            # Interfaces that are not [SecureContext] can't inherit
+            # from [SecureContext] interfaces.
+            if (self.parent.getExtendedAttribute("SecureContext") and
+                not self.getExtendedAttribute("SecureContext")):
+                raise WebIDLError("Interface %s does not have "
+                                  "[SecureContext] but inherits from "
+                                  "interface %s which does" %
+                                  (self.identifier.name,
+                                   self.parent.identifier.name),
+                                  [self.location, self.parent.location])
+
         for iface in self.implementedInterfaces:
             iface.finish(scope)
 
         cycleInGraph = self.findInterfaceLoopPoint(self)
         if cycleInGraph:
             raise WebIDLError("Interface %s has itself as ancestor or "
                               "implemented interface" % self.identifier.name,
                               [self.location, cycleInGraph.location])
@@ -1206,16 +1238,17 @@ class IDLInterface(IDLObjectWithScope, I
                     if self.isOnGlobalProtoChain():
                         raise WebIDLError("[Alias] must not be used on a "
                                           "[Global] interface operation",
                                           [member.location])
                     if (member.getExtendedAttribute("Exposed") or
                         member.getExtendedAttribute("ChromeOnly") or
                         member.getExtendedAttribute("Pref") or
                         member.getExtendedAttribute("Func") or
+                        member.getExtendedAttribute("SecureContext") or
                         member.getExtendedAttribute("AvailableIn") or
                         member.getExtendedAttribute("CheckAnyPermissions") or
                         member.getExtendedAttribute("CheckAllPermissions")):
                         raise WebIDLError("[Alias] must not be used on a "
                                           "conditionally exposed operation",
                                           [member.location])
                     if member.isStatic():
                         raise WebIDLError("[Alias] must not be used on a "
@@ -1470,16 +1503,28 @@ class IDLInterface(IDLObjectWithScope, I
                         "[PrimaryGlobal] specified twice",
                         [attr.location,
                          self.parentScope.primaryGlobalAttr.location])
                 self.parentScope.primaryGlobalAttr = attr
                 self.parentScope.primaryGlobalName = self.identifier.name
                 self.parentScope.globalNames.add(self.identifier.name)
                 self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
                 self._isOnGlobalProtoChain = True
+            elif identifier == "SecureContext":
+                if not attr.noArguments():
+                    raise WebIDLError("[%s] must take no arguments" % identifier,
+                                      [attr.location])
+                # This gets propagated to all our members.
+                for member in self.members:
+                    if member.getExtendedAttribute("SecureContext"):
+                        raise WebIDLError("[SecureContext] specified on both "
+                                          "an interface member and on the "
+                                          "interface itself",
+                                          [member.location, attr.location])
+                    member.addExtendedAttributes([attr])
             elif (identifier == "NeedResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
                   identifier == "UnsafeInPrerendering" or
                   identifier == "LegacyEventInit" or
                   identifier == "ProbablyShortLivingObject" or
                   identifier == "LegacyUnenumerableNamedProperties" or
@@ -1650,16 +1695,17 @@ class IDLInterface(IDLObjectWithScope, I
         if self.parent:
             deps.add(self.parent)
         return deps
 
     def hasMembersInSlots(self):
         return self._ownMembersInSlots != 0
 
     conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
+                                    "SecureContext",
                                     "CheckAnyPermissions",
                                     "CheckAllPermissions" ]
     def isExposedConditionally(self):
         return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
 
 
 class IDLDictionary(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members):
@@ -2066,27 +2112,48 @@ class IDLUnresolvedType(IDLType):
         name = self.name.resolve(scope, None)
         return IDLWrapperType(self.location, obj, self._promiseInnerType)
 
     def isDistinguishableFrom(self, other):
         raise TypeError("Can't tell whether an unresolved type is or is not "
                         "distinguishable from other things")
 
 
-class IDLNullableType(IDLType):
+class IDLParameterizedType(IDLType):
+    def __init__(self, location, name, innerType):
+        IDLType.__init__(self, location, name)
+        self.builtin = False
+        self.inner = innerType
+
+    def includesRestrictedFloat(self):
+        return self.inner.includesRestrictedFloat()
+
+    def resolveType(self, parentScope):
+        assert isinstance(parentScope, IDLScope)
+        self.inner.resolveType(parentScope)
+
+    def isComplete(self):
+        return self.inner.isComplete()
+
+    def unroll(self):
+        return self.inner.unroll()
+
+    def _getDependentObjects(self):
+        return self.inner._getDependentObjects()
+
+
+class IDLNullableType(IDLParameterizedType):
     def __init__(self, location, innerType):
         assert not innerType.isVoid()
         assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
 
         name = innerType.name
         if innerType.isComplete():
             name += "OrNull"
-        IDLType.__init__(self, location, name)
-        self.inner = innerType
-        self.builtin = False
+        IDLParameterizedType.__init__(self, location, name, innerType)
 
     def __eq__(self, other):
         return isinstance(other, IDLNullableType) and self.inner == other.inner
 
     def __str__(self):
         return self.inner.__str__() + "OrNull"
 
     def nullable(self):
@@ -2117,19 +2184,16 @@ class IDLNullableType(IDLType):
         return self.inner.isUSVString()
 
     def isFloat(self):
         return self.inner.isFloat()
 
     def isUnrestricted(self):
         return self.inner.isUnrestricted()
 
-    def includesRestrictedFloat(self):
-        return self.inner.includesRestrictedFloat()
-
     def isInteger(self):
         return self.inner.isInteger()
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         return self.inner.isSequence()
@@ -2174,59 +2238,44 @@ class IDLNullableType(IDLType):
         return self.inner.isUnion()
 
     def isSerializable(self):
         return self.inner.isSerializable()
 
     def tag(self):
         return self.inner.tag()
 
-    def resolveType(self, parentScope):
-        assert isinstance(parentScope, IDLScope)
-        self.inner.resolveType(parentScope)
-
-    def isComplete(self):
-        return self.inner.isComplete()
-
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
         if self.inner.nullable():
             raise WebIDLError("The inner type of a nullable type must not be "
                               "a nullable type",
                               [self.location, self.inner.location])
         if self.inner.isUnion():
             if self.inner.hasNullableType:
                 raise WebIDLError("The inner type of a nullable type must not "
                                   "be a union type that itself has a nullable "
                                   "type as a member type", [self.location])
 
         self.name = self.inner.name + "OrNull"
         return self
 
-    def unroll(self):
-        return self.inner.unroll()
-
     def isDistinguishableFrom(self, other):
         if (other.nullable() or (other.isUnion() and other.hasNullableType) or
             other.isDictionary()):
             # Can't tell which type null should become
             return False
         return self.inner.isDistinguishableFrom(other)
 
-    def _getDependentObjects(self):
-        return self.inner._getDependentObjects()
-
-
-class IDLSequenceType(IDLType):
+
+class IDLSequenceType(IDLParameterizedType):
     def __init__(self, location, parameterType):
         assert not parameterType.isVoid()
 
-        IDLType.__init__(self, location, parameterType.name)
-        self.inner = parameterType
-        self.builtin = False
+        IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
         # Need to set self.name up front if our inner type is already complete,
         # since in that case our .complete() won't be called.
         if self.inner.isComplete():
             self.name = self.inner.name + "Sequence"
 
     def __eq__(self, other):
         return isinstance(other, IDLSequenceType) and self.inner == other.inner
 
@@ -2267,89 +2316,58 @@ class IDLSequenceType(IDLType):
         return False
 
     def isEnum(self):
         return False
 
     def isSerializable(self):
         return self.inner.isSerializable()
 
-    def includesRestrictedFloat(self):
-        return self.inner.includesRestrictedFloat()
-
     def tag(self):
         return IDLType.Tags.sequence
 
-    def resolveType(self, parentScope):
-        assert isinstance(parentScope, IDLScope)
-        self.inner.resolveType(parentScope)
-
-    def isComplete(self):
-        return self.inner.isComplete()
-
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
         self.name = self.inner.name + "Sequence"
         return self
 
-    def unroll(self):
-        return self.inner.unroll()
-
     def isDistinguishableFrom(self, other):
         if other.isPromise():
             return False
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isInterface() or
                 other.isDictionary() or
                 other.isCallback() or other.isMozMap())
 
-    def _getDependentObjects(self):
-        return self.inner._getDependentObjects()
-
-
-class IDLMozMapType(IDLType):
-    # XXXbz This is pretty similar to IDLSequenceType in various ways.
-    # And maybe to IDLNullableType.  Should we have a superclass for
-    # "type containing this other type"?  Bug 1015318.
+
+class IDLMozMapType(IDLParameterizedType):
     def __init__(self, location, parameterType):
         assert not parameterType.isVoid()
 
-        IDLType.__init__(self, location, parameterType.name)
-        self.inner = parameterType
-        self.builtin = False
+        IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
         # Need to set self.name up front if our inner type is already complete,
         # since in that case our .complete() won't be called.
         if self.inner.isComplete():
             self.name = self.inner.name + "MozMap"
 
     def __eq__(self, other):
         return isinstance(other, IDLMozMapType) and self.inner == other.inner
 
     def __str__(self):
         return self.inner.__str__() + "MozMap"
 
     def isMozMap(self):
         return True
 
-    def includesRestrictedFloat(self):
-        return self.inner.includesRestrictedFloat()
-
     def tag(self):
         return IDLType.Tags.mozmap
 
-    def resolveType(self, parentScope):
-        assert isinstance(parentScope, IDLScope)
-        self.inner.resolveType(parentScope)
-
-    def isComplete(self):
-        return self.inner.isComplete()
-
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
         self.name = self.inner.name + "MozMap"
         return self
 
     def unroll(self):
         # We do not unroll our inner.  Just stop at ourselves.  That
         # lets us add headers for both ourselves and our inner as
@@ -2363,19 +2381,16 @@ class IDLMozMapType(IDLType):
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isNonCallbackInterface() or other.isSequence())
 
     def isExposedInAllOf(self, exposureSet):
         return self.inner.unroll().isExposedInAllOf(exposureSet)
 
-    def _getDependentObjects(self):
-        return self.inner._getDependentObjects()
-
 
 class IDLUnionType(IDLType):
     def __init__(self, location, memberTypes):
         IDLType.__init__(self, location, "")
         self.memberTypes = memberTypes
         self.hasNullableType = False
         self._dictionaryType = None
         self.flatMemberTypes = None
@@ -3883,16 +3898,17 @@ class IDLConst(IDLInterfaceMember):
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
         if identifier == "Exposed":
             convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
         elif (identifier == "Pref" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
+              identifier == "SecureContext" or
               identifier == "AvailableIn" or
               identifier == "CheckAnyPermissions" or
               identifier == "CheckAllPermissions"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
                               [attr.location])
@@ -4183,16 +4199,17 @@ class IDLAttribute(IDLInterfaceMember):
                                   [attr.location, self.location])
         elif (identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "SetterThrows" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
+              identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "AvailableIn" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
               identifier == "CheckAnyPermissions" or
               identifier == "CheckAllPermissions" or
               identifier == "BinaryName"):
             # Known attributes that we don't need to do anything with here
@@ -4894,16 +4911,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
                                   [attr.location, self.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
+              identifier == "SecureContext" or
               identifier == "AvailableIn" or
               identifier == "CheckAnyPermissions" or
               identifier == "CheckAllPermissions" or
               identifier == "BinaryName" or
               identifier == "StaticClassOverride"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
@@ -0,0 +1,318 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("""
+        [SecureContext]
+        interface TestSecureContextOnInterface {
+          const octet TEST_CONSTANT = 0;
+          readonly attribute byte testAttribute;
+          void testMethod(byte foo);
+        };
+        partial interface TestSecureContextOnInterface {
+          const octet TEST_CONSTANT_2 = 0;
+          readonly attribute byte testAttribute2;
+          void testMethod2(byte foo);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 6, "TestSecureContextOnInterface should have six members")
+    harness.ok(results[0].getExtendedAttribute("SecureContext"),
+               "Interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to constant members")
+    harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to attribute members")
+    harness.ok(results[0].members[2].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to method members")
+    harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to constant members from partial interface")
+    harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to attribute members from partial interface")
+    harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to method members from partial interface")
+
+    # Same thing, but with the partial interface specified first:
+    parser = parser.reset()
+    parser.parse("""
+        partial interface TestSecureContextOnInterfaceAfterPartialInterface {
+          const octet TEST_CONSTANT_2 = 0;
+          readonly attribute byte testAttribute2;
+          void testMethod2(byte foo);
+        };
+        [SecureContext]
+        interface TestSecureContextOnInterfaceAfterPartialInterface {
+          const octet TEST_CONSTANT = 0;
+          readonly attribute byte testAttribute;
+          void testMethod(byte foo);
+        };
+     """)
+    results = parser.finish()
+    harness.check(len(results[1].members), 6, "TestSecureContextOnInterfaceAfterPartialInterface should have six members")
+    harness.ok(results[1].getExtendedAttribute("SecureContext"),
+               "Interface should have [SecureContext] extended attribute")
+    harness.ok(results[1].members[0].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to constant members")
+    harness.ok(results[1].members[1].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to attribute members")
+    harness.ok(results[1].members[2].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to method members")
+    harness.ok(results[1].members[3].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to constant members from partial interface")
+    harness.ok(results[1].members[4].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to attribute members from partial interface")
+    harness.ok(results[1].members[5].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to method members from partial interface")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface TestSecureContextOnPartialInterface {
+          const octet TEST_CONSTANT = 0;
+          readonly attribute byte testAttribute;
+          void testMethod(byte foo);
+        };
+        [SecureContext]
+        partial interface TestSecureContextOnPartialInterface {
+          const octet TEST_CONSTANT_2 = 0;
+          readonly attribute byte testAttribute2;
+          void testMethod2(byte foo);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 6, "TestSecureContextOnPartialInterface should have six members")
+    harness.ok(results[0].getExtendedAttribute("SecureContext") is None,
+               "[SecureContext] should not propagate from a partial interface to the interface")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+               "[SecureContext] should not propagate from a partial interface to the interface's constant members")
+    harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
+               "[SecureContext] should not propagate from a partial interface to the interface's attribute members")
+    harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+               "[SecureContext] should not propagate from a partial interface to the interface's method members")
+    harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"),
+               "Constant members from [SecureContext] partial interface should be [SecureContext]")
+    harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+               "Attribute members from [SecureContext] partial interface should be [SecureContext]")
+    harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"),
+               "Method members from [SecureContext] partial interface should be [SecureContext]")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface TestSecureContextOnInterfaceMembers {
+          const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+          [SecureContext]
+          const octet TEST_SECURE_CONSTANT       = 1;
+          const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+          readonly attribute byte testNonSecureAttribute1;
+          [SecureContext]
+          readonly attribute byte testSecureAttribute;
+          readonly attribute byte testNonSecureAttribute2;
+          void testNonSecureMethod1(byte foo);
+          [SecureContext]
+          void testSecureMethod(byte foo);
+          void testNonSecureMethod2(byte foo);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 9, "TestSecureContextOnInterfaceMembers should have nine members")
+    harness.ok(results[0].getExtendedAttribute("SecureContext") is None,
+               "[SecureContext] on members should not propagate up to the interface")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+               "Constant should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+               "Constant should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+               "Constant should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+               "Attribute should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+               "Attribute should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None,
+               "Attribute should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None,
+               "Method should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"),
+               "Method should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None,
+               "Method should not have [SecureContext] extended attribute")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface TestSecureContextOnPartialInterfaceMembers {
+        };
+        partial interface TestSecureContextOnPartialInterfaceMembers {
+          const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+          [SecureContext]
+          const octet TEST_SECURE_CONSTANT       = 1;
+          const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+          readonly attribute byte testNonSecureAttribute1;
+          [SecureContext]
+          readonly attribute byte testSecureAttribute;
+          readonly attribute byte testNonSecureAttribute2;
+          void testNonSecureMethod1(byte foo);
+          [SecureContext]
+          void testSecureMethod(byte foo);
+          void testNonSecureMethod2(byte foo);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 9, "TestSecureContextOnPartialInterfaceMembers should have nine members")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+               "Constant from partial interface should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+               "Constant from partial interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+               "Constant from partial interface should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+               "Attribute from partial interface should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+               "Attribute from partial interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None,
+               "Attribute from partial interface should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None,
+               "Method from partial interface should not have [SecureContext] extended attribute")
+    harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"),
+               "Method from partial interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None,
+               "Method from partial interface should not have [SecureContext] extended attribute")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [SecureContext=something]
+            interface TestSecureContextTakesNoValue1 {
+              const octet TEST_SECURE_CONSTANT = 0;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "[SecureContext] must take no arguments (testing on interface)")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface TestSecureContextForOverloads1 {
+              [SecureContext]
+              void testSecureMethod(byte foo);
+            };
+            partial interface TestSecureContextForOverloads1 {
+              void testSecureMethod(byte foo, byte bar);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "If [SecureContext] appears on an overloaded operation, then it MUST appear on all overloads")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface TestSecureContextForOverloads2 {
+              [SecureContext]
+              void testSecureMethod(byte foo);
+            };
+            partial interface TestSecureContextForOverloads2 {
+              [SecureContext]
+              void testSecureMethod(byte foo, byte bar);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(not threw, "[SecureContext] can appear on an overloaded operation if it appears on all overloads")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [SecureContext]
+            interface TestSecureContextOnInterfaceAndMember {
+              [SecureContext]
+              void testSecureMethod(byte foo);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "[SecureContext] must not appear on an interface and interface member")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface TestSecureContextOnPartialInterfaceAndMember {
+            };
+            [SecureContext]
+            partial interface TestSecureContextOnPartialInterfaceAndMember {
+              [SecureContext]
+              void testSecureMethod(byte foo);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "[SecureContext] must not appear on a partial interface and one of the partial interface's member's")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [SecureContext]
+            interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+            };
+            partial interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+              [SecureContext]
+              void testSecureMethod(byte foo);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "[SecureContext] must not appear on an interface and one of its partial interface's member's")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [SecureContext]
+            interface TestSecureContextOnInheritedInterface {
+            };
+            interface TestSecureContextNotOnInheritingInterface : TestSecureContextOnInheritedInterface {
+              void testSecureMethod(byte foo);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw, "[SecureContext] must appear on interfaces that inherit from another [SecureContext] interface")
+
+    # Test 'implements'. The behavior tested here may have to change depending
+    # on the resolution of https://github.com/heycam/webidl/issues/118
+    parser = parser.reset()
+    parser.parse("""
+        [SecureContext]
+        interface TestSecureContextInterfaceThatImplementsNonSecureContextInterface {
+          const octet TEST_CONSTANT = 0;
+        };
+        interface TestNonSecureContextInterface {
+          const octet TEST_CONSTANT_2 = 0;
+          readonly attribute byte testAttribute2;
+          void testMethod2(byte foo);
+        };
+        TestSecureContextInterfaceThatImplementsNonSecureContextInterface implements TestNonSecureContextInterface;
+     """)
+    results = parser.finish()
+    harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have two members")
+    harness.ok(results[0].getExtendedAttribute("SecureContext"),
+               "Interface should have [SecureContext] extended attribute")
+    harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+               "[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface")
+    harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
+               "Constants copied from non-[SecureContext] interface should not be [SecureContext]")
+    harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+               "Attributes copied from non-[SecureContext] interface should not be [SecureContext]")
+    harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+               "Methods copied from non-[SecureContext] interface should not be [SecureContext]")
+ 
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -849,16 +849,26 @@ public:
   void Prefable18();
   void Prefable19();
   void Prefable20();
   void Prefable21();
   void Prefable22();
   void Prefable23();
   void Prefable24();
 
+  // Conditionally exposed methods/attributes involving [SecureContext]
+  bool ConditionalOnSecureContext1();
+  bool ConditionalOnSecureContext2();
+  bool ConditionalOnSecureContext3();
+  bool ConditionalOnSecureContext4();
+  void ConditionalOnSecureContext5();
+  void ConditionalOnSecureContext6();
+  void ConditionalOnSecureContext7();
+  void ConditionalOnSecureContext8();
+
   // Miscellania
   int32_t AttrWithLenientThis();
   void SetAttrWithLenientThis(int32_t);
   uint32_t UnforgeableAttr();
   uint32_t UnforgeableAttr2();
   uint32_t UnforgeableMethod();
   uint32_t UnforgeableMethod2();
   void Stringify(nsString&);
@@ -1369,12 +1379,26 @@ public:
 
   static
   already_AddRefed<TestInterfaceWithPromiseConstructorArg>
     Constructor(const GlobalObject&, Promise&, ErrorResult&);
 
   virtual nsISupports* GetParentObject();
 };
 
+class TestSecureContextInterface : public nsISupports, public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  static
+  already_AddRefed<TestSecureContextInterface>
+    Constructor(const GlobalObject&, ErrorResult&);
+
+  static void AlsoSecureContext(const GlobalObject&);
+
+  virtual nsISupports* GetParentObject();
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -859,16 +859,34 @@ interface TestInterface {
   void prefable21();
   [Func="TestFuncControlledMember", AvailableIn=CertifiedApps]
   void prefable22();
   [Pref="abc.def", Func="TestFuncControlledMember", AvailableIn=CertifiedApps]
   void prefable23();
   [Pref="abc.def", Func="TestFuncControlledMember", AvailableIn=PrivilegedApps]
   void prefable24();
 
+  // Conditionally exposed methods/attributes involving [SecureContext]
+  [SecureContext]
+  readonly attribute boolean conditionalOnSecureContext1;
+  [SecureContext, Pref="abc.def"]
+  readonly attribute boolean conditionalOnSecureContext2;
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  readonly attribute boolean conditionalOnSecureContext3;
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  readonly attribute boolean conditionalOnSecureContext4;
+  [SecureContext]
+  void conditionalOnSecureContext5();
+  [SecureContext, Pref="abc.def"]
+  void conditionalOnSecureContext6();
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  void conditionalOnSecureContext7();
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  void conditionalOnSecureContext8();
+
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   [Unforgeable] long unforgeableMethod();
   [Unforgeable, ChromeOnly] long unforgeableMethod2();
   stringifier;
   void passRenamedInterface(TestRenamedInterface arg);
@@ -1164,8 +1182,13 @@ interface TestCppKeywordNamedMethodsInte
 interface TestDeprecatedInterface {
   static void alsoDeprecated();
 };
 
 
 [Constructor(Promise<void> promise)]
 interface TestInterfaceWithPromiseConstructorArg {
 };
+
+[SecureContext]
+interface TestSecureContextInterface {
+  static void alsoSecureContext();
+};
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -712,16 +712,34 @@ interface TestExampleInterface {
   readonly attribute boolean prefable16;
   [Pref="abc.def", Func="TestFuncControlledMember"]
   void prefable17();
   [Func="TestFuncControlledMember"]
   void prefable18();
   [Func="TestFuncControlledMember"]
   void prefable19();
 
+  // Conditionally exposed methods/attributes involving [SecureContext]
+  [SecureContext]
+  readonly attribute boolean conditionalOnSecureContext1;
+  [SecureContext, Pref="abc.def"]
+  readonly attribute boolean conditionalOnSecureContext2;
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  readonly attribute boolean conditionalOnSecureContext3;
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  readonly attribute boolean conditionalOnSecureContext4;
+  [SecureContext]
+  void conditionalOnSecureContext5();
+  [SecureContext, Pref="abc.def"]
+  void conditionalOnSecureContext6();
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  void conditionalOnSecureContext7();
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  void conditionalOnSecureContext8();
+
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   [Unforgeable] long unforgeableMethod();
   [Unforgeable, ChromeOnly] long unforgeableMethod2();
   stringifier;
   void passRenamedInterface(TestRenamedInterface arg);
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -737,16 +737,34 @@ interface TestJSImplInterface {
   void prefable21();
   [Func="TestFuncControlledMember", AvailableIn=CertifiedApps]
   void prefable22();
   [Pref="abc.def", Func="TestFuncControlledMember", AvailableIn=CertifiedApps]
   void prefable23();
   [Pref="abc.def", Func="TestFuncControlledMember", AvailableIn=PrivilegedApps]
   void prefable24();
 
+  // Conditionally exposed methods/attributes involving [SecureContext]
+  [SecureContext]
+  readonly attribute boolean conditionalOnSecureContext1;
+  [SecureContext, Pref="abc.def"]
+  readonly attribute boolean conditionalOnSecureContext2;
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  readonly attribute boolean conditionalOnSecureContext3;
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  readonly attribute boolean conditionalOnSecureContext4;
+  [SecureContext]
+  void conditionalOnSecureContext5();
+  [SecureContext, Pref="abc.def"]
+  void conditionalOnSecureContext6();
+  [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+  void conditionalOnSecureContext7();
+  [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+  void conditionalOnSecureContext8();
+
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   // FIXME: Bug 863954 Unforgeable things get all confused when
   // non-JS-implemented interfaces inherit from JS-implemented ones or vice
   // versa.
   //   [Unforgeable] readonly attribute long unforgeableAttr;
   //   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   //   [Unforgeable] long unforgeableMethod();
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -998,17 +998,17 @@ private:
     ScopedPK11SymKey symKey(PK11_ImportSymKey(slot, mMechanism, PK11_OriginUnwrap,
                                               CKA_SIGN, &keyItem, nullptr));
     if (!symKey) {
       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
     }
 
     // Compute the MAC
     SECItem param = { siBuffer, nullptr, 0 };
-    ScopedPK11Context ctx(PK11_CreateContextBySymKey(mMechanism, CKA_SIGN,
+    UniquePK11Context ctx(PK11_CreateContextBySymKey(mMechanism, CKA_SIGN,
                                                      symKey.get(), &param));
     if (!ctx.get()) {
       return NS_ERROR_DOM_OPERATION_ERR;
     }
     nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
     rv = MapSECStatus(PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1720,18 +1720,22 @@ EventStateManager::GenerateDragGesture(n
     if (Abs(distance.x) > AssertedCast<uint32_t>(pixelThresholdX) ||
         Abs(distance.y) > AssertedCast<uint32_t>(pixelThresholdY)) {
       if (Prefs::ClickHoldContextMenu()) {
         // stop the click-hold before we fire off the drag gesture, in case
         // it takes a long time
         KillClickHoldTimer();
       }
 
-      nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak();
-      nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(container);
+      nsCOMPtr<nsIDocShell> docshell = aPresContext->GetDocShell();
+      if (!docshell) {
+        return;
+      }
+
+      nsCOMPtr<nsPIDOMWindowOuter> window = docshell->GetWindow();
       if (!window)
         return;
 
       RefPtr<DataTransfer> dataTransfer =
         new DataTransfer(window, eDragStart, false, -1);
 
       nsCOMPtr<nsISelection> selection;
       nsCOMPtr<nsIContent> eventContent, targetContent;
--- a/dom/html/HTMLButtonElement.h
+++ b/dom/html/HTMLButtonElement.h
@@ -160,16 +160,17 @@ public:
     SetHTMLAttr(nsGkAtoms::value, aValue, aRv);
   }
 
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
 
 protected:
   virtual ~HTMLButtonElement();
 
   uint8_t mType;
   bool mDisabledChanged;
   bool mInInternalActivate;
--- a/dom/html/HTMLFieldSetElement.h
+++ b/dom/html/HTMLFieldSetElement.h
@@ -21,16 +21,17 @@ namespace dom {
 class HTMLFieldSetElement final : public nsGenericHTMLFormElement,
                                   public nsIDOMHTMLFieldSetElement,
                                   public nsIConstraintValidation
 {
 public:
   using nsGenericHTMLFormElement::GetForm;
   using nsIConstraintValidation::Validity;
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
 
   explicit HTMLFieldSetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLFieldSetElement, fieldset)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -459,17 +459,17 @@ HTMLFormElement::UnbindFromTree(bool aDe
   nsINode* cur;
   do {
     cur = ancestor->GetParentNode();
     if (!cur) {
       break;
     }
     ancestor = cur;
   } while (1);
-  
+
   CollectOrphans(ancestor, mControls->mElements
 #ifdef DEBUG
                  , this
 #endif
                  );
   CollectOrphans(ancestor, mControls->mNotInElements
 #ifdef DEBUG
                  , this
@@ -662,34 +662,34 @@ HTMLFormElement::DoSubmit(WidgetEvent* a
   if (window) {
     mSubmitPopupState = window->GetPopupControlState();
   } else {
     mSubmitPopupState = openAbused;
   }
 
   mSubmitInitiatedFromUserInput = EventStateManager::IsHandlingUserInput();
 
-  if(mDeferSubmission) { 
+  if(mDeferSubmission) {
     // we are in an event handler, JS submitted so we have to
     // defer this submission. let's remember it and return
     // without submitting
     mPendingSubmission = submission;
     // ensure reentrancy
     mIsSubmitting = false;
-    return NS_OK; 
-  } 
-  
-  // 
+    return NS_OK;
+  }
+
+  //
   // perform the submission
   //
-  return SubmitSubmission(submission); 
+  return SubmitSubmission(submission);
 }
 
 nsresult
-HTMLFormElement::BuildSubmission(nsFormSubmission** aFormSubmission, 
+HTMLFormElement::BuildSubmission(nsFormSubmission** aFormSubmission,
                                  WidgetEvent* aEvent)
 {
   NS_ASSERTION(!mPendingSubmission, "tried to build two submissions!");
 
   // Get the originating frame (failure is non-fatal)
   nsGenericHTMLElement* originatingElement = nullptr;
   if (aEvent) {
     InternalFormEvent* formEvent = aEvent->AsFormEvent();
@@ -1061,20 +1061,20 @@ HTMLFormElement::WalkFormElements(nsForm
   }
 
   return NS_OK;
 }
 
 // nsIForm
 
 NS_IMETHODIMP_(uint32_t)
-HTMLFormElement::GetElementCount() const 
+HTMLFormElement::GetElementCount() const
 {
   uint32_t count = 0;
-  mControls->GetLength(&count); 
+  mControls->GetLength(&count);
   return count;
 }
 
 Element*
 HTMLFormElement::IndexedGetter(uint32_t aIndex, bool &aFound)
 {
   Element* element = mControls->mElements.SafeElementAt(aIndex, nullptr);
   aFound = element != nullptr;
@@ -1257,25 +1257,25 @@ HTMLFormElement::AddElement(nsGenericHTM
     if (!gPasswordManagerInitialized) {
       gPasswordManagerInitialized = true;
       NS_CreateServicesFromCategory(NS_PASSWORDMANAGER_CATEGORY,
                                     nullptr,
                                     NS_PASSWORDMANAGER_CATEGORY);
     }
     PostPasswordEvent();
   }
- 
+
   // Default submit element handling
   if (aChild->IsSubmitControl()) {
     // Update mDefaultSubmitElement, mFirstSubmitInElements,
     // mFirstSubmitNotInElements.
 
     nsGenericHTMLFormElement** firstSubmitSlot =
       childInElements ? &mFirstSubmitInElements : &mFirstSubmitNotInElements;
-    
+
     // The new child is the new first submit in its list if the firstSubmitSlot
     // is currently empty or if the child is before what's currently in the
     // slot.  Note that if we already have a control in firstSubmitSlot and
     // we're appending this element can't possibly replace what's currently in
     // the slot.  Also note that aChild can't become the mDefaultSubmitElement
     // unless it replaces what's in the slot.  If it _does_ replace what's in
     // the slot, it becomes the default submit if either the default submit is
     // what's in the slot or the child is earlier than the default submit.
@@ -1329,17 +1329,17 @@ HTMLFormElement::AddElement(nsGenericHTM
 
   return NS_OK;
 }
 
 nsresult
 HTMLFormElement::AddElementToTable(nsGenericHTMLFormElement* aChild,
                                    const nsAString& aName)
 {
-  return mControls->AddElementToTable(aChild, aName);  
+  return mControls->AddElementToTable(aChild, aName);
 }
 
 
 nsresult
 HTMLFormElement::RemoveElement(nsGenericHTMLFormElement* aChild,
                                bool aUpdateValidity)
 {
   //
@@ -1352,17 +1352,17 @@ HTMLFormElement::RemoveElement(nsGeneric
     radio->WillRemoveFromRadioGroup();
   }
 
   // Determine whether to remove the child from the elements list
   // or the not in elements list.
   bool childInElements = HTMLFormControlsCollection::ShouldBeInElements(aChild);
   nsTArray<nsGenericHTMLFormElement*>& controls = childInElements ?
       mControls->mElements :  mControls->mNotInElements;
-  
+
   // Find the index of the child. This will be used later if necessary
   // to find the default submit.
   size_t index = controls.IndexOf(aChild);
   NS_ENSURE_STATE(index != controls.NoIndex);
 
   controls.RemoveElementAt(index);
 
   // Update our mFirstSubmit* values.
@@ -1775,17 +1775,17 @@ HTMLFormElement::GetActionURL(nsIURI** a
 }
 
 NS_IMETHODIMP_(nsIFormControl*)
 HTMLFormElement::GetDefaultSubmitElement() const
 {
   NS_PRECONDITION(mDefaultSubmitElement == mFirstSubmitInElements ||
                   mDefaultSubmitElement == mFirstSubmitNotInElements,
                   "What happened here?");
-  
+
   return mDefaultSubmitElement;
 }
 
 bool
 HTMLFormElement::IsDefaultSubmitElement(const nsIFormControl* aControl) const
 {
   NS_PRECONDITION(aControl, "Unexpected call");
 
@@ -1834,30 +1834,30 @@ HTMLFormElement::ImplicitSubmissionIsDis
   return numDisablingControlsFound != 1;
 }
 
 NS_IMETHODIMP
 HTMLFormElement::GetEncoding(nsAString& aEncoding)
 {
   return GetEnctype(aEncoding);
 }
- 
+
 NS_IMETHODIMP
 HTMLFormElement::SetEncoding(const nsAString& aEncoding)
 {
   return SetEnctype(aEncoding);
 }
 
 int32_t
 HTMLFormElement::Length()
 {
   return mControls->Length();
 }
 
-NS_IMETHODIMP    
+NS_IMETHODIMP
 HTMLFormElement::GetLength(int32_t* aLength)
 {
   *aLength = Length();
   return NS_OK;
 }
 
 void
 HTMLFormElement::ForgetCurrentSubmission()
@@ -2538,17 +2538,17 @@ HTMLFormElement::AddToPastNamesMap(const
   // If candidates contains exactly one node. Add a mapping from name to the
   // node in candidates in the form element's past names map, replacing the
   // previous entry with the same name, if any.
   nsCOMPtr<nsIContent> node = do_QueryInterface(aChild);
   if (node) {
     mPastNameLookupTable.Put(aName, aChild);
   }
 }
- 
+
 JSObject*
 HTMLFormElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLFormElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -382,16 +382,21 @@ public:
 
   // XPCOM Reset() is OK
 
   bool CheckValidity()
   {
     return CheckFormValidity(nullptr);
   }
 
+  bool ReportValidity()
+  {
+    return CheckValidFormSubmission();
+  }
+
   Element*
   IndexedGetter(uint32_t aIndex, bool &aFound);
 
   already_AddRefed<nsISupports>
   NamedGetter(const nsAString& aName, bool &aFound);
 
   void GetSupportedNames(nsTArray<nsString>& aRetval);
 
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/HTMLIFrameElementBinding.h"
 #include "nsMappedAttributes.h"
 #include "nsAttrValueInlines.h"
 #include "nsError.h"
 #include "nsRuleData.h"
 #include "nsStyleConsts.h"
 #include "nsContentUtils.h"
+#include "nsSandboxFlags.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
 
 namespace mozilla {
 namespace dom {
 
 // static
 const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] = {
@@ -210,17 +211,22 @@ HTMLIFrameElement::SetAttr(int32_t aName
   return NS_OK;
 }
 
 nsresult
 HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue,
                                 bool aNotify)
 {
-  if (aName == nsGkAtoms::sandbox && aNameSpaceID == kNameSpaceID_None && mFrameLoader) {
+  if ((aName == nsGkAtoms::sandbox ||
+       // The allowfullscreen attribute affects the sandboxed fullscreen
+       // flag, thus we should also reapply it if that is changed.
+       aName == nsGkAtoms::allowfullscreen ||
+       aName == nsGkAtoms::mozallowfullscreen) &&
+      aNameSpaceID == kNameSpaceID_None && mFrameLoader) {
     // If we have an nsFrameLoader, apply the new sandbox flags.
     // Since this is called after the setter, the sandbox flags have
     // alreay been updated.
     mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
   }
   return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                                  aNotify);
 }
@@ -241,17 +247,36 @@ HTMLIFrameElement::UnsetAttr(int32_t aNa
 
   return NS_OK;
 }
 
 uint32_t
 HTMLIFrameElement::GetSandboxFlags()
 {
   const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox);
-  return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
+  // No sandbox attribute, no sandbox flags.
+  if (!sandboxAttr) {
+    return 0;
+  }
+
+  //  Start off by setting all the restriction flags.
+  uint32_t out = SANDBOX_ALL_FLAGS;
+
+// Macro for updating the flag according to the keywords
+#define SANDBOX_KEYWORD(string, atom, flags)                             \
+  if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
+#include "IframeSandboxKeywordList.h"
+#undef SANDBOX_KEYWORD
+
+  if (GetParsedAttr(nsGkAtoms::allowfullscreen) ||
+      GetParsedAttr(nsGkAtoms::mozallowfullscreen)) {
+    out &= ~SANDBOXED_FULLSCREEN;
+  }
+
+  return out;
 }
 
 JSObject*
 HTMLIFrameElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLIFrameElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -104,16 +104,17 @@ class HTMLInputElement final : public ns
                                public nsITextControlElement,
                                public nsIPhonetic,
                                public nsIDOMNSEditableElement,
                                public nsIConstraintValidation
 {
 public:
   using nsIConstraintValidation::GetValidationMessage;
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::WillValidate;
   using nsIConstraintValidation::Validity;
   using nsGenericHTMLFormElementWithState::GetForm;
 
   HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                    mozilla::dom::FromParser aFromParser);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLInputElement, input)
@@ -1290,25 +1291,25 @@ protected:
    * Hack for bug 1086684: Stash the .value when we're a file picker.
    */
   nsString mFirstFilePath;
 #endif
 
   RefPtr<FileList>  mFileList;
 
   nsString mStaticDocFileList;
-  
-  /** 
+
+  /**
    * The value of the input element when first initialized and it is updated
-   * when the element is either changed through a script, focused or dispatches   
+   * when the element is either changed through a script, focused or dispatches
    * a change event. This is to ensure correct future change event firing.
    * NB: This is ONLY applicable where the element is a text control. ie,
    * where type= "text", "email", "search", "tel", "url" or "password".
    */
-  nsString mFocusedValue;  
+  nsString mFocusedValue;
 
   /**
    * If mIsDraggingRange is true, this is the value that the input had before
    * the drag started. Used to reset the input to its old value if the drag is
    * canceled.
    */
   Decimal mRangeThumbDragStartValue;
 
@@ -1406,17 +1407,17 @@ private:
 
     bool operator== (const nsFilePickerFilter& other) const {
       if ((mFilter == other.mFilter) && (mFilterMask == other.mFilterMask)) {
         return true;
       } else {
         return false;
       }
     }
-    
+
     // Filter mask, using values defined in nsIFilePicker
     int32_t mFilterMask;
     // If mFilterMask is defined, mTitle and mFilter are useless and should be
     // ignored
     nsString mTitle;
     nsString mFilter;
   };
 
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -151,16 +151,17 @@ public:
   }
   void SetHeight(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::height, aValue, aRv);
   }
   using nsObjectLoadingContent::GetContentDocument;
   nsPIDOMWindowOuter* GetContentWindow();
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
   void GetAlign(DOMString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::align, aValue);
   }
   void SetAlign(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::align, aValue, aRv);
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -262,16 +262,17 @@ public:
   void GetValue(DOMString& aValue);
   // Uses XPCOM SetValue.
 
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
 
   using nsINode::Remove;
 
 
   // nsINode
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -373,17 +374,17 @@ public:
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                  nsAttrValueOrString* aValue,
                                  bool aNotify) override;
   virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify) override;
   virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify) override;
-  
+
   virtual void DoneAddingChildren(bool aHaveNotified) override;
   virtual bool IsDoneAddingChildren() override {
     return mIsDoneAddingChildren;
   }
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -55,27 +55,21 @@ protected:
 
   virtual JSObject* GetWrapperPreserveColorInternal() override
   {
     return nsWrapperCache::GetWrapperPreserveColor();
   }
 
   // Those rows that are not in table sections
   HTMLTableElement* mParent;
-  RefPtr<nsContentList> mOrphanRows;  
 };
 
 
 TableRowsCollection::TableRowsCollection(HTMLTableElement *aParent)
   : mParent(aParent)
-  , mOrphanRows(new nsContentList(mParent,
-                                  kNameSpaceID_XHTML,
-                                  nsGkAtoms::tr,
-                                  nsGkAtoms::tr,
-                                  false))
 {
 }
 
 TableRowsCollection::~TableRowsCollection()
 {
   // we do NOT have a ref-counted reference to mParent, so do NOT
   // release it!  this is to avoid circular references.  The
   // instantiator who provided mParent is responsible for managing our
@@ -83,95 +77,103 @@ TableRowsCollection::~TableRowsCollectio
 }
 
 JSObject*
 TableRowsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TableRowsCollection, mOrphanRows)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(TableRowsCollection)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TableRowsCollection)
 
 NS_INTERFACE_TABLE_HEAD(TableRowsCollection)
   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
   NS_INTERFACE_TABLE(TableRowsCollection, nsIHTMLCollection,
                      nsIDOMHTMLCollection)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(TableRowsCollection)
 NS_INTERFACE_MAP_END
 
 // Macro that can be used to avoid copy/pasting code to iterate over the
 // rowgroups.  _code should be the code to execute for each rowgroup.  The
-// rowgroup's rows will be in the nsIDOMHTMLCollection* named "rows".  Note
-// that this may be null at any time.  This macro assumes an nsresult named
+// rowgroup's rows will be in the nsIDOMHTMLCollection* named "rows".
+// _trCode should be the code to execute for each tr row.  Note that
+// this may be null at any time.  This macro assumes an nsresult named
 // |rv| is in scope.
-#define DO_FOR_EACH_ROWGROUP(_code)                                  \
-  do {                                                               \
-    if (mParent) {                                                   \
-      /* THead */                                                    \
-      HTMLTableSectionElement* rowGroup = mParent->GetTHead();       \
-      nsIHTMLCollection* rows;                                       \
-      if (rowGroup) {                                                \
-        rows = rowGroup->Rows();                                     \
-        do { /* gives scoping */                                     \
-          _code                                                      \
-        } while (0);                                                 \
-      }                                                              \
-      /* TBodies */                                                  \
-      for (nsIContent* _node = mParent->nsINode::GetFirstChild();    \
-           _node; _node = _node->GetNextSibling()) {                 \
-        if (_node->IsHTMLElement(nsGkAtoms::tbody)) {                \
-          rowGroup = static_cast<HTMLTableSectionElement*>(_node);   \
-          rows = rowGroup->Rows();                                   \
-          do { /* gives scoping */                                   \
-            _code                                                    \
-          } while (0);                                               \
+  #define DO_FOR_EACH_BY_ORDER(_code, _trCode)                       \
+    do {                                                             \
+      if (mParent) {                                                 \
+        HTMLTableSectionElement* rowGroup;                           \
+        nsIHTMLCollection* rows;                                     \
+        /* THead */                                                  \
+        for (nsIContent* _node = mParent->nsINode::GetFirstChild();  \
+             _node; _node = _node->GetNextSibling()) {               \
+           if (_node->IsHTMLElement(nsGkAtoms::thead)) {             \
+             rowGroup = static_cast<HTMLTableSectionElement*>(_node);\
+             rows = rowGroup->Rows();                                \
+             do { /* gives scoping */                                \
+               _code                                                 \
+             } while (0);                                            \
+           }                                                         \
+        }                                                            \
+        /* TBodies */                                                \
+        for (nsIContent* _node = mParent->nsINode::GetFirstChild();  \
+             _node; _node = _node->GetNextSibling()) {               \
+          if (_node->IsHTMLElement(nsGkAtoms::tr)) {                 \
+            do {                                                     \
+              _trCode                                                \
+            } while (0);                                             \
+          } else if (_node->IsHTMLElement(nsGkAtoms::tbody)) {       \
+            rowGroup = static_cast<HTMLTableSectionElement*>(_node); \
+            rows = rowGroup->Rows();                                 \
+            do { /* gives scoping */                                 \
+              _code                                                  \
+            } while (0);                                             \
+          }                                                          \
+        }                                                            \
+        /* TFoot */                                                  \
+        for (nsIContent* _node = mParent->nsINode::GetFirstChild();  \
+             _node; _node = _node->GetNextSibling()) {               \
+           if (_node->IsHTMLElement(nsGkAtoms::tfoot)) {             \
+             rowGroup = static_cast<HTMLTableSectionElement*>(_node);\
+             rows = rowGroup->Rows();                                \
+             do { /* gives scoping */                                \
+               _code                                                 \
+             } while (0);                                            \
+           }                                                         \
         }                                                            \
       }                                                              \
-      /* orphan rows */                                              \
-      rows = mOrphanRows;                                            \
-      do { /* gives scoping */                                       \
-        _code                                                        \
-      } while (0);                                                   \
-      /* TFoot */                                                    \
-      rowGroup = mParent->GetTFoot();                                \
-      rows = nullptr;                                                \
-      if (rowGroup) {                                                \
-        rows = rowGroup->Rows();                                     \
-        do { /* gives scoping */                                     \
-          _code                                                      \
-        } while (0);                                                 \
-      }                                                              \
-    }                                                                \
-  } while (0)
+    } while (0)
 
 static uint32_t
 CountRowsInRowGroup(nsIDOMHTMLCollection* rows)
 {
   uint32_t length = 0;
-  
+
   if (rows) {
     rows->GetLength(&length);
   }
-  
+
   return length;
 }
 
 // we re-count every call.  A better implementation would be to set
 // ourselves up as an observer of contentAppended, contentInserted,
 // and contentDeleted
-NS_IMETHODIMP 
+NS_IMETHODIMP
 TableRowsCollection::GetLength(uint32_t* aLength)
 {
   *aLength=0;
 
-  DO_FOR_EACH_ROWGROUP(
+  DO_FOR_EACH_BY_ORDER({
     *aLength += CountRowsInRowGroup(rows);
-  );
+  }, {
+    (*aLength)++;
+  });
 
   return NS_OK;
 }
 
 // Returns the item at index aIndex if available. If null is returned,
 // then aCount will be set to the number of rows in this row collection.
 // Otherwise, the value of aCount is undefined.
 static Element*
@@ -182,97 +184,134 @@ GetItemOrCountInRowGroup(nsIDOMHTMLColle
 
   if (rows) {
     rows->GetLength(aCount);
     if (aIndex < *aCount) {
       nsIHTMLCollection* list = static_cast<nsIHTMLCollection*>(rows);
       return list->GetElementAt(aIndex);
     }
   }
-  
+
   return nullptr;
 }
 
 Element*
 TableRowsCollection::GetElementAt(uint32_t aIndex)
 {
-  DO_FOR_EACH_ROWGROUP(
+  DO_FOR_EACH_BY_ORDER({
     uint32_t count;
     Element* node = GetItemOrCountInRowGroup(rows, aIndex, &count);
     if (node) {
-      return node; 
+      return node;
     }
 
     NS_ASSERTION(count <= aIndex, "GetItemOrCountInRowGroup screwed up");
     aIndex -= count;
-  );
+  },{
+    if (aIndex == 0) {
+      return _node->AsElement();
+    }
+    aIndex--;
+  });
 
   return nullptr;
 }
 
-NS_IMETHODIMP 
+NS_IMETHODIMP
 TableRowsCollection::Item(uint32_t aIndex, nsIDOMNode** aReturn)
 {
   nsISupports* node = GetElementAt(aIndex);
   if (!node) {
     *aReturn = nullptr;
 
     return NS_OK;
   }
 
   return CallQueryInterface(node, aReturn);
 }
 
 Element*
 TableRowsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
 {
   aFound = false;
-  DO_FOR_EACH_ROWGROUP(
+  nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
+  NS_ENSURE_TRUE(nameAtom, nullptr);
+  DO_FOR_EACH_BY_ORDER({
     Element* item = rows->NamedGetter(aName, aFound);
     if (aFound) {
       return item;
     }
-  );
+  }, {
+    if (_node->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+                           nameAtom, eCaseMatters) ||
+        _node->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
+                           nameAtom, eCaseMatters)) {
+      aFound = true;
+      return _node->AsElement();
+    }
+  });
+
   return nullptr;
 }
 
 void
 TableRowsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
 {
-  DO_FOR_EACH_ROWGROUP(
+  DO_FOR_EACH_BY_ORDER({
     nsTArray<nsString> names;
     nsCOMPtr<nsIHTMLCollection> coll = do_QueryInterface(rows);
     if (coll) {
       coll->GetSupportedNames(names);
       for (uint32_t i = 0; i < names.Length(); ++i) {
         if (!aNames.Contains(names[i])) {
           aNames.AppendElement(names[i]);
         }
       }
     }
-  );
+  }, {
+    if (_node->HasID()) {
+      nsIAtom* idAtom = _node->GetID();
+      MOZ_ASSERT(idAtom != nsGkAtoms::_empty,
+                 "Empty ids don't get atomized");
+      nsDependentAtomString idStr(idAtom);
+      if (!aNames.Contains(idStr)) {
+        aNames.AppendElement(idStr);
+      }
+    }
+
+    nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(_node);
+    if (el) {
+      const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
+      if (val && val->Type() == nsAttrValue::eAtom) {
+        nsIAtom* nameAtom = val->GetAtomValue();
+        MOZ_ASSERT(nameAtom != nsGkAtoms::_empty,
+                   "Empty names don't get atomized");
+        nsDependentAtomString nameStr(nameAtom);
+        if (!aNames.Contains(nameStr)) {
+          aNames.AppendElement(nameStr);
+        }
+      }
+    }
+  });
 }
 
 
-NS_IMETHODIMP 
+NS_IMETHODIMP
 TableRowsCollection::NamedItem(const nsAString& aName,
                                nsIDOMNode** aReturn)
 {
-  DO_FOR_EACH_ROWGROUP(
-    nsCOMPtr<nsIHTMLCollection> collection = do_QueryInterface(rows);
-    if (collection) {
-      nsresult rv = collection->NamedItem(aName, aReturn);
-      if (NS_FAILED(rv) || *aReturn) {
-        return rv;
-      }
-    }
-  );
+  bool found;
+  nsISupports* node = GetFirstNamedElement(aName, found);
+  if (!node) {
+    *aReturn = nullptr;
 
-  *aReturn = nullptr;
-  return NS_OK;
+    return NS_OK;
+  }
+
+  return CallQueryInterface(node, aReturn);
 }
 
 NS_IMETHODIMP
 TableRowsCollection::ParentDestroyed()
 {
   // see comment in destructor, do NOT release mParent!
   mParent = nullptr;
 
@@ -535,17 +574,17 @@ HTMLTableElement::InsertRow(int32_t aInd
         parent->InsertBefore(*newRow, refRow, aError);
       }
 
       if (aError.Failed()) {
         return nullptr;
       }
     }
   } else {
-    // the row count was 0, so 
+    // the row count was 0, so
     // find the last row group and insert there as first child
     nsCOMPtr<nsIContent> rowGroup;
     for (nsIContent* child = nsINode::GetLastChild();
          child;
          child = child->GetPreviousSibling()) {
       if (child->IsHTMLElement(nsGkAtoms::tbody)) {
         rowGroup = child;
         break;
@@ -636,17 +675,17 @@ HTMLTableElement::ParseAttribute(int32_t
         nsAttrValue::ValueType type = aResult.Type();
         return !((type == nsAttrValue::eInteger &&
                   aResult.GetIntegerValue() == 0) ||
                  (type == nsAttrValue::ePercent &&
                   aResult.GetPercentValue() == 0.0f));
       }
       return false;
     }
-    
+
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor ||
         aAttribute == nsGkAtoms::bordercolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::hspace ||
@@ -712,31 +751,31 @@ HTMLTableElement::MapAttributesIntoRule(
     // vspace is mapped into top and bottom margins
     // - *** Quirks Mode only ***
     if (eCompatibility_NavQuirks == mode) {
       value = aAttributes->GetAttr(nsGkAtoms::hspace);
 
       if (value && value->Type() == nsAttrValue::eInteger) {
         nsCSSValue* marginLeft = aData->ValueForMarginLeft();
         if (marginLeft->GetUnit() == eCSSUnit_Null)
-          marginLeft->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
+          marginLeft->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
         nsCSSValue* marginRight = aData->ValueForMarginRight();
         if (marginRight->GetUnit() == eCSSUnit_Null)
           marginRight->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
       }
 
       value = aAttributes->GetAttr(nsGkAtoms::vspace);
 
       if (value && value->Type() == nsAttrValue::eInteger) {
         nsCSSValue* marginTop = aData->ValueForMarginTop();
         if (marginTop->GetUnit() == eCSSUnit_Null)
-          marginTop->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
+          marginTop->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
         nsCSSValue* marginBottom = aData->ValueForMarginBottom();
         if (marginBottom->GetUnit() == eCSSUnit_Null)
-          marginBottom->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
+          marginBottom->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
       }
     }
   }
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
     // width: value
     nsCSSValue* width = aData->ValueForWidth();
     if (width->GetUnit() == eCSSUnit_Null) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
@@ -810,19 +849,19 @@ HTMLTableElement::IsAttributeMapped(cons
   static const MappedAttributeEntry attributes[] = {
     { &nsGkAtoms::cellpadding },
     { &nsGkAtoms::cellspacing },
     { &nsGkAtoms::border },
     { &nsGkAtoms::width },
     { &nsGkAtoms::height },
     { &nsGkAtoms::hspace },
     { &nsGkAtoms::vspace },
-    
+
     { &nsGkAtoms::bordercolor },
-    
+
     { &nsGkAtoms::align },
     { nullptr }
   };
 
   static const MappedAttributeEntry* const map[] = {
     attributes,
     sCommonAttributeMap,
     sBackgroundAttributeMap,
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -260,16 +260,17 @@ public:
   void SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError);
   // XPCOM GetValue/SetValue are fine
   uint32_t GetTextLength();
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
+  using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
   // XPCOM Select is fine
   uint32_t GetSelectionStart(ErrorResult& aError);
   void SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError);
   uint32_t GetSelectionEnd(ErrorResult& aError);
   void SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError);
   void GetSelectionDirection(nsAString& aDirection, ErrorResult& aError);
   void SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError);
@@ -299,19 +300,19 @@ protected:
   /** Whether state restoration should be inhibited in DoneAddingChildren. */
   bool                     mInhibitStateRestoration;
   /** Whether our disabled state has changed from the default **/
   bool                     mDisabledChanged;
   /** Whether we should make :-moz-ui-invalid apply on the element. **/
   bool                     mCanShowInvalidUI;
   /** Whether we should make :-moz-ui-valid apply on the element. **/
   bool                     mCanShowValidUI;
-  
+
   void FireChangeEventIfNeeded();
-  
+
   nsString mFocusedValue;
 
   /** The state of the text editor (selection controller and the editor) **/
   nsTextEditorState mState;
 
   NS_IMETHOD SelectAll(nsPresContext* aPresContext);
   /**
    * Get the value, whether it is from the content or the frame.
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -118,18 +118,21 @@ nsGenericHTMLFrameElement::GetContentWin
   mFrameLoader->GetDepthTooGreat(&depthTooGreat);
   if (depthTooGreat) {
     // Claim to have no contentWindow
     return nullptr;
   }
 
   nsCOMPtr<nsIDocShell> doc_shell;
   mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
+  if (!doc_shell) {
+    return nullptr;
+  }
 
-  nsCOMPtr<nsPIDOMWindowOuter> win = do_GetInterface(doc_shell);
+  nsCOMPtr<nsPIDOMWindowOuter> win = doc_shell->GetWindow();
 
   if (!win) {
     return nullptr;
   }
 
   NS_ASSERTION(win->IsOuterWindow(),
                "Uh, this window should always be an outer window!");
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2958,17 +2958,17 @@ nsHTMLDocument::GetMidasCommandManager(n
   nsPIDOMWindowOuter *window = GetWindow();
   if (!window)
     return NS_ERROR_FAILURE;
 
   nsIDocShell *docshell = window->GetDocShell();
   if (!docshell)
     return NS_ERROR_FAILURE;
 
-  mMidasCommandManager = do_GetInterface(docshell);
+  mMidasCommandManager = docshell->GetCommandManager();
   if (!mMidasCommandManager)
     return NS_ERROR_FAILURE;
 
   NS_ADDREF(*aCmdMgr = mMidasCommandManager);
 
   return NS_OK;
 }
 
--- a/dom/html/nsIConstraintValidation.cpp
+++ b/dom/html/nsIConstraintValidation.cpp
@@ -5,20 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIConstraintValidation.h"
 
 #include "nsAString.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/HTMLFieldSetElement.h"
+#include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/ValidityState.h"
 #include "nsIFormControl.h"
 #include "nsContentUtils.h"
 
+#include "nsIFormSubmitObserver.h"
+#include "nsIObserverService.h"
+
 const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsIConstraintValidation::nsIConstraintValidation()
   : mValidityBitField(0)
   // By default, all elements are subjects to constraint validation.
@@ -121,16 +125,81 @@ nsIConstraintValidation::CheckValidity(b
 {
   NS_ENSURE_ARG_POINTER(aValidity);
 
   *aValidity = CheckValidity();
 
   return NS_OK;
 }
 
+bool
+nsIConstraintValidation::ReportValidity()
+{
+  if (!IsCandidateForConstraintValidation() || IsValid()) {
+    return true;
+  }
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(this);
+  MOZ_ASSERT(content, "This class should be inherited by HTML elements only!");
+
+  bool defaultAction = true;
+  nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
+                                       NS_LITERAL_STRING("invalid"),
+                                       false, true, &defaultAction);
+  if (!defaultAction) {
+    return false;
+  }
+
+  nsCOMPtr<nsIObserverService> service =
+    mozilla::services::GetObserverService();
+  if (!service) {
+    NS_WARNING("No observer service available!");
+    return true;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> theEnum;
+  nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
+                                            getter_AddRefs(theEnum));
+
+  // Return true on error here because that's what we always did
+  NS_ENSURE_SUCCESS(rv, true);
+
+  bool hasObserver = false;
+  rv = theEnum->HasMoreElements(&hasObserver);
+
+  nsCOMPtr<nsIMutableArray> invalidElements =
+    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+  invalidElements->AppendElement(content, false);
+
+  NS_ENSURE_SUCCESS(rv, true);
+  nsCOMPtr<nsISupports> inst;
+  nsCOMPtr<nsIFormSubmitObserver> observer;
+  bool more = true;
+  while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
+    theEnum->GetNext(getter_AddRefs(inst));
+    observer = do_QueryInterface(inst);
+
+    if (observer) {
+      observer->NotifyInvalidSubmit(nullptr, invalidElements);
+    }
+  }
+
+  if (content->IsHTMLElement(nsGkAtoms::input) &&
+      nsContentUtils::IsFocusedContent(content)) {
+    HTMLInputElement* inputElement =
+    HTMLInputElement::FromContentOrNull(content);
+
+    inputElement->UpdateValidityUIBits(true);
+  }
+
+  dom::Element* element = content->AsElement();
+  element->UpdateState(true);
+  return false;
+}
+
 void
 nsIConstraintValidation::SetValidityState(ValidityStateType aState,
                                           bool aValue)
 {
   bool previousValidity = IsValid();
 
   if (aValue) {
     mValidityBitField |= aState;
--- a/dom/html/nsIConstraintValidation.h
+++ b/dom/html/nsIConstraintValidation.h
@@ -68,16 +68,17 @@ public:
                         bool aValue);
 
   // Web IDL binding methods
   bool WillValidate() const {
     return IsCandidateForConstraintValidation();
   }
   mozilla::dom::ValidityState* Validity();
   bool CheckValidity();
+  bool ReportValidity();
 
 protected:
 
   // You can't instantiate an object from that class.
   nsIConstraintValidation();
 
   nsresult GetValidity(nsIDOMValidityState** aValidity);
   nsresult CheckValidity(bool* aValidity);
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -80,20 +80,23 @@ skip-if = buildapp == 'mulet'
 [test_output_element.html]
 [test_pattern_attribute.html]
 [test_progress_element.html]
 [test_radio_in_label.html]
 [test_radio_radionodelist.html]
 [test_required_attribute.html]
 [test_restore_form_elements.html]
 [test_save_restore_radio_groups.html]
+[test_select_change_event.html]
+skip-if = android_version == '18'
 [test_select_selectedOptions.html]
 [test_select_validation.html]
 [test_set_range_text.html]
 [test_step_attribute.html]
 [test_stepup_stepdown.html]
 [test_textarea_attributes_reflection.html]
 [test_validation.html]
 skip-if = buildapp == 'b2g' # b2g(374 total, bug 901848, no keygen support) b2g-debug(374 total, bug 901848, no keygen support) b2g-desktop(374 total, bug 901848, no keygen support)
 [test_valueAsDate_pref.html]
 [test_valueasdate_attribute.html]
 [test_valueasnumber_attribute.html]
 [test_validation_not_in_doc.html]
+[test_reportValidation_preventDefault.html]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_reportValidation_preventDefault.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1088761
+-->
+<head>
+  <title>Test for Bug 1088761</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    input, textarea, fieldset, button, select, keygen, output, object { background-color: rgb(0,0,0) !important; }
+    :valid   { background-color: rgb(0,255,0) !important; }
+    :invalid { background-color: rgb(255,0,0) !important; }
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1088761">Mozilla Bug 1088761</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <fieldset id='f' oninvalid="invalidEventHandler(event, true);"></fieldset>
+  <input id='i' required oninvalid="invalidEventHandler(event, true);">
+  <button id='b' oninvalid="invalidEventHandler(event, true);"></button>
+  <select id='s' required oninvalid="invalidEventHandler(event, true);"></select>
+  <textarea id='t' required oninvalid="invalidEventHandler(event, true);"></textarea>
+  <output id='o' oninvalid="invalidEventHandler(event, true);"></output>
+  <keygen id='k' oninvalid="invalidEventHandler(event, true);"></keygen>
+  <object id='obj' oninvalid="invalidEventHandler(event, true);"></object>
+</div>
+<div id="content2" style="display: none">
+  <fieldset id='f2' oninvalid="invalidEventHandler(event, false);"></fieldset>
+  <input id='i2' required oninvalid="invalidEventHandler(event, false);">
+  <button id='b2' oninvalid="invalidEventHandler(event, false);"></button>
+  <select id='s2' required oninvalid="invalidEventHandler(event, false);"></select>
+  <textarea id='t2' required oninvalid="invalidEventHandler(event, false);"></textarea>
+  <output id='o2' oninvalid="invalidEventHandler(event, false);"></output>
+  <keygen id='k2' oninvalid="invalidEventHandler(event, false);"></keygen>
+  <object id='obj2' oninvalid="invalidEventHandler(event, false);"></object>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1088761 **/
+
+var gInvalid = false;
+
+function invalidEventHandler(aEvent, isPreventDefault)
+{
+  if (isPreventDefault) {
+      aEvent.preventDefault();
+  }
+
+  is(aEvent.type, "invalid", "Invalid event type should be invalid");
+  ok(!aEvent.bubbles, "Invalid event should not bubble");
+  ok(aEvent.cancelable, "Invalid event should be cancelable");
+  gInvalid = true;
+}
+
+function checkReportValidityForInvalid(element)
+{
+  gInvalid = false;
+  ok(!element.reportValidity(), "reportValidity() should return false when the element is not valid");
+  ok(gInvalid, "Invalid event should have been handled");
+}
+
+function checkReportValidityForValid(element)
+{
+  gInvalid = false;
+  ok(element.reportValidity(), "reportValidity() should return true when the element is valid");
+  ok(!gInvalid, "Invalid event shouldn't have been handled");
+}
+
+checkReportValidityForInvalid(document.getElementById('i'));
+checkReportValidityForInvalid(document.getElementById('s'));
+checkReportValidityForInvalid(document.getElementById('t'));
+
+checkReportValidityForInvalid(document.getElementById('i2'));
+checkReportValidityForInvalid(document.getElementById('s2'));
+checkReportValidityForInvalid(document.getElementById('t2'));
+
+checkReportValidityForValid(document.getElementById('o'));
+checkReportValidityForValid(document.getElementById('k'));
+checkReportValidityForValid(document.getElementById('obj'));
+checkReportValidityForValid(document.getElementById('f'));
+
+checkReportValidityForValid(document.getElementById('o2'));
+checkReportValidityForValid(document.getElementById('k2'));
+checkReportValidityForValid(document.getElementById('obj2'));
+checkReportValidityForValid(document.getElementById('f2'));
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_select_change_event.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265968
+-->
+<head>
+  <title>Test for Bug 1265968</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265968">Mozilla Bug 1265968</a>
+<p id="display"></p>
+<div id="content">
+  <select id="select" onchange="++selectChange;">
+    <option>one</option>
+    <option>two</option>
+    <option>three</option>
+    <option>four</option>
+    <option>five</option>
+  </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  var select = document.getElementById("select");
+  var selectChange = 0;
+  var expectedChange = 0;
+
+  select.focus();
+  for (var i = 1; i < select.length; i++) {
+    synthesizeKey("VK_DOWN", {});
+    is(select.options[i].selected, true, "Option should be selected");
+    is(selectChange, ++expectedChange, "Down key should fire change event.");
+  }
+
+  // We are at the end of the list, going down should not fire change event.
+  synthesizeKey("VK_DOWN", {});
+  is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+  for (var i = select.length - 2; i >= 0; i--) {
+    synthesizeKey("VK_UP", {});
+    is(select.options[i].selected, true, "Option should be selected");
+    is(selectChange, ++expectedChange, "Up key should fire change event.");
+  }
+
+  // We are at the top of the list, going up should not fire change event.
+  synthesizeKey("VK_UP", {});
+  is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -22223,17 +22223,17 @@ DeleteDatabaseOp::NoteDatabaseClosed(Dat
 
 void
 DeleteDatabaseOp::SendBlockedNotification()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
 
   if (!IsActorDestroyed()) {
-    Unused << SendBlocked(0);
+    Unused << SendBlocked(mPreviousVersion);
   }
 }
 
 void
 DeleteDatabaseOp::SendResults()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::SendingResults);
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -33,16 +33,18 @@ support-files =
   unit/test_create_index_with_integer_keys.js
   unit/test_create_locale_aware_index.js
   unit/test_create_objectStore.js
   unit/test_cursor_mutation.js
   unit/test_cursor_update_updates_indexes.js
   unit/test_cursors.js
   unit/test_deleteDatabase.js
   unit/test_deleteDatabase_interactions.js
+  unit/test_deleteDatabase_onblocked.js
+  unit/test_deleteDatabase_onblocked_duringVersionChange.js
   unit/test_event_source.js
   unit/test_filehandle_append_read_data.js
   unit/test_getAll.js
   unit/test_globalObjects_ipc.js
   unit/test_globalObjects_other.js
   unit/test_globalObjects_xpc.js
   unit/test_global_data.js
   unit/test_index_empty_keyPath.js
@@ -166,16 +168,20 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_cursor_update_updates_indexes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_cursors.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase_interactions.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
+[test_deleteDatabase_onblocked.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
+[test_deleteDatabase_onblocked_duringVersionChange.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_error_events_abort_transactions.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_event_propagation.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_event_source.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_exceptions_in_events.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_deleteDatabase_onblocked.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Onblocked Test During Deleting Database</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_onblocked.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_deleteDatabase_onblocked_duringVersionChange.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Onblocked Test During Version Change Transaction</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_onblocked_duringVersionChange.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js
@@ -0,0 +1,83 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+  const dbVersion = 10;
+
+  let openRequest = indexedDB.open(name, dbVersion);
+  openRequest.onerror = errorHandler;
+  openRequest.onblocked = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  let db = event.target.result;
+  db.onversionchange = errorHandler;
+  db.createObjectStore("stuff");
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  db.onversionchange = grabEventAndContinueHandler;
+  let deletingRequest = indexedDB.deleteDatabase(name);
+  deletingRequest.onerror = errorHandler;
+  deletingRequest.onsuccess = errorHandler;
+  deletingRequest.onblocked = errorHandler;
+
+  event = yield undefined;
+
+  is(event.type, "versionchange", "Expect an versionchange event");
+  is(event.target, db, "Event has right target");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+  is(event.oldVersion, dbVersion, "Correct old version");
+  is(event.newVersion, null, "Correct new version");
+
+  deletingRequest.onblocked = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "blocked", "Expect an blocked event");
+  is(event.target, deletingRequest, "Event has right target");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+  is(event.oldVersion, dbVersion, "Correct old version");
+  is(event.newVersion, null, "Correct new version");
+
+  deletingRequest.onsuccess = grabEventAndContinueHandler;
+  db.close();
+
+  event = yield undefined;
+
+  is(event.type, "success", "expect a success event");
+  is(event.target, deletingRequest, "event has right target");
+  is(event.target.result, undefined, "event should have no result");
+
+  openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+  db = event.target.result;
+  is(db.version, 1, "DB has proper version");
+  is(db.objectStoreNames.length, 0, "DB should have no object stores");
+
+  db.close();
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js
@@ -0,0 +1,84 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+  const dbVersion = 10;
+
+  let openRequest = indexedDB.open(name, dbVersion);
+  openRequest.onerror = errorHandler;
+  openRequest.onblocked = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  let db = event.target.result;
+  db.onversionchange = errorHandler;
+  db.createObjectStore("stuff");
+
+  let deletingRequest = indexedDB.deleteDatabase(name);
+  deletingRequest.onerror = errorHandler;
+  deletingRequest.onsuccess = errorHandler;
+  deletingRequest.onblocked = errorHandler;
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  db.onversionchange = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "versionchange", "Expect an versionchange event");
+  is(event.target, db, "Event has right target");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+  is(event.oldVersion, dbVersion, "Correct old version");
+  is(event.newVersion, null, "Correct new version");
+
+  deletingRequest.onblocked = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "blocked", "Expect an blocked event");
+  is(event.target, deletingRequest, "Event has right target");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+  is(event.oldVersion, dbVersion, "Correct old version");
+  is(event.newVersion, null, "Correct new version");
+
+  deletingRequest.onsuccess = grabEventAndContinueHandler;
+  db.close();
+
+  event = yield undefined;
+
+  is(event.type, "success", "expect a success event");
+  is(event.target, deletingRequest, "event has right target");
+  is(event.target.result, undefined, "event should have no result");
+
+  openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+  db = event.target.result;
+  is(db.version, 1, "DB has proper version");
+  is(db.objectStoreNames.length, 0, "DB should have no object stores");
+
+  db.close();
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-shared.ini
+++ b/dom/indexedDB/test/unit/xpcshell-shared.ini
@@ -17,16 +17,18 @@
 skip-if = toolkit == 'android' # bug 864843
 [test_create_objectStore.js]
 [test_cursor_cycle.js]
 [test_cursor_mutation.js]
 [test_cursor_update_updates_indexes.js]
 [test_cursors.js]
 [test_deleteDatabase.js]
 [test_deleteDatabase_interactions.js]
+[test_deleteDatabase_onblocked.js]
+[test_deleteDatabase_onblocked_duringVersionChange.js]
 [test_event_source.js]
 [test_getAll.js]
 [test_globalObjects_other.js]
 skip-if = toolkit == 'android' # bug 1079278
 [test_globalObjects_xpc.js]
 [test_global_data.js]
 [test_index_empty_keyPath.js]
 [test_index_getAll.js]
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -218,16 +218,23 @@ interface nsIDOMWindowUtils : nsISupport
    * This can be used to implement a non-reflowing scale-zoom, e.g.
    * for pinch-zoom on mobile platforms.
    *
    * The caller of this method must have chrome privileges.
    */
   void setResolutionAndScaleTo(in float aResolution);
 
   /**
+   * Set a resolution on the presShell which is the "restored" from history.
+   * This resolution should be used when painting for the first time. Calling
+   * this too late may have no effect.
+   */
+  void setRestoreResolution(in float aResolution);
+
+  /**
    * Whether the resolution has been set by the user.
    * This gives a way to check whether the provided resolution is the default
    * value or restored from a previous session.
    *
    * Can only be accessed with chrome privileges.
    */
   readonly attribute boolean isResolutionSet;
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -464,16 +464,23 @@ TabParent::DestroyInternal()
   // If this fails, it's most likely due to a content-process crash,
   // and auto-cleanup will kick in.  Otherwise, the child side will
   // destroy itself and send back __delete__().
   Unused << SendDestroy();
 
   if (RenderFrameParent* frame = GetRenderFrame()) {
     RemoveTabParentFromTable(frame->GetLayersId());
     frame->Destroy();
+
+    // Notify our layer tree update observer that we're going away. It's
+    // possible that we race with a notification and there can be an
+    // LayerTreeUpdateRunnable on the main thread's event queue with a pointer
+    // to us. However, our actual destruction won't be until yet another event
+    // *after* that one is processed, so this should be safe.
+    mLayerUpdateObserver->TabParentDestroyed();
   }
 
   // Let all PluginWidgets know we are tearing down. Prevents
   // these objects from sending async events after the child side
   // is shut down.
   const ManagedContainer<PPluginWidgetParent>& kids =
     ManagedPPluginWidgetParent();
   for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) {
@@ -2257,16 +2264,20 @@ TabParent::GetTabIdFrom(nsIDocShell *doc
     return static_cast<TabChild*>(tabChild.get())->GetTabId();
   }
   return TabId(0);
 }
 
 RenderFrameParent*
 TabParent::GetRenderFrame()
 {
+  if (!mLayerUpdateObserver) {
+    mLayerUpdateObserver = new LayerTreeUpdateObserver(this);
+  }
+
   PRenderFrameParent* p = LoneManagedOrNullAsserts(ManagedPRenderFrameParent());
   return static_cast<RenderFrameParent*>(p);
 }
 
 bool
 TabParent::RecvRequestIMEToCommitComposition(const bool& aCancel,
                                              bool* aIsCommitted,
                                              nsString* aCommittedString)
@@ -2912,69 +2923,72 @@ TabParent::NavigateByKey(bool aForward, 
 {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
   return NS_OK;
 }
 
 class LayerTreeUpdateRunnable final
   : public mozilla::Runnable
 {
-  uint64_t mLayersId;
+  RefPtr<LayerTreeUpdateObserver> mUpdateObserver;
   bool mActive;
 
 public:
-  explicit LayerTreeUpdateRunnable(uint64_t aLayersId, bool aActive)
-    : mLayersId(aLayersId), mActive(aActive) {}
+  explicit LayerTreeUpdateRunnable(LayerTreeUpdateObserver* aObs, bool aActive)
+    : mUpdateObserver(aObs), mActive(aActive)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
 
 private:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
-    TabParent* tabParent = TabParent::GetTabParentFromLayersId(mLayersId);
-    if (tabParent) {
+    if (RefPtr<TabParent> tabParent = mUpdateObserver->GetTabParent()) {
       tabParent->LayerTreeUpdate(mActive);
     }
     return NS_OK;
   }
 };
 
-// This observer runs on the compositor thread, so we dispatch a runnable to the
-// main thread to actually dispatch the event.
-class LayerTreeUpdateObserver : public CompositorUpdateObserver
+void
+LayerTreeUpdateObserver::ObserveUpdate(uint64_t aLayersId, bool aActive)
 {
-  virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) {
-    RefPtr<LayerTreeUpdateRunnable> runnable = new LayerTreeUpdateRunnable(aLayersId, aActive);
-    NS_DispatchToMainThread(runnable);
-  }
-};
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  RefPtr<LayerTreeUpdateRunnable> runnable =
+    new LayerTreeUpdateRunnable(this, aActive);
+  NS_DispatchToMainThread(runnable);
+}
+
 
 bool
 TabParent::RequestNotifyLayerTreeReady()
 {
   RenderFrameParent* frame = GetRenderFrame();
   if (!frame || !frame->IsInitted()) {
     mNeedLayerTreeReadyNotification = true;
   } else {
     CompositorBridgeParent::RequestNotifyLayerTreeReady(
       frame->GetLayersId(),
-      new LayerTreeUpdateObserver());
+      mLayerUpdateObserver);
   }
   return true;
 }
 
 bool
 TabParent::RequestNotifyLayerTreeCleared()
 {
   RenderFrameParent* frame = GetRenderFrame();
   if (!frame) {
     return false;
   }
 
   CompositorBridgeParent::RequestNotifyLayerTreeCleared(
     frame->GetLayersId(),
-    new LayerTreeUpdateObserver());
+    mLayerUpdateObserver);
   return true;
 }
 
 bool
 TabParent::LayerTreeUpdate(bool aActive)
 {
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
@@ -3003,19 +3017,27 @@ TabParent::SwapLayerTreeObservers(TabPar
   }
 
   RenderFrameParent* rfp = GetRenderFrame();
   RenderFrameParent* otherRfp = aOther->GetRenderFrame();
   if (!rfp || !otherRfp) {
     return;
   }
 
+  // The swap that happens for the observers in CompositorBridgeParent has to
+  // happen in a lock so that an update being processed on the compositor thread
+  // can't grab the layer update observer for the wrong tab parent.
   CompositorBridgeParent::SwapLayerTreeObservers(
     rfp->GetLayersId(),
     otherRfp->GetLayersId());
+
+  // No need for a lock, destruction can only happen on the main thread and we
+  // only read mLayerUpdateObserver::mTabParent on the main thread.
+  Swap(mLayerUpdateObserver, aOther->mLayerUpdateObserver);
+  mLayerUpdateObserver->SwapTabParent(aOther->mLayerUpdateObserver);
 }
 
 bool
 TabParent::RecvRemotePaintIsReady()
 {
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
     NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -12,17 +12,19 @@
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Move.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
 #include "nsIKeyEventInPluginCallback.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIWebBrowserPersistable.h"
@@ -73,16 +75,49 @@ class ClonedMessageData;
 class nsIContentParent;
 class Element;
 class DataTransfer;
 
 namespace ipc {
 class StructuredCloneData;
 } // ipc namespace
 
+// This observer runs on the compositor thread, so we dispatch a runnable to the
+// main thread to actually dispatch the event.
+class LayerTreeUpdateObserver : public layers::CompositorUpdateObserver
+{
+public:
+  explicit LayerTreeUpdateObserver(TabParent* aTabParent)
+    : mTabParent(aTabParent)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) override;
+
+  virtual void SwapTabParent(LayerTreeUpdateObserver* aOther) {
+    MOZ_ASSERT(NS_IsMainThread());
+    Swap(mTabParent, aOther->mTabParent);
+  }
+
+  void TabParentDestroyed() {
+    MOZ_ASSERT(NS_IsMainThread());
+    mTabParent = nullptr;
+  }
+
+  TabParent* GetTabParent() {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mTabParent;
+  }
+
+private:
+  // NB: Should never be touched off the main thread!
+  TabParent* mTabParent;
+};
+
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsIKeyEventInPluginCallback
                       , public nsSupportsWeakReference
                       , public TabContext
@@ -748,16 +783,18 @@ private:
   // to dispatch events.
   typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable;
   static LayerToTabParentTable* sLayerToTabParentTable;
 
   static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
 
   static void RemoveTabParentFromTable(uint64_t aLayersId);
 
+  RefPtr<LayerTreeUpdateObserver> mLayerUpdateObserver;
+
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
   AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -70,17 +70,16 @@ ChannelMediaResource::ChannelMediaResour
                                            const nsACString& aContentType)
   : BaseMediaResource(aCallback, aChannel, aURI, aContentType),
     mOffset(0),
     mReopenOnError(false),
     mIgnoreClose(false),
     mCacheStream(this),
     mLock("ChannelMediaResource.mLock"),
     mIgnoreResume(false),
-    mIsTransportSeekable(true),
     mSuspendAgent(mChannel)
 {
 }
 
 ChannelMediaResource::~ChannelMediaResource()
 {
   if (mListener) {
     // Kill its reference to us since we're going away
@@ -292,17 +291,16 @@ ChannelMediaResource::OnStartRequest(nsI
     }
 
     mCallback->SetInfinite(!dataIsBounded);
   }
   mCacheStream.SetTransportSeekable(seekable);
 
   {
     MutexAutoLock lock(mLock);
-    mIsTransportSeekable = seekable;
     mChannelStatistics->Start();
   }
 
   mReopenOnError = false;
   mIgnoreClose = false;
 
   mSuspendAgent.UpdateSuspendedStatusIfNeeded();
 
@@ -310,18 +308,17 @@ ChannelMediaResource::OnStartRequest(nsI
   owner->DownloadProgressed();
 
   return NS_OK;
 }
 
 bool
 ChannelMediaResource::IsTransportSeekable()
 {
-  MutexAutoLock lock(mLock);
-  return mIsTransportSeekable;
+  return mCacheStream.IsTransportSeekable();
 }
 
 nsresult
 ChannelMediaResource::ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
                                               int64_t& aRangeStart,
                                               int64_t& aRangeEnd,
                                               int64_t& aRangeTotal)
 {
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -695,20 +695,16 @@ protected:
   Mutex               mLock;
   RefPtr<MediaChannelStatistics> mChannelStatistics;
 
   // True if we couldn't suspend the stream and we therefore don't want
   // to resume later. This is usually due to the channel not being in the
   // isPending state at the time of the suspend request.
   bool mIgnoreResume;
 
-  // True if the stream can seek into unbuffered ranged, i.e. if the
-  // connection supports byte range requests.
-  bool mIsTransportSeekable;
-
   ChannelSuspendAgent mSuspendAgent;
 };
 
 /**
  * RAII class that handles pinning and unpinning for MediaResource and derived.
  * This should be used when making calculations that involve potentially-cached
  * MediaResource data, so that the state of the world can't change out from under
  * us.
--- a/dom/media/mediasource/SourceBufferTask.h
+++ b/dom/media/mediasource/SourceBufferTask.h
@@ -18,17 +18,18 @@ namespace mozilla {
 class SourceBufferTask {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SourceBufferTask);
   enum class Type  {
     AppendBuffer,
     Abort,
     Reset,
     RangeRemoval,
-    EvictData
+    EvictData,
+    Detach
   };
 
   typedef Pair<bool, SourceBufferAttributes> AppendBufferResult;
   typedef MozPromise<AppendBufferResult, nsresult, /* IsExclusive = */ true> AppendPromise;
   typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> RangeRemovalPromise;
 
   virtual Type GetType() const = 0;
 
@@ -93,11 +94,17 @@ public:
 
   static const Type sType = Type::EvictData;
   Type GetType() const override { return Type::EvictData; }
 
   media::TimeUnit mPlaybackTime;
   int64_t mSizeToEvict;
 };
 
+class DetachTask : public SourceBufferTask {
+public:
+  static const Type sType = Type::Detach;
+  Type GetType() const override { return Type::Detach; }
+};
+
 } // end mozilla namespace
 
 #endif
\ No newline at end of file
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -94,30 +94,28 @@ TrackBuffersManager::TrackBuffersManager
   , mNewMediaSegmentStarted(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
   , mEnded(false)
-  , mDetached(false)
   , mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
                                                  100 * 1024 * 1024))
   , mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
                                                  30 * 1024 * 1024))
   , mEvictionOccurred(false)
   , mMonitor("TrackBuffersManager")
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
 }
 
 TrackBuffersManager::~TrackBuffersManager()
 {
-  CancelAllTasks();
   ShutdownDemuxers();
 }
 
 RefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::AppendData(MediaByteBuffer* aData,
                                 const SourceBufferAttributes& aAttributes)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -133,149 +131,118 @@ TrackBuffersManager::AppendData(MediaByt
 }
 
 RefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::DoAppendData(RefPtr<MediaByteBuffer> aData,
                                   SourceBufferAttributes aAttributes)
 {
   RefPtr<AppendBufferTask> task = new AppendBufferTask(aData, aAttributes);
   RefPtr<AppendPromise> p = task->mPromise.Ensure(__func__);
-  mQueue.Push(task);
-
-  ProcessTasks();
+  QueueTask(task);
 
   return p;
 }
 
 void
+TrackBuffersManager::QueueTask(SourceBufferTask* aTask)
+{
+  if (!OnTaskQueue()) {
+    GetTaskQueue()->Dispatch(NewRunnableMethod<RefPtr<SourceBufferTask>>(
+      this, &TrackBuffersManager::QueueTask, aTask));
+    return;
+  }
+  MOZ_ASSERT(OnTaskQueue());
+  mQueue.Push(aTask);
+  ProcessTasks();
+}
+
+void
 TrackBuffersManager::ProcessTasks()
 {
+  MOZ_ASSERT(OnTaskQueue());
   typedef SourceBufferTask::Type Type;
 
-  if (mDetached) {
+  if (mCurrentTask) {
+    // Already have a task pending. ProcessTask will be scheduled once the
+    // current task complete.
+    return;
+  }
+  RefPtr<SourceBufferTask> task = mQueue.Pop();
+  if (!task) {
+    // nothing to do.
     return;
   }
-
-  if (OnTaskQueue()) {
-    if (mCurrentTask) {
-      // Already have a task pending. ProcessTask will be scheduled once the
-      // current task complete.
-      return;
-    }
-    RefPtr<SourceBufferTask> task = mQueue.Pop();
-    if (!task) {
-      // nothing to do.
-      return;
+  switch (task->GetType()) {
+    case Type::AppendBuffer:
+      mCurrentTask = task;
+      if (!mInputBuffer) {
+        mInputBuffer = task->As<AppendBufferTask>()->mBuffer;
+      } else if (!mInputBuffer->AppendElements(*task->As<AppendBufferTask>()->mBuffer, fallible)) {
+        RejectAppend(NS_ERROR_OUT_OF_MEMORY, __func__);
+        return;
+      }
+      mSourceBufferAttributes =
+        MakeUnique<SourceBufferAttributes>(task->As<AppendBufferTask>()->mAttributes);
+      mAppendWindow =
+        TimeInterval(TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowStart()),
+                     TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowEnd()));
+      ScheduleSegmentParserLoop();
+      break;
+    case Type::RangeRemoval:
+    {
+      bool rv = CodedFrameRemoval(task->As<RangeRemovalTask>()->mRange);
+      task->As<RangeRemovalTask>()->mPromise.Resolve(rv, __func__);
+      break;
     }
-    switch (task->GetType()) {
-      case Type::AppendBuffer:
-        mCurrentTask = task;
-        if (!mInputBuffer) {
-          mInputBuffer = task->As<AppendBufferTask>()->mBuffer;
-        } else if (!mInputBuffer->AppendElements(*task->As<AppendBufferTask>()->mBuffer, fallible)) {
-          RejectAppend(NS_ERROR_OUT_OF_MEMORY, __func__);
-          return;
-        }
-        mSourceBufferAttributes =
-          MakeUnique<SourceBufferAttributes>(task->As<AppendBufferTask>()->mAttributes);
-        mAppendWindow =
-          TimeInterval(TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowStart()),
-                       TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowEnd()));
-        ScheduleSegmentParserLoop();
-        break;
-      case Type::RangeRemoval:
-      {
-        bool rv = CodedFrameRemoval(task->As<RangeRemovalTask>()->mRange);
-        task->As<RangeRemovalTask>()->mPromise.Resolve(rv, __func__);
-        break;
-      }
-      case Type::EvictData:
-        DoEvictData(task->As<EvictDataTask>()->mPlaybackTime,
-                    task->As<EvictDataTask>()->mSizeToEvict);
-        break;
-      case Type::Abort:
-        // not handled yet, and probably never.
-        break;
-      case Type::Reset:
-        CompleteResetParserState();
-        break;
-      default:
-        NS_WARNING("Invalid Task");
-    }
+    case Type::EvictData:
+      DoEvictData(task->As<EvictDataTask>()->mPlaybackTime,
+                  task->As<EvictDataTask>()->mSizeToEvict);
+      break;
+    case Type::Abort:
+      // not handled yet, and probably never.
+      break;
+    case Type::Reset:
+      CompleteResetParserState();
+      break;
+    case Type::Detach:
+      mTaskQueue = nullptr;
+      MOZ_DIAGNOSTIC_ASSERT(mQueue.Length() == 0,
+                            "Detach task must be the last");
+      return;
+    default:
+      NS_WARNING("Invalid Task");
   }
   GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::ProcessTasks));
 }
 
-// A PromiseHolder will assert upon destruction if it has a pending promise
-// that hasn't been completed. It is possible that a task didn't get processed
-// due to the owning SourceBuffer having shutdown.
-// We resolve/reject all pending promises and remove all pending tasks from the
-// queue.
-void
-TrackBuffersManager::CancelAllTasks()
-{
-  typedef SourceBufferTask::Type Type;
-  MOZ_DIAGNOSTIC_ASSERT(mDetached);
-
-  if (mCurrentTask) {
-    mQueue.Push(mCurrentTask);
-    mCurrentTask = nullptr;
-  }
-
-  RefPtr<SourceBufferTask> task;
-  while ((task = mQueue.Pop())) {
-    switch (task->GetType()) {
-      case Type::AppendBuffer:
-        task->As<AppendBufferTask>()->mPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
-        break;
-      case Type::RangeRemoval:
-        task->As<RangeRemovalTask>()->mPromise.ResolveIfExists(false, __func__);
-        break;
-      case Type::EvictData:
-        break;
-      case Type::Abort:
-        // not handled yet, and probably never.
-        break;
-      case Type::Reset:
-        break;
-      default:
-        NS_WARNING("Invalid Task");
-    }
-  }
-}
-
 // The MSE spec requires that we abort the current SegmentParserLoop
 // which is then followed by a call to ResetParserState.
 // However due to our asynchronous design this causes inherent difficulties.
 // As the spec behaviour is non deterministic anyway, we instead process all
 // pending frames found in the input buffer.
 void
 TrackBuffersManager::AbortAppendData()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 
-  RefPtr<AbortTask> task = new AbortTask();
-  mQueue.Push(task);
-  ProcessTasks();
+  QueueTask(new AbortTask());
 }
 
 void
 TrackBuffersManager::ResetParserState(SourceBufferAttributes& aAttributes)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 
   // Spec states:
   // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
   // However, we will wait until all coded frames have been processed regardless
   // of the value of append state.
-  RefPtr<ResetTask> task = new ResetTask();
-  mQueue.Push(task);
-  ProcessTasks();
+  QueueTask(new ResetTask());
 
   // ResetParserState has some synchronous steps that much be performed now.
   // The remaining steps will be performed once the ResetTask gets executed.
 
   // 6. If the mode attribute equals "sequence", then set the group start timestamp to the group end timestamp
   if (aAttributes.GetAppendMode() == SourceBufferAppendMode::Sequence) {
     aAttributes.SetGroupStartTimestamp(aAttributes.GetGroupEndTimestamp());
   }
@@ -319,19 +286,17 @@ TrackBuffersManager::EvictData(const Tim
   }
 
   if (mBufferFull && mEvictionOccurred) {
     return EvictDataResult::BUFFER_FULL;
   }
 
   MSE_DEBUG("Reaching our size limit, schedule eviction of %lld bytes", toEvict);
 
-  RefPtr<EvictDataTask> task = new EvictDataTask(aPlaybackTime, toEvict);
-  mQueue.Push(task);
-  ProcessTasks();
+  QueueTask(new EvictDataTask(aPlaybackTime, toEvict));
 
   return EvictDataResult::NO_DATA_EVICTED;
 }
 
 TimeIntervals
 TrackBuffersManager::Buffered()
 {
   MSE_DEBUG("");
@@ -379,17 +344,17 @@ TrackBuffersManager::Ended()
   mEnded = true;
 }
 
 void
 TrackBuffersManager::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
-  mDetached = true;
+  QueueTask(new DetachTask());
 }
 
 void
 TrackBuffersManager::CompleteResetParserState()
 {
   MOZ_ASSERT(OnTaskQueue());
   MSE_DEBUG("");
 
@@ -517,18 +482,18 @@ TrackBuffersManager::DoEvictData(const T
 
 RefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::CodedFrameRemovalWithPromise(TimeInterval aInterval)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   RefPtr<RangeRemovalTask> task = new RangeRemovalTask(aInterval);
   RefPtr<RangeRemovalPromise> p = task->mPromise.Ensure(__func__);
-  mQueue.Push(task);
-  ProcessTasks();
+  QueueTask(task);
+
   return p;
 }
 
 bool
 TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
 {
   MOZ_ASSERT(OnTaskQueue());
   MSE_DEBUG("From %.2fs to %.2f",
@@ -748,54 +713,43 @@ TrackBuffersManager::SegmentParserLoop()
     }
   }
 }
 
 void
 TrackBuffersManager::NeedMoreData()
 {
   MSE_DEBUG("");
-  if (mDetached) {
-    // We've been detached.
-    return;
-  }
   MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
   MOZ_DIAGNOSTIC_ASSERT(mSourceBufferAttributes);
 
   mCurrentTask->As<AppendBufferTask>()->mPromise.Resolve(
     SourceBufferTask::AppendBufferResult(mActiveTrack,
                                          *mSourceBufferAttributes),
                                          __func__);
   mSourceBufferAttributes = nullptr;
   mCurrentTask = nullptr;
   ProcessTasks();
 }
 
 void
 TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
 {
   MSE_DEBUG("rv=%d", aRejectValue);
-  if (mDetached) {
-    // We've been detached.
-    return;
-  }
   MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
 
   mCurrentTask->As<AppendBufferTask>()->mPromise.Reject(aRejectValue, __func__);
   mSourceBufferAttributes = nullptr;
   mCurrentTask = nullptr;
   ProcessTasks();
 }
 
 void
 TrackBuffersManager::ScheduleSegmentParserLoop()
 {
-  if (mDetached) {
-    return;
-  }
   GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop));
 }
 
 void
 TrackBuffersManager::ShutdownDemuxers()
 {
   if (mVideoTracks.mDemuxer) {
     mVideoTracks.mDemuxer->BreakCycles();
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -30,16 +30,20 @@ class MediaRawData;
 class MediaSourceDemuxer;
 class SourceBufferResource;
 
 class SourceBufferTaskQueue {
 public:
   SourceBufferTaskQueue()
   : mMonitor("SourceBufferTaskQueue")
   {}
+  ~SourceBufferTaskQueue()
+  {
+    MOZ_ASSERT(mQueue.IsEmpty(), "All tasks must have been processed");
+  }
 
   void Push(SourceBufferTask* aTask)
   {
     MonitorAutoLock mon(mMonitor);
     mQueue.AppendElement(aTask);
   }
 
   already_AddRefed<SourceBufferTask> Pop()
@@ -373,37 +377,34 @@ private:
   bool OnTaskQueue()
   {
     return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn();
   }
   RefPtr<AutoTaskQueue> mTaskQueue;
 
   // SourceBuffer Queues and running context.
   SourceBufferTaskQueue mQueue;
+  void QueueTask(SourceBufferTask* aTask);
   void ProcessTasks();
-  void CancelAllTasks();
   // Set if the TrackBuffersManager is currently processing a task.
   // At this stage, this task is always a AppendBufferTask.
   RefPtr<SourceBufferTask> mCurrentTask;
   // Current SourceBuffer state for ongoing task.
   // Its content is returned to the SourceBuffer once the AppendBufferTask has
   // completed.
   UniquePtr<SourceBufferAttributes> mSourceBufferAttributes;
   // The current sourcebuffer append window. It's content is equivalent to
   // mSourceBufferAttributes.mAppendWindowStart/End
   media::TimeInterval mAppendWindow;
 
   // Strong references to external objects.
   nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
 
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
-  // Set to true if the parent SourceBuffer has shutdown.
-  // We will not reschedule or process new task once mDetached is set.
-  Atomic<bool> mDetached;
 
   // Global size of this source buffer content.
   Atomic<int64_t> mSizeSourceBuffer;
   const int64_t mVideoEvictionThreshold;
   const int64_t mAudioEvictionThreshold;
   Atomic<bool> mEvictionOccurred;
 
   // Monitor to protect following objects accessed across multipple threads.
--- a/dom/media/platforms/agnostic/WAVDecoder.cpp
+++ b/dom/media/platforms/agnostic/WAVDecoder.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WAVDecoder.h"
 #include "AudioSampleFormat.h"
 #include "nsAutoPtr.h"
+#include "mozilla/SyncRunnable.h"
 
 using mp4_demuxer::ByteReader;
 
 namespace mozilla {
 
 int16_t
 DecodeALawSample(uint8_t aValue)
 {
@@ -41,21 +42,22 @@ DecodeULawSample(uint8_t aValue)
   int8_t sign = (aValue & 0x80) ? -1 : 1;
   uint8_t exponent = (aValue & 0x70) >> 4;
   uint8_t mantissa = aValue & 0x0F;
   int16_t sample = (33 + 2 * mantissa) * (2 << (exponent + 1)) - 33;
   return sign * sample;
 }
 
 WaveDataDecoder::WaveDataDecoder(const AudioInfo& aConfig,
-                                 FlushableTaskQueue* aTaskQueue,
+                                 TaskQueue* aTaskQueue,
                                  MediaDataDecoderCallback* aCallback)
   : mInfo(aConfig)
   , mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
+  , mIsFlushing(false)
   , mFrames(0)
 {
 }
 
 nsresult
 WaveDataDecoder::Shutdown()
 {
   return NS_OK;
@@ -65,36 +67,42 @@ RefPtr<MediaDataDecoder::InitPromise>
 WaveDataDecoder::Init()
 {
   return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
 }
 
 nsresult
 WaveDataDecoder::Input(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
   mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
-                         this, &WaveDataDecoder::Decode,
-                         RefPtr<MediaRawData>(aSample)));
+                       this, &WaveDataDecoder::ProcessDecode, aSample));
 
   return NS_OK;
 }
 
 void
-WaveDataDecoder::Decode(MediaRawData* aSample)
+WaveDataDecoder::ProcessDecode(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  if (mIsFlushing) {
+    return;
+  }
   if (!DoDecode(aSample)) {
     mCallback->Error();
   } else if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 bool
 WaveDataDecoder::DoDecode(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   size_t aLength = aSample->Size();
   ByteReader aReader = ByteReader(aSample->Data(), aLength);
   int64_t aOffset = aSample->mOffset;
   uint64_t aTstampUsecs = aSample->mTime;
 
   int32_t frames = aLength * 8 / mInfo.mBitDepth / mInfo.mChannels;
 
   AlignedAudioBuffer buffer(frames * mInfo.mChannels);
@@ -143,33 +151,40 @@ WaveDataDecoder::DoDecode(MediaRawData* 
                                   mInfo.mChannels,
                                   mInfo.mRate));
   mFrames += frames;
 
   return true;
 }
 
 void
-WaveDataDecoder::DoDrain()
+WaveDataDecoder::ProcessDrain()
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   mCallback->DrainComplete();
 }
 
 nsresult
 WaveDataDecoder::Drain()
 {
-  mTaskQueue->Dispatch(NewRunnableMethod(this, &WaveDataDecoder::DoDrain));
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  mTaskQueue->Dispatch(NewRunnableMethod(this, &WaveDataDecoder::ProcessDrain));
   return NS_OK;
 }
 
 nsresult
 WaveDataDecoder::Flush()
 {
-  mTaskQueue->Flush();
-  mFrames = 0;
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  mIsFlushing = true;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
+    mFrames = 0;
+  });
+  SyncRunnable::DispatchToThread(mTaskQueue, r);
+  mIsFlushing = false;
   return NS_OK;
 }
 
 /* static */
 bool
 WaveDataDecoder::IsWave(const nsACString& aMimeType)
 {
   // Some WebAudio uses "audio/x-wav",
--- a/dom/media/platforms/agnostic/WAVDecoder.h
+++ b/dom/media/platforms/agnostic/WAVDecoder.h
@@ -11,38 +11,39 @@
 #include "mp4_demuxer/ByteReader.h"
 
 namespace mozilla {
 
 class WaveDataDecoder : public MediaDataDecoder
 {
 public:
   WaveDataDecoder(const AudioInfo& aConfig,
-                  FlushableTaskQueue* aTaskQueue,
+                  TaskQueue* aTaskQueue,
                   MediaDataDecoderCallback* aCallback);
 
+  // Return true if mimetype is Wave
+  static bool IsWave(const nsACString& aMimeType);
+
+private:
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   const char* GetDescriptionName() const override
   {
     return "wave audio decoder";
   }
 
-  // Return true if mimetype is Wave
-  static bool IsWave(const nsACString& aMimeType);
-
-private:
-  void Decode (MediaRawData* aSample);
-  bool DoDecode (MediaRawData* aSample);
-  void DoDrain ();
+  void ProcessDecode(MediaRawData* aSample);
+  bool DoDecode(MediaRawData* aSample);
+  void ProcessDrain();
 
   const AudioInfo& mInfo;
-  RefPtr<FlushableTaskQueue> mTaskQueue;
+  const RefPtr<TaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
+  Atomic<bool> mIsFlushing;
 
   int64_t mFrames;
 };
 
 } // namespace mozilla
-#endif
\ No newline at end of file
+#endif
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -148,36 +148,39 @@ WMFVideoMFTManager::GetMediaSubtypeGUID(
     case VP8: return MFVideoFormat_VP80;
     case VP9: return MFVideoFormat_VP90;
     default: return GUID_NULL;
   };
 }
 
 struct BlacklistedD3D11DLL
 {
+  constexpr
+  BlacklistedD3D11DLL(LPCWSTR aName, DWORD a, DWORD b, DWORD c, DWORD d)
+    : name(aName), ms((a << 16) | b), ls((c << 16) | d)
+  {}
   LPCWSTR name;
   DWORD ms;
   DWORD ls;
 };
-#define DLLVER(a, b, c, d) \
-        ((DWORD(a) << 16) | DWORD(b)),  ((DWORD(c) << 16) | DWORD(d))
-static const BlacklistedD3D11DLL sBlacklistedD3D11DLL[] =
+static constexpr BlacklistedD3D11DLL sBlacklistedD3D11DLL[] =
 {
   // Keep same DLL names together.
-  { L"igd10umd32.dll", DLLVER(9,17,10,2857) },
-  { L"tosqep.dll", DLLVER(1,2,15,526) },
-  { L"tosqep.dll", DLLVER(1,1,12,201) },
-  { L"tosqep.dll", DLLVER(1,0,11,318) },
-  { L"tosqep.dll", DLLVER(1,0,11,215) },
-  { L"tosqep64.dll", DLLVER(1,1,12,201) },
-  { L"tosqep64.dll", DLLVER(1,0,11,215) },
+  BlacklistedD3D11DLL(L"igd10umd32.dll", 9,17,10,2857),
+  BlacklistedD3D11DLL(L"isonyvideoprocessor.dll", 4,1,2247,8090),
+  BlacklistedD3D11DLL(L"isonyvideoprocessor.dll", 4,1,2153,6200),
+  BlacklistedD3D11DLL(L"tosqep.dll", 1,2,15,526),
+  BlacklistedD3D11DLL(L"tosqep.dll", 1,1,12,201),
+  BlacklistedD3D11DLL(L"tosqep.dll", 1,0,11,318),
+  BlacklistedD3D11DLL(L"tosqep.dll", 1,0,11,215),
+  BlacklistedD3D11DLL(L"tosqep64.dll", 1,1,12,201),
+  BlacklistedD3D11DLL(L"tosqep64.dll", 1,0,11,215),
   // Keep this last.
-  { nullptr, 0u, 0u }
+  BlacklistedD3D11DLL(nullptr, 0,0,0,0)
 };
-#undef DLLVER
 
 // If a blacklisted DLL is found, return its information, otherwise nullptr.
 static const BlacklistedD3D11DLL*
 IsD3D11DLLBlacklisted()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
   // Cache the result, so we only check DLLs once per browser run.
   static const BlacklistedD3D11DLL* sAlreadySearched = nullptr;
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -364,17 +364,18 @@ SapiService::Speak(const nsAString& aTex
   long rate = aRate != 0 ? static_cast<long>(10 * log10(aRate) / log10(3)) : 0;
   if (FAILED(spVoice->SetRate(rate))) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the pitch using xml
   nsAutoString xml;
   xml.AssignLiteral("<pitch absmiddle=\"");
-  xml.AppendFloat(aPitch * 10.0f - 10.0f);
+  // absmiddle doesn't allow float type
+  xml.AppendInt(static_cast<int32_t>(aPitch * 10.0f - 10.0f));
   xml.AppendLiteral("\">");
   uint32_t textOffset = xml.Length();
 
   const char16_t* escapedText =
     nsEscapeHTML2(aText.BeginReading(), aText.Length());
   if (!escapedText) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -82,17 +82,17 @@ public:
     if (!docShell) {
         return NS_OK;
     }
     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
     NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
 
     // now get the document from sameTypeRoot
-    nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
+    nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
     NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
 
     // Get eventSink and the current security state from the docShell
     nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
     NS_ASSERTION(eventSink, "No eventSink from docShell.");
     nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
     NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
     uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
@@ -759,17 +759,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
 
     if (!httpsParentExists) {
       *aDecision = nsIContentPolicy::ACCEPT;
       return NS_OK;
     }
   }
 
   // Get the root document from the sameTypeRoot
-  nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
+  nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
   NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
 
   // Get eventSink and the current security state from the docShell
   nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
   NS_ASSERTION(eventSink, "No eventSink from docShell.");
   nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
   NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
   uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
--- a/dom/speakermanager/SpeakerManager.cpp
+++ b/dom/speakermanager/SpeakerManager.cpp
@@ -100,17 +100,17 @@ SpeakerManager::DispatchSimpleEvent(cons
   }
 }
 
 void
 SpeakerManager::Init(nsPIDOMWindowInner* aWindow)
 {
   BindToOwner(aWindow);
 
-  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
+  nsCOMPtr<nsIDocShell> docshell = GetOwner()->GetDocShell();
   NS_ENSURE_TRUE_VOID(docshell);
   docshell->GetIsActive(&mVisible);
 
   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
   NS_ENSURE_TRUE_VOID(target);
 
   target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
                                  this,
--- a/dom/webidl/HTMLButtonElement.webidl
+++ b/dom/webidl/HTMLButtonElement.webidl
@@ -36,13 +36,14 @@ interface HTMLButtonElement : HTMLElemen
            attribute DOMString value;
 // Not yet implemented:
 //           attribute HTMLMenuElement? menu;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // Not yet implemented:
 //  readonly attribute NodeList labels;
 };
--- a/dom/webidl/HTMLFieldSetElement.webidl
+++ b/dom/webidl/HTMLFieldSetElement.webidl
@@ -22,11 +22,12 @@ interface HTMLFieldSetElement : HTMLElem
 
   readonly attribute HTMLCollection elements;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
 
   boolean checkValidity();
+  boolean reportValidity();
 
   void setCustomValidity(DOMString error);
 };
--- a/dom/webidl/HTMLFormElement.webidl
+++ b/dom/webidl/HTMLFormElement.webidl
@@ -42,12 +42,13 @@ interface HTMLFormElement : HTMLElement 
   getter Element (unsigned long index);
   // TODO this should be: getter (RadioNodeList or HTMLInputElement or HTMLImageElement) (DOMString name);
   getter nsISupports (DOMString name);
 
   [Throws]
   void submit();
   void reset();
   boolean checkValidity();
+  boolean reportValidity();
 
   [Pref="dom.forms.requestAutocomplete"]
   void requestAutocomplete();
 };
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -101,16 +101,17 @@ interface HTMLInputElement : HTMLElement
 
   [Pure]
   readonly attribute boolean willValidate;
   [Pure]
   readonly attribute ValidityState validity;
   [GetterThrows]
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   // Bug 850365 readonly attribute NodeList labels;
 
   void select();
 
   [Throws]
            // TODO: unsigned vs signed
--- a/dom/webidl/HTMLObjectElement.webidl
+++ b/dom/webidl/HTMLObjectElement.webidl
@@ -35,16 +35,17 @@ interface HTMLObjectElement : HTMLElemen
   readonly attribute Document? contentDocument;
   // Not pure: can trigger about:blank instantiation
   readonly attribute WindowProxy? contentWindow;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   [Throws]
   legacycaller any (any... arguments);
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#HTMLObjectElement-partial
 partial interface HTMLObjectElement {
--- a/dom/webidl/HTMLOutputElement.webidl
+++ b/dom/webidl/HTMLOutputElement.webidl
@@ -25,13 +25,14 @@ interface HTMLOutputElement : HTMLElemen
            attribute DOMString defaultValue;
   [SetterThrows, Pure]
            attribute DOMString value;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // Not yet implemented (bug 556743).
 //  readonly attribute NodeList labels;
 };
--- a/dom/webidl/HTMLSelectElement.webidl
+++ b/dom/webidl/HTMLSelectElement.webidl
@@ -45,15 +45,16 @@ interface HTMLSelectElement : HTMLElemen
            attribute long selectedIndex;
   [Pure]
            attribute DOMString value;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
 // NYI:  readonly attribute NodeList labels;
 
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=20720
   void remove();
 };
--- a/dom/webidl/HTMLTextAreaElement.webidl
+++ b/dom/webidl/HTMLTextAreaElement.webidl
@@ -47,16 +47,17 @@ interface HTMLTextAreaElement : HTMLElem
            attribute DOMString defaultValue;
   [TreatNullAs=EmptyString] attribute DOMString value;
   readonly attribute unsigned long textLength;
 
   readonly attribute boolean willValidate;
   readonly attribute ValidityState validity;
   readonly attribute DOMString validationMessage;
   boolean checkValidity();
+  boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   // readonly attribute NodeList labels;
 
   void select();
   [Throws]
            attribute unsigned long selectionStart;
   [Throws]
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -905,17 +905,17 @@ private:
     }
 
     // If the top-level worker is a dedicated worker and has a window, and the
     // window has a docshell, the caching behavior of this worker should match
     // that of that docshell.
     if (topWorkerPrivate->IsDedicatedWorker()) {
       nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
       if (window) {
-        nsCOMPtr<nsIDocShell> docShell = do_GetInterface(window);
+        nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
         if (docShell) {
           nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       }
     }
 
     // If we are loading a script for a ServiceWorker then we must not
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -703,17 +703,17 @@ nsXBLWindowKeyHandler::WalkHandlersAndEx
     // <command> is non-empty).
     nsCOMPtr<Element> commandElement;
     if (!GetElementForHandler(handler, getter_AddRefs(commandElement))) {
       continue;
     }
 
     bool isReserved = false;
     if (commandElement) {
-      if (!IsExecutableElement(commandElement)) {
+      if (aExecute && !IsExecutableElement(commandElement)) {
         continue;
       }
 
       isReserved =
         commandElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::reserved,
                                     nsGkAtoms::_true, eCaseMatters);
       if (aOutReservedForChrome) {
         *aOutReservedForChrome = isReserved;
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -377,16 +377,17 @@ XULDocument::StartDocumentLoad(const cha
             }
         }
     }
     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
     // we'll possibly need to reset our content type afterwards.
     mStillWalking = true;
     mMayStartLayout = false;
     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
+    mFullscreenEnabled = true;
 
     mChannel = aChannel;
 
     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
     nsresult rv =
         NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
     NS_ENSURE_SUCCESS(rv, rv);
     
--- a/editor/composer/nsComposerCommandsUpdater.cpp
+++ b/editor/composer/nsComposerCommandsUpdater.cpp
@@ -349,17 +349,17 @@ nsComposerCommandsUpdater::SelectionIsCo
   return false;
 }
 
 already_AddRefed<nsPICommandUpdater>
 nsComposerCommandsUpdater::GetCommandUpdater()
 {
   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   NS_ENSURE_TRUE(docShell, nullptr);
-  nsCOMPtr<nsICommandManager> manager = do_GetInterface(docShell);
+  nsCOMPtr<nsICommandManager> manager = docShell->GetCommandManager();
   nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
   return updater.forget();
 }
 
 #if 0
 #pragma mark -
 #endif
 
--- a/editor/composer/nsComposerDocumentCommands.cpp
+++ b/editor/composer/nsComposerDocumentCommands.cpp
@@ -395,17 +395,17 @@ nsSetDocumentStateCommand::GetCommandSta
  *                                 //    or "obs_documentWillBeDestroyed"
           in wstring     aData );  // ignored (set to "command_status_changed")
  *
  *  3. Add the observer by:
  *       commandManager.addObserver(observeobject, obs_documentCreated);
  *  4. In the appropriate location in editorSession, editor, or commands code,
  *     trigger the notification of this observer by something like:
  *
- *  nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(mDocShell);
+ *  nsCOMPtr<nsICommandManager> commandManager = mDocShell->GetCommandManager();
  *  nsCOMPtr<nsPICommandUpdater> commandUpdater = do_QueryInterface(commandManager);
  *  NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
  *    commandUpdater->CommandStatusChanged(obs_documentCreated);
  *
  *  5. Use GetCommandStateParams() to obtain state information
  *     e.g., any creation state codes when creating an editor are
  *     supplied for "obs_documentCreated" command in the
  *     "state_data" param's value
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -815,17 +815,17 @@ nsEditingSession::OnLocationChange(nsIWe
 
   doc->SetDocumentURI(aURI);
 
   // Notify the location-changed observer that
   //  the document URL has changed
   nsIDocShell *docShell = piWindow->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
-  nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShell);
+  nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
   nsCOMPtr<nsPICommandUpdater> commandUpdater =
                                   do_QueryInterface(commandManager);
   NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
 
   return commandUpdater->CommandStatusChanged("obs_documentLocationChanged");
 }
 
 /*---------------------------------------------------------------------------
--- a/extensions/auth/nsAuthSambaNTLM.cpp
+++ b/extensions/auth/nsAuthSambaNTLM.cpp
@@ -251,17 +251,18 @@ nsAuthSambaNTLM::GetNextToken(const void
     free(encoded);
     request.Append('\n');
 
     if (!WriteString(mToChildFD, request))
         return NS_ERROR_FAILURE;
     nsCString line;
     if (!ReadLine(mFromChildFD, line))
         return NS_ERROR_FAILURE;
-    if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK "))) {
+    if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK ")) &&
+        !StringBeginsWith(line, NS_LITERAL_CSTRING("AF "))) {
         // Something went wrong. Perhaps no credentials are accessible.
         return NS_ERROR_FAILURE;
     }
     uint8_t* buf = ExtractMessage(line, outTokenLen);
     if (!buf)
         return NS_ERROR_FAILURE;
     *outToken = nsMemory::Clone(buf, *outTokenLen);
     free(buf);
new file mode 100644
--- /dev/null
+++ b/gfx/2d/CGTextDrawing.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+#ifndef _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
+#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
+
+#include <ApplicationServices/ApplicationServices.h>
+#include "nsDebug.h"
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "PathCG.h"
+#include <dlfcn.h>
+
+// This is used when we explicitly need CG to draw text to support things such
+// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
+// are really only to enable Skia to support drawing text in those situations.
+
+namespace mozilla {
+namespace gfx {
+
+typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
+
+static CGContextSetFontSmoothingBackgroundColorFunc
+GetCGContextSetFontSmoothingBackgroundColorFunc()
+{
+  static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
+  static bool lookedUpFunc = false;
+  if (!lookedUpFunc) {
+    func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
+      RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
+    lookedUpFunc = true;
+  }
+  return func;
+}
+
+static CGColorRef
+ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
+{
+  CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+  return CGColorCreate(aColorSpace, components);
+}
+
+static bool
+SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
+                                const GlyphRenderingOptions* aRenderingOptions)
+{
+  if (aRenderingOptions) {
+    Color fontSmoothingBackgroundColor =
+      static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+    if (fontSmoothingBackgroundColor.a > 0) {
+      CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
+        GetCGContextSetFontSmoothingBackgroundColorFunc();
+      if (setFontSmoothingBGColorFunc) {
+        CGColorRef color = ColorToCGColor(aColorSpace, fontSmoothingBackgroundColor);
+        setFontSmoothingBGColorFunc(aCGContext, color);
+        CGColorRelease(color);
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+// Font rendering with a non-transparent font smoothing background color
+// can leave pixels in our buffer where the rgb components exceed the alpha
+// component. When this happens we need to clean up the data afterwards.
+// The purpose of this is probably the following: Correct compositing of
+// subpixel anti-aliased fonts on transparent backgrounds requires
+// different alpha values per RGB component. Usually, premultiplied color
+// values are derived by multiplying all components with the same per-pixel
+// alpha value. However, if you multiply each component with a *different*
+// alpha, and set the alpha component of the pixel to, say, the average
+// of the alpha values that you used during the premultiplication of the
+// RGB components, you can trick OVER compositing into doing a simplified
+// form of component alpha compositing. (You just need to make sure to
+// clamp the components of the result pixel to [0,255] afterwards.)
+static void
+EnsureValidPremultipliedData(CGContextRef aContext,
+                             CGRect aTextBounds = CGRectInfinite)
+{
+  if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
+      CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
+    return;
+  }
+
+  uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
+  CGRect bitmapBounds = CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
+  int stride = CGBitmapContextGetBytesPerRow(aContext);
+
+  CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
+  int startX = bounds.origin.x;
+  int endX = startX + bounds.size.width;
+  MOZ_ASSERT(endX <= bitmapBounds.size.width);
+
+
+  // CGRect assume that our origin is the bottom left.
+  // The data assumes that the origin is the top left.
+  // Have to switch the Y axis so that our coordinates are correct
+  int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
+  int endY = startY + bounds.size.height;
+  MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));
+
+  for (int y = startY; y < endY; y++) {
+    for (int x = startX; x < endX; x++) {
+      int i = y * stride + x * 4;
+      uint8_t a = bitmapData[i + 3];
+
+      bitmapData[i + 0] = std::min(a, bitmapData[i+0]);
+      bitmapData[i + 1] = std::min(a, bitmapData[i+1]);
+      bitmapData[i + 2] = std::min(a, bitmapData[i+2]);
+    }
+  }
+}
+
+static CGRect
+ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
+{
+  CGFloat x1, x2, y1, y2;
+  if (count < 1)
+    return CGRectZero;
+
+  x1 = bboxes[0].origin.x + positions[0].x;
+  x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
+  y1 = bboxes[0].origin.y + positions[0].y;
+  y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
+
+  // accumulate max and minimum coordinates
+  for (int i = 1; i < count; i++) {
+    x1 = std::min(x1, bboxes[i].origin.x + positions[i].x);
+    y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
+    x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
+    y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
+  }
+
+  CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
+  return extents;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -16,16 +16,17 @@
 #include <vector>
 #include <algorithm>
 #include "MacIOSurface.h"
 #include "FilterNodeSoftware.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Types.h" // for decltype
 #include "mozilla/Vector.h"
+#include "CGTextDrawing.h"
 
 using namespace std;
 
 //CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
 
 // A private API that Cairo has been using for a long time
 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
 
@@ -410,22 +411,16 @@ DrawTargetCG::DrawFilter(FilterNode *aNo
                          const Rect &aSourceRect,
                          const Point &aDestPoint,
                          const DrawOptions &aOptions)
 {
   FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
   filter->Draw(this, aSourceRect, aDestPoint, aOptions);
 }
 
-static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
-{
-  CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
-  return CGColorCreate(aColorSpace, components);
-}
-
 class GradientStopsCG : public GradientStops
 {
   public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG)
 
   GradientStopsCG(CGColorSpaceRef aColorSpace,
                   const std::vector<GradientStop>& aStops,
                   ExtendMode aExtendMode)
@@ -1446,94 +1441,44 @@ DrawTargetCG::Fill(const Path *aPath, co
     else
       CGContextFillPath(cg);
   }
 
   fixer.Fix(this);
   CGContextRestoreGState(mCg);
 }
 
-CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
-{
-  CGFloat x1, x2, y1, y2;
-  if (count < 1)
-    return CGRectZero;
-
-  x1 = bboxes[0].origin.x + positions[0].x;
-  x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
-  y1 = bboxes[0].origin.y + positions[0].y;
-  y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
-
-  // accumulate max and minimum coordinates
-  for (int i = 1; i < count; i++) {
-    x1 = min(x1, bboxes[i].origin.x + positions[i].x);
-    y1 = min(y1, bboxes[i].origin.y + positions[i].y);
-    x2 = max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
-    y2 = max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
-  }
-
-  CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
-  return extents;
-}
-
-typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
-
-static CGContextSetFontSmoothingBackgroundColorFunc
-GetCGContextSetFontSmoothingBackgroundColorFunc()
-{
-  static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
-  static bool lookedUpFunc = false;
-  if (!lookedUpFunc) {
-    func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
-      RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
-    lookedUpFunc = true;
-  }
-  return func;
-}
-
 void
 DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions,
                          const GlyphRenderingOptions *aGlyphRenderingOptions)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
   MarkChanged();
 
   assert(aBuffer.mNumGlyphs);
   CGContextSaveGState(mCg);
 
-  if (aGlyphRenderingOptions && aGlyphRenderingOptions->GetType() == FontType::MAC) {
-    Color fontSmoothingBackgroundColor =
-      static_cast<const GlyphRenderingOptionsCG*>(aGlyphRenderingOptions)->FontSmoothingBackgroundColor();
-    if (fontSmoothingBackgroundColor.a > 0) {
-      CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
-        GetCGContextSetFontSmoothingBackgroundColorFunc();
-      if (setFontSmoothingBGColorFunc) {
-        CGColorRef color = ColorToCGColor(mColorSpace, fontSmoothingBackgroundColor);
-        setFontSmoothingBGColorFunc(mCg, color);
-        CGColorRelease(color);
-
-        // Font rendering with a non-transparent font smoothing background color
-        // can leave pixels in our buffer where the rgb components exceed the alpha
-        // component. When this happens we need to clean up the data afterwards.
-        // The purpose of this is probably the following: Correct compositing of
-        // subpixel anti-aliased fonts on transparent backgrounds requires
-        // different alpha values per RGB component. Usually, premultiplied color
-        // values are derived by multiplying all components with the same per-pixel
-        // alpha value. However, if you multiply each component with a *different*
-        // alpha, and set the alpha component of the pixel to, say, the average
-        // of the alpha values that you used during the premultiplication of the
-        // RGB components, you can trick OVER compositing into doing a simplified
-        // form of component alpha compositing. (You just need to make sure to
-        // clamp the components of the result pixel to [0,255] afterwards.)
-        mMayContainInvalidPremultipliedData = true;
-      }
-    }
+  if (SetFontSmoothingBackgroundColor(mCg, mColorSpace, aGlyphRenderingOptions)) {
+    // Font rendering with a non-transparent font smoothing background color
+    // can leave pixels in our buffer where the rgb components exceed the alpha
+    // component. When this happens we need to clean up the data afterwards.
+    // The purpose of this is probably the following: Correct compositing of
+    // subpixel anti-aliased fonts on transparent backgrounds requires
+    // different alpha values per RGB component. Usually, premultiplied color
+    // values are derived by multiplying all components with the same per-pixel
+    // alpha value. However, if you multiply each component with a *different*
+    // alpha, and set the alpha component of the pixel to, say, the average
+    // of the alpha values that you used during the premultiplication of the
+    // RGB components, you can trick OVER compositing into doing a simplified
+    // form of component alpha compositing. (You just need to make sure to
+    // clamp the components of the result pixel to [0,255] afterwards.)
+    mMayContainInvalidPremultipliedData = true;
   }
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
   CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
   if (MOZ2D_ERROR_IF(!cg)) {
     return;
   }
@@ -1555,28 +1500,31 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo
   if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
       !positions.resizeUninitialized(aBuffer.mNumGlyphs)) {
     gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
     return;
   }
 
   // Handle the flip
   CGContextScaleCTM(cg, 1, -1);
+
+  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+    glyphs[i] = aBuffer.mGlyphs[i].mIndex;
+
+    // Negative Y axis since glyph positions assume top left is at (0, 0)
+    // whereas CG is bottom left.
+    positions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
+                               -aBuffer.mGlyphs[i].mPosition.y);
+  }
+
   // CGContextSetTextMatrix works differently with kCGTextClip && kCGTextFill
   // It seems that it transforms the positions with TextFill and not with TextClip
   // Therefore we'll avoid it. See also:
   // http://cgit.freedesktop.org/cairo/commit/?id=9c0d761bfcdd28d52c83d74f46dd3c709ae0fa69
 
-  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
-    glyphs[i] = aBuffer.mGlyphs[i].mIndex;
-    // XXX: CGPointMake might not be inlined
-    positions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
-                              -aBuffer.mGlyphs[i].mPosition.y);
-  }
-
   //XXX: CGContextShowGlyphsAtPositions is 10.5+ for older versions use CGContextShowGlyphsWithAdvances
   if (isGradient(aPattern)) {
     CGContextSetTextDrawingMode(cg, kCGTextClip);
     CGRect extents;
     if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
       CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
       CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
                                       glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
@@ -1806,47 +1754,16 @@ DrawTargetCG::Init(BackendType aType,
     // reading back the surface. This should trigger something equivilent
     // to glClear.
     ClearRect(Rect(0, 0, mSize.width, mSize.height));
   }
 
   return true;
 }
 
-static void
-EnsureValidPremultipliedData(CGContextRef aContext)
-{
-  if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
-      CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
-    return;
-  }
-
-  uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
-  int w = CGBitmapContextGetWidth(aContext);
-  int h = CGBitmapContextGetHeight(aContext);
-  int stride = CGBitmapContextGetBytesPerRow(aContext);
-  for (int y = 0; y < h; y++) {
-    for (int x = 0; x < w; x++) {
-      int i = y * stride + x * 4;
-      uint8_t a = bitmapData[i + 3];
-
-      // Clamp rgb components to the alpha component.
-      if (bitmapData[i + 0] > a) {
-        bitmapData[i + 0] = a;
-      }
-      if (bitmapData[i + 1] > a) {
-        bitmapData[i + 1] = a;
-      }
-      if (bitmapData[i + 2] > a) {
-        bitmapData[i + 2] = a;
-      }
-    }
-  }
-}
-
 void
 DrawTargetCG::Flush()
 {
 #ifdef MOZ_WIDGET_COCOA
   if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
     CGContextFlush(mCg);
   } else if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP &&
              mMayContainInvalidPremultipliedData) {
@@ -2003,30 +1920,29 @@ DrawTargetCG::PushClipRect(const Rect &a
 void
 DrawTargetCG::PushClip(const Path *aPath)
 {
   if (MOZ2D_ERROR_IF(!mCg)) {
     return;
   }
 
   CGContextSaveGState(mCg);
-
-  CGContextBeginPath(mCg);
   assert(aPath->GetBackendType() == BackendType::COREGRAPHICS);
 
   const PathCG *cgPath = static_cast<const PathCG*>(aPath);
 
   // Weirdly, CoreGraphics clips empty paths as all shown
   // but emtpy rects as all clipped.  We detect this situation and
   // workaround it appropriately
   if (CGPathIsEmpty(cgPath->GetPath())) {
-    // XXX: should we return here?
     CGContextClipToRect(mCg, CGRectZero);
+    return;
   }
 
+  CGContextBeginPath(mCg);
 
   /* We go through a bit of trouble to temporarilly set the transform
    * while we add the path. XXX: this could be improved if we keep
    * the CTM as resident state on the DrawTarget. */
   CGContextSaveGState(mCg);
   CGContextAddPath(mCg, cgPath->GetPath());
   CGContextRestoreGState(mCg);
 
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -25,16 +25,24 @@
 
 #ifdef USE_SKIA_GPU
 #include "GLDefs.h"
 #include "skia/include/gpu/SkGr.h"
 #include "skia/include/gpu/GrContext.h"
 #include "skia/include/gpu/gl/GrGLInterface.h"
 #endif
 
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#include "mozilla/Vector.h"
+#include "ScaledFontMac.h"
+#include "DrawTargetCG.h"
+#include "CGTextDrawing.h"
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 class GradientStopsSkia : public GradientStops
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia)
   GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops, ExtendMode aExtendMode)
@@ -121,21 +129,34 @@ GetBitmapForSurface(SourceSurface* aSurf
     gfxDebug() << "Failed installing pixels on Skia bitmap for temporary surface";
   }
 
   return bitmap;
 }
 
 DrawTargetSkia::DrawTargetSkia()
   : mSnapshot(nullptr)
+#ifdef MOZ_WIDGET_COCOA
+  , mCG(nullptr)
+  , mColorSpace(CGColorSpaceCreateDeviceRGB())
+  , mCanvasData(nullptr)
+  , mCGSize(0, 0)
+#endif
 {
 }
 
 DrawTargetSkia::~DrawTargetSkia()
 {
+#ifdef MOZ_WIDGET_COCOA
+  if (mCG) {
+    CGColorSpaceRelease(mColorSpace);
+    CGContextRelease(mCG);
+    mCG = nullptr;
+  }
+#endif
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetSkia::Snapshot()
 {
   RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
   if (!snapshot) {
     snapshot = new SourceSurfaceSkia();
@@ -589,32 +610,409 @@ DrawTargetSkia::ShouldLCDRenderText(Font
       default:
         // TODO: Figure out what to do for the other platforms.
         return false;
     }
   }
   return (aAntialiasMode == AntialiasMode::SUBPIXEL);
 }
 
+#ifdef MOZ_WIDGET_COCOA
+class CGClipApply : public SkCanvas::ClipVisitor {
+public:
+  explicit CGClipApply(CGContextRef aCGContext)
+    : mCG(aCGContext) {}
+  void clipRect(const SkRect& aRect, SkRegion::Op op, bool antialias) override {
+    CGRect rect = CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height());
+    CGContextClipToRect(mCG, rect);
+  }
+
+  void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) override {
+    SkPath path;
+    path.addRRect(rrect);
+    clipPath(path, op, antialias);
+  }
+
+  void clipPath(const SkPath& aPath, SkRegion::Op, bool antialias) override {
+    SkPath::Iter iter(aPath, true);
+    SkPoint source[4];
+    SkPath::Verb verb;
+    RefPtr<PathBuilderCG> pathBuilder =
+        new PathBuilderCG(GetFillRule(aPath.getFillType()));
+
+    while ((verb = iter.next(source)) != SkPath::kDone_Verb) {
+      switch (verb) {
+      case SkPath::kMove_Verb:
+      {
+        SkPoint dest = source[0];
+        pathBuilder->MoveTo(Point(dest.fX, dest.fY));
+        break;
+      }
+      case SkPath::kLine_Verb:
+      {
+        // The first point should be the end point of whatever
+        // verb we got to get here.
+        SkPoint second = source[1];
+        pathBuilder->LineTo(Point(second.fX, second.fY));
+        break;
+      }
+      case SkPath::kQuad_Verb:
+      {
+        SkPoint second = source[1];
+        SkPoint third = source[2];
+
+        pathBuilder->QuadraticBezierTo(Point(second.fX, second.fY),
+                                       Point(third.fX, third.fY));
+        break;
+      }
+      case SkPath::kCubic_Verb:
+      {
+        SkPoint second = source[1];
+        SkPoint third = source[2];
+        SkPoint fourth = source[2];
+
+        pathBuilder->BezierTo(Point(second.fX, second.fY),
+                              Point(third.fX, third.fY),
+                              Point(fourth.fX, fourth.fY));
+        break;
+      }
+      case SkPath::kClose_Verb:
+      {
+        pathBuilder->Close();
+        break;
+      }
+      default:
+      {
+        SkDEBUGFAIL("unknown verb");
+        break;
+      }
+      } // end switch
+    } // end while
+
+    RefPtr<Path> path = pathBuilder->Finish();
+    PathCG* cgPath = static_cast<PathCG*>(path.get());
+
+    // Weirdly, CoreGraphics clips empty paths as all shown
+    // but empty rects as all clipped.  We detect this situation and
+    // workaround it appropriately
+    if (CGPathIsEmpty(cgPath->GetPath())) {
+      CGContextClipToRect(mCG, CGRectZero);
+      return;
+    }
+
+    CGContextBeginPath(mCG);
+    CGContextAddPath(mCG, cgPath->GetPath());
+
+    if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD) {
+      CGContextEOClip(mCG);
+    } else {
+      CGContextClip(mCG);
+    }
+  }
+
+private:
+  CGContextRef mCG;
+};
+
+/***
+ * We have to do a lot of work to draw glyphs with CG because
+ * CG assumes that the origin of rects are in the bottom left
+ * while every other DrawTarget assumes the top left is the origin.
+ * This means we have to transform the CGContext to have rects
+ * actually be applied in top left fashion. We do this by:
+ *
+ * 1) Translating the context up by the height of the canvas
+ * 2) Flipping the context by the Y axis so it's upside down.
+ *
+ * These two transforms put the origin in the top left.
+ * Transforms are better understood thinking about them from right to left order (mathematically).
+ *
+ * Consider a point we want to draw at (0, 10) in normal cartesian planes with
+ * a box of (100, 100). in CG terms, this would be at (0, 10).
+ * Positive Y values point up.
+ * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
+ * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in DrawTarget
+ * terms should end up at (0, 90). How does this work with the current transforms?
+ *
+ * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian coordinates
+ * of (0, 10). The first flip of the Y axis puts the point now at (0, -10);
+ * Next, we translate the context up by the size of the canvas (Positive Y values go up in CG
+ * coordinates but down in our draw target coordinates). Since our canvas size is (100, 100),
+ * the resulting coordinate becomes (0, 90), which is what we expect from our DrawTarget code.
+ * These two transforms put the CG context equal to what every other DrawTarget expects.
+ *
+ * Next, we need two more transforms for actual text. IF we left the transforms as is,
+ * the text would be drawn upside down, so we need another flip of the Y axis
+ * to draw the text right side up. However, with only the flip, the text would be drawn
+ * in the wrong place. Thus we also have to invert the Y position of the glyphs to get them
+ * in the right place.
+ *
+ * Thus we have the following transforms:
+ * 1) Translation of the context up
+ * 2) Flipping the context around the Y axis
+ * 3) Flipping the context around the Y axis
+ * 4) Inverting the Y position of each glyph
+ *
+ * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
+ * of DrawTargetSkia between (2) and (3).
+ *
+ * Consider the example letter P, drawn at (0, 20) in CG coordinates in a (100, 100) rect.
+ * Again, going right to left of the transforms. We'd get:
+ *
+ * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
+ * 2) The letter P upside down (b) at (0, 20) due to the second flip
+ * 3) The letter P right side up at (0, -20) due to the first flip
+ * 4) The letter P right side up at (0, 80) due to the translation
+ *
+ * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top left.
+ */
+static bool
+SetupCGContext(DrawTargetSkia* aDT,
+               CGContextRef aCGContext,
+               RefPtrSkia<SkCanvas> aCanvas)
+{
+  // DrawTarget expects the origin to be at the top left, but CG
+  // expects it to be at the bottom left. Transform to set the origin to
+  // the top left. Have to set this before we do anything else.
+  // This is transform (1) up top
+  CGContextTranslateCTM(aCGContext, 0, aDT->GetSize().height);
+
+  // Transform (2) from the comments.
+  CGContextScaleCTM(aCGContext, 1, -1);
+
+  // Want to apply clips BEFORE the transform since the transform
+  // will apply to the clips we apply.
+  // CGClipApply applies clips in device space, so it would be a mistake
+  // to transform these clips.
+  CGClipApply clipApply(aCGContext);
+  aCanvas->replayClips(&clipApply);
+
+  CGContextConcatCTM(aCGContext, GfxMatrixToCGAffineTransform(aDT->GetTransform()));
+  return true;
+}
+
+static bool
+SetupCGGlyphs(CGContextRef aCGContext,
+              const GlyphBuffer& aBuffer,
+              Vector<CGGlyph,32>& aGlyphs,
+              Vector<CGPoint,32>& aPositions)
+{
+  // Flip again so we draw text in right side up. Transform (3) from the top
+  CGContextScaleCTM(aCGContext, 1, -1);
+
+  if (!aGlyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
+      !aPositions.resizeUninitialized(aBuffer.mNumGlyphs)) {
+    gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
+    return false;
+  }
+
+  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+    aGlyphs[i] = aBuffer.mGlyphs[i].mIndex;
+
+    // Flip the y coordinates so that text ends up in the right spot after the (3) flip
+    // Inversion from (4) in the comments.
+    aPositions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
+                                -aBuffer.mGlyphs[i].mPosition.y);
+  }
+
+  return true;
+}
+// End long comment about transforms. SetupCGContext and SetupCGGlyphs should stay
+// next to each other.
+
+// The context returned from this method will have the origin
+// in the top left and will hvae applied all the neccessary clips
+// and transforms to the CGContext. See the comment above
+// SetupCGContext.
+CGContextRef
+DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
+{
+  int32_t stride;
+  SurfaceFormat format;
+  IntSize size;
+
+  uint8_t* aSurfaceData = nullptr;
+  if (!LockBits(&aSurfaceData, &size, &stride, &format)) {
+    NS_WARNING("Could not lock skia bits to wrap CG around");
+    return nullptr;
+  }
+
+  if ((aSurfaceData == mCanvasData) && mCG && (mCGSize == size)) {
+    // If our canvas data still points to the same data,
+    // we can reuse the CG Context
+    CGContextSaveGState(mCG);
+    CGContextSetAlpha(mCG, aOptions.mAlpha);
+    SetupCGContext(this, mCG, mCanvas);
+    return mCG;
+  }
+
+  if (mCG) {
+    // Release the old CG context since it's no longer valid.
+    CGContextRelease(mCG);
+  }
+
+  mCanvasData = aSurfaceData;
+  mCGSize = size;
+
+  mCG = CGBitmapContextCreateWithData(mCanvasData,
+                                      mCGSize.width,
+                                      mCGSize.height,
+                                      8, /* bits per component */
+                                      stride,
+                                      mColorSpace,
+                                      kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst,
+                                      NULL, /* Callback when released */
+                                      NULL);
+  if (!mCG) {
+    ReleaseBits(mCanvasData);
+    NS_WARNING("Could not create bitmap around skia data\n");
+    return nullptr;
+  }
+
+  CGContextSetAlpha(mCG, aOptions.mAlpha);
+  CGContextSetShouldAntialias(mCG, aOptions.mAntialiasMode != AntialiasMode::NONE);
+  CGContextSetShouldSmoothFonts(mCG, true);
+  CGContextSetTextDrawingMode(mCG, kCGTextFill);
+  CGContextSaveGState(mCG);
+  SetupCGContext(this, mCG, mCanvas);
+  return mCG;
+}
+
+void
+DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext)
+{
+  MOZ_ASSERT(aCGContext == mCG);
+  ReleaseBits(mCanvasData);
+  CGContextRestoreGState(aCGContext);
+}
+
+static void
+SetFontColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace, const Pattern& aPattern)
+{
+  const Color& color = static_cast<const ColorPattern&>(aPattern).mColor;
+  CGColorRef textColor = ColorToCGColor(aColorSpace, color);
+  CGContextSetFillColorWithColor(aCGContext, textColor);
+  CGColorRelease(textColor);
+}
+
+/***
+ * We need this to support subpixel AA text on OS X in two cases:
+ * text in DrawTargets that are not opaque and text over vibrant backgrounds.
+ * Skia normally doesn't support subpixel AA text on transparent backgrounds.
+ * To get around this, we have to wrap the Skia bytes with a CGContext and ask
+ * CG to draw the text.
+ * In vibrancy cases, we have to use a private API,
+ * CGContextSetFontSmoothingBackgroundColor, which sets the expected
+ * background color the text will draw onto so that CG can render the text
+ * properly. After that, we have to go back and fixup the pixels
+ * such that their alpha values are correct.
+ */
+bool
+DrawTargetSkia::FillGlyphsWithCG(ScaledFont *aFont,
+                                 const GlyphBuffer &aBuffer,
+                                 const Pattern &aPattern,
+                                 const DrawOptions &aOptions,
+                                 const GlyphRenderingOptions *aRenderingOptions)
+{
+  MOZ_ASSERT(aFont->GetType() == FontType::MAC);
+  MOZ_ASSERT(aPattern.GetType() == PatternType::COLOR);
+
+  CGContextRef cgContext = BorrowCGContext(aOptions);
+  if (!cgContext) {
+    return false;
+  }
+
+  Vector<CGGlyph,32> glyphs;
+  Vector<CGPoint,32> positions;
+  if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
+    return false;
+  }
+
+  SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
+  SetFontColor(cgContext, mColorSpace, aPattern);
+
+  ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
+  if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
+    ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
+                                       positions.begin(),
+                                       aBuffer.mNumGlyphs, cgContext);
+  } else {
+    CGContextSetFont(cgContext, macFont->mFont);
+    CGContextSetFontSize(cgContext, macFont->mSize);
+    CGContextShowGlyphsAtPositions(cgContext, glyphs.begin(), positions.begin(),
+                                   aBuffer.mNumGlyphs);
+  }
+
+  // Calculate the area of the text we just drew
+  CGRect *bboxes = new CGRect[aBuffer.mNumGlyphs];
+  CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontDefaultOrientation,
+                                  glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
+  CGRect extents = ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
+  delete[] bboxes;
+
+  CGAffineTransform cgTransform = CGContextGetCTM(cgContext);
+  extents = CGRectApplyAffineTransform(extents, cgTransform);
+
+  // Have to round it out to ensure we fully cover all pixels
+  Rect rect(extents.origin.x, extents.origin.y, extents.size.width, extents.size.height);
+  rect.RoundOut();
+  extents = CGRectMake(rect.x, rect.y, rect.width, rect.height);
+
+  EnsureValidPremultipliedData(cgContext, extents);
+
+  ReturnCGContext(cgContext);
+  return true;
+}
+
+static bool
+HasFontSmoothingBackgroundColor(const GlyphRenderingOptions* aRenderingOptions)
+{
+  // This should generally only be true if we have a popup context menu
+  if (aRenderingOptions && aRenderingOptions->GetType() == FontType::MAC) {
+    Color fontSmoothingBackgroundColor =
+      static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
+    return fontSmoothingBackgroundColor.a > 0;
+  }
+
+  return false;
+}
+
+static bool
+ShouldUseCGToFillGlyphs(const GlyphRenderingOptions* aOptions, const Pattern& aPattern)
+{
+  return HasFontSmoothingBackgroundColor(aOptions) &&
+          aPattern.GetType() == PatternType::COLOR;
+}
+
+#endif
+
 void
 DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
                            const GlyphBuffer &aBuffer,
                            const Pattern &aPattern,
                            const DrawOptions &aOptions,
                            const GlyphRenderingOptions *aRenderingOptions)
 {
   if (aFont->GetType() != FontType::MAC &&
       aFont->GetType() != FontType::SKIA &&
       aFont->GetType() != FontType::GDI &&
       aFont->GetType() != FontType::DWRITE) {
     return;
   }
 
   MarkChanged();
 
+#ifdef MOZ_WIDGET_COCOA
+  if (ShouldUseCGToFillGlyphs(aRenderingOptions, aPattern)) {
+    if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions, aRenderingOptions)) {
+      return;
+    }
+  }
+#endif
+
   ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
 
   AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
   paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
   paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
   paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
   bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aOptions.mAntialiasMode);
@@ -1224,16 +1622,21 @@ DrawTargetSkia::PushLayer(bool aOpaque, 
   SkCanvas::SaveLayerRec saveRec(aBounds.IsEmpty() ? nullptr : &bounds,
                                  &paint,
                                  backdrop.get(),
                                  aOpaque ? SkCanvas::kIsOpaque_SaveLayerFlag : 0);
 
   mCanvas->saveLayer(saveRec);
 
   SetPermitSubpixelAA(aOpaque);
+
+#ifdef MOZ_WIDGET_COCOA
+  CGContextRelease(mCG);
+  mCG = nullptr;
+#endif
 }
 
 void
 DrawTargetSkia::PopLayer()
 {
   MarkChanged();
 
   MOZ_ASSERT(mPushedLayers.size());
@@ -1291,16 +1694,21 @@ DrawTargetSkia::PopLayer()
     }
   } else {
     mCanvas->restore();
   }
 
   SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
 
   mPushedLayers.pop_back();
+
+#ifdef MOZ_WIDGET_COCOA
+  CGContextRelease(mCG);
+  mCG = nullptr;
+#endif
 }
 
 already_AddRefed<GradientStops>
 DrawTargetSkia::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
 {
   std::vector<GradientStop> stops;
   stops.resize(aNumStops);
   for (uint32_t i = 0; i < aNumStops; i++) {
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -10,16 +10,20 @@
 
 #include "2D.h"
 #include "HelpersSkia.h"
 #include "Rect.h"
 #include "PathSkia.h"
 #include <sstream>
 #include <vector>
 
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 class SourceSurfaceSkia;
 
 class DrawTargetSkia : public DrawTarget
 {
 public:
@@ -69,16 +73,26 @@ public:
                           const DrawOptions &aOptions = DrawOptions()) override;
   virtual void Stroke(const Path *aPath,
                       const Pattern &aPattern,
                       const StrokeOptions &aStrokeOptions = StrokeOptions(),
                       const DrawOptions &aOptions = DrawOptions()) override;
   virtual void Fill(const Path *aPath,
                     const Pattern &aPattern,
                     const DrawOptions &aOptions = DrawOptions()) override;
+#ifdef MOZ_WIDGET_COCOA
+  CGContextRef BorrowCGContext(const DrawOptions &aOptions);
+  void ReturnCGContext(CGContextRef);
+  bool FillGlyphsWithCG(ScaledFont *aFont,
+                        const GlyphBuffer &aBuffer,
+                        const Pattern &aPattern,
+                        const DrawOptions &aOptions = DrawOptions(),
+                        const GlyphRenderingOptions *aRenderingOptions = nullptr);
+#endif
+
   virtual void FillGlyphs(ScaledFont *aFont,
                           const GlyphBuffer &aBuffer,
                           const Pattern &aPattern,
                           const DrawOptions &aOptions = DrawOptions(),
                           const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
   virtual void Mask(const Pattern &aSource,
                     const Pattern &aMask,
                     const DrawOptions &aOptions = DrawOptions()) override;
@@ -172,14 +186,21 @@ private:
 
 #ifdef USE_SKIA_GPU
   RefPtrSkia<GrContext> mGrContext;
 #endif
 
   IntSize mSize;
   RefPtrSkia<SkCanvas> mCanvas;
   SourceSurfaceSkia* mSnapshot;
+
+#ifdef MOZ_WIDGET_COCOA
+  CGContextRef mCG;
+  CGColorSpaceRef mColorSpace;
+  uint8_t* mCanvasData;
+  IntSize mCGSize;
+#endif
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _MOZILLA_GFX_SOURCESURFACESKIA_H
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -11,16 +11,17 @@
 #include "skia/include/effects/SkDashPathEffect.h"
 #include "skia/include/core/SkShader.h"
 #ifdef USE_SKIA_GPU
 #include "skia/include/gpu/GrTypes.h"
 #endif
 #include "mozilla/Assertions.h"
 #include <vector>
 #include "RefPtrSkia.h"
+#include "nsDebug.h"
 
 namespace mozilla {
 namespace gfx {
 
 static inline SkColorType
 GfxFormatToSkiaColorType(SurfaceFormat format)
 {
   switch (format)
@@ -348,12 +349,30 @@ GfxHintingToSkiaHinting(FontHinting aHin
     case FontHinting::NORMAL:
       return SkPaint::kNormal_Hinting;
     case FontHinting::FULL:
       return SkPaint::kFull_Hinting;
   }
   return SkPaint::kNormal_Hinting;
 }
 
+static inline FillRule GetFillRule(SkPath::FillType aFillType)
+{
+  switch (aFillType)
+  {
+  case SkPath::kWinding_FillType:
+    return FillRule::FILL_WINDING;
+  case SkPath::kEvenOdd_FillType:
+    return FillRule::FILL_EVEN_ODD;
+  case SkPath::kInverseWinding_FillType:
+  case SkPath::kInverseEvenOdd_FillType:
+  default:
+    NS_WARNING("Unsupported fill type\n");
+    break;
+  }
+
+  return FillRule::FILL_EVEN_ODD;
+}
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_HELPERSSKIA_H_ */
--- a/gfx/2d/PathD2D.h
+++ b/gfx/2d/PathD2D.h
@@ -42,16 +42,18 @@ public:
   virtual Point CurrentPoint() const;
 
   virtual already_AddRefed<Path> Finish();
 
   virtual BackendType GetBackendType() const { return mBackendType; }
 
   ID2D1GeometrySink *GetSink() { return mSink; }
 
+  bool IsFigureActive() const { return mFigureActive; }
+
 private:
   friend class PathD2D;
 
   void EnsureActive(const Point &aPoint);
 
   RefPtr<ID2D1GeometrySink> mSink;
   RefPtr<ID2D1PathGeometry> mGeometry;
 
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -163,16 +163,20 @@ ScaledFontDWrite::CopyGlyphsToBuilder(co
   if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) {
     ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
     return;
   }
 
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(aBuilder);
 
+  if (pathBuilderD2D->IsFigureActive()) {
+    gfxCriticalNote << "Attempting to copy glyphs to PathBuilderD2D with active figure.";
+  }
+
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 }
 
 void
 ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
 {
   std::vector<UINT16> indices;
   std::vector<FLOAT> advances;
@@ -183,19 +187,23 @@ ScaledFontDWrite::CopyGlyphsToSink(const
 
   memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     indices[i] = aBuffer.mGlyphs[i].mIndex;
     offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
     offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
   }
 
-  mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
-                                &offsets.front(), aBuffer.mNumGlyphs,
-                                FALSE, FALSE, aSink);
+  HRESULT hr =
+    mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
+                                  &offsets.front(), aBuffer.mNumGlyphs,
+                                  FALSE, FALSE, aSink);
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to copy glyphs to geometry sink. Code: " << hexa(hr);
+  }
 }
 
 bool
 ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
 {
   UINT32 fileCount = 0;
   mFontFace->GetFiles(&fileCount, nullptr);
 
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -32,16 +32,17 @@ public:
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
 private:
   friend class DrawTargetCG;
+  friend class DrawTargetSkia;
   CGFontRef mFont;
   CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null
 
   typedef void (CTFontDrawGlyphsFuncT)(CTFontRef,
                                        const CGGlyph[], const CGPoint[],
                                        size_t, CGContextRef);
 
   static bool sSymbolLookupDone;
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/CompositorSession.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorSession.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "base/process_util.h"
+
+namespace mozilla {
+namespace layers {
+
+class InProcessCompositorSession final : public CompositorSession
+{
+public:
+  InProcessCompositorSession(
+    widget::CompositorWidgetProxy* aWidgetProxy,
+    ClientLayerManager* aLayerManager,
+    CSSToLayoutDeviceScale aScale,
+    bool aUseAPZ,
+    bool aUseExternalSurfaceSize,
+    int aSurfaceWidth, int aSurfaceHeight);
+
+  CompositorBridgeParent* GetInProcessBridge() const override;
+  void SetContentController(GeckoContentController* aController) override;
+  uint64_t RootLayerTreeId() const override;
+  APZCTreeManager* GetAPZCTreeManager() const override;
+  void Shutdown() override;
+
+private:
+  RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+};
+
+already_AddRefed<CompositorSession>
+CompositorSession::CreateTopLevel(widget::CompositorWidgetProxy* aWidgetProxy,
+                                  ClientLayerManager* aLayerManager,
+                                  CSSToLayoutDeviceScale aScale,
+                                  bool aUseAPZ,
+                                  bool aUseExternalSurfaceSize,
+                                  int aSurfaceWidth, int aSurfaceHeight)
+{
+  RefPtr<InProcessCompositorSession> session = new InProcessCompositorSession(
+    aWidgetProxy,
+    aLayerManager,
+    aScale,
+    aUseAPZ,
+    aUseExternalSurfaceSize,
+    aSurfaceWidth, aSurfaceHeight);
+  return session.forget();
+}
+
+CompositorSession::CompositorSession()
+{
+}
+
+CompositorSession::~CompositorSession()
+{
+}
+
+CompositorBridgeChild*
+CompositorSession::GetCompositorBridgeChild()
+{
+  return mCompositorBridgeChild;
+}
+
+InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidgetProxy* aWidgetProxy,
+                                                       ClientLayerManager* aLayerManager,
+                                                       CSSToLayoutDeviceScale aScale,
+                                                       bool aUseAPZ,
+                                                       bool aUseExternalSurfaceSize,
+                                                       int aSurfaceWidth, int aSurfaceHeight)
+{
+  mCompositorBridgeParent = new CompositorBridgeParent(
+    aWidgetProxy,
+    aScale,
+    aUseAPZ,
+    aUseExternalSurfaceSize,
+    aSurfaceWidth,
+    aSurfaceHeight);
+  mCompositorBridgeChild = new CompositorBridgeChild(aLayerManager);
+  mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent);
+  mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId());
+}
+
+CompositorBridgeParent*
+InProcessCompositorSession::GetInProcessBridge() const
+{
+  return mCompositorBridgeParent;
+}
+
+void
+InProcessCompositorSession::SetContentController(GeckoContentController* aController)
+{
+  mCompositorBridgeParent->SetControllerForLayerTree(RootLayerTreeId(), aController);
+}
+
+uint64_t
+InProcessCompositorSession::RootLayerTreeId() const
+{
+  return mCompositorBridgeParent->RootLayerTreeId();
+}
+
+APZCTreeManager*
+InProcessCompositorSession::GetAPZCTreeManager() const
+{
+  return mCompositorBridgeParent->GetAPZCTreeManager(RootLayerTreeId());
+}
+
+void
+InProcessCompositorSession::Shutdown()
+{
+  // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in
+  // ClientLayerManager destructor. See bug 1133426.
+  RefPtr<CompositorBridgeChild> compositorChild = mCompositorBridgeChild;
+  RefPtr<CompositorBridgeParent> compositorParent = mCompositorBridgeParent;
+  mCompositorBridgeChild->Destroy();
+  mCompositorBridgeChild = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/ipc/CompositorSession.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* 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/. */
+#ifndef _include_mozilla_gfx_ipc_CompositorSession_h_
+#define _include_mozilla_gfx_ipc_CompositorSession_h_
+
+#include "base/basictypes.h"
+#include "Units.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace widget {
+class CompositorWidgetProxy;
+} // namespace widget
+namespace layers {
+
+class GeckoContentController;
+class APZCTreeManager;
+class CompositorBridgeParent;
+class CompositorBridgeChild;
+class ClientLayerManager;
+
+// A CompositorSession provides access to a compositor without exposing whether
+// or not it's in-process or out-of-process.
+class CompositorSession
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession)
+
+  static already_AddRefed<CompositorSession> CreateTopLevel(
+    widget::CompositorWidgetProxy* aWidgetProxy,
+    ClientLayerManager* aLayerManager,
+    CSSToLayoutDeviceScale aScale,
+    bool aUseAPZ,
+    bool aUseExternalSurfaceSize,
+    int aSurfaceWidth, int aSurfaceHeight);
+
+  virtual void Shutdown() = 0;
+
+  // This returns a CompositorBridgeParent if the compositor resides in the same process.
+  virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
+
+  // Set the GeckoContentController for the root of the layer tree.
+  virtual void SetContentController(GeckoContentController* aController) = 0;
+
+  // Return the id of the root layer tree.
+  virtual uint64_t RootLayerTreeId() const = 0;
+
+  // Return the Async Pan/Zoom Tree Manager for this compositor.
+  virtual APZCTreeManager* GetAPZCTreeManager() const = 0;
+
+  // Return the child end of the compositor IPC bridge.
+  CompositorBridgeChild* GetCompositorBridgeChild();
+
+protected:
+  CompositorSession();
+  virtual ~CompositorSession();
+
+protected:
+  RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(CompositorSession);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // _include_mozilla_gfx_ipc_CompositorSession_h_
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -8,27 +8,32 @@ EXPORTS.mozilla += [
     'D3DMessageUtils.h',
     'GfxMessageUtils.h'
 ]
 
 EXPORTS.mozilla.gfx += [
     'SharedDIB.h',
 ]
 
+EXPORTS.mozilla.layers += [
+    'CompositorSession.h',
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXPORTS.mozilla.gfx += [
         'SharedDIBSurface.h',
         'SharedDIBWin.h',
     ]
     UNIFIED_SOURCES += [
         'SharedDIBSurface.cpp',
         'SharedDIBWin.cpp',
     ]
 
 UNIFIED_SOURCES += [
+    'CompositorSession.cpp',
     'D3DMessageUtils.cpp',
     'SharedDIB.cpp',
 ]
 
 IPDL_SOURCES = [
     'GraphicsMessages.ipdlh',
 ]
 
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -30,16 +30,17 @@ namespace mozilla {
 namespace layers {
 
 class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage
 {
 public:
   BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
     : RecyclingPlanarYCbCrImage(aRecycleBin)
     , mScaleHint(aScaleHint)
+    , mStride(0)
     , mDelayedConversion(false)
   {
     SetOffscreenFormat(aOffscreenFormat);
   }
 
   ~BasicPlanarYCbCrImage()
   {
     if (mDecodedBuffer) {
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -14,18 +14,16 @@
 #include "mozilla/Hal.h"                // for hal::SetCurrentThreadPriority()
 #include "mozilla/HalTypes.h"           // for hal::THREAD_PRIORITY_COMPOSITOR
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
 #include "mozilla/layers/CompositableTransactionParent.h"
-#include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent
-#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -9,17 +9,17 @@
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "CompositableTransactionParent.h"
 #include "ImageContainerParent.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
-#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 
 class MessageLoop;
 
 namespace base {
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -68,16 +68,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gonk',
     'qt',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
+    DEFINES['SK_FONT_HOST_USE_SYSTEM_SETTINGS'] = 1
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -604,16 +604,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gonk',
     'qt',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
+    DEFINES['SK_FONT_HOST_USE_SYSTEM_SETTINGS'] = 1
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
@@ -261,17 +261,17 @@ void DWriteFontTypeface::onFilterRec(SkS
     rec->fFlags &= ~flagsWeDontSupport;
 
     SkPaint::Hinting h = rec->getHinting();
     // DirectWrite does not provide for hinting hints.
     h = SkPaint::kSlight_Hinting;
     rec->setHinting(h);
 
 #if SK_FONT_HOST_USE_SYSTEM_SETTINGS
-    IDWriteFactory* factory = get_dwrite_factory();
+    IDWriteFactory* factory = sk_get_dwrite_factory();
     if (factory != nullptr) {
         SkTScopedComPtr<IDWriteRenderingParams> defaultRenderingParams;
         if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) {
             float gamma = defaultRenderingParams->GetGamma();
             rec->setDeviceGamma(gamma);
             rec->setPaintGamma(gamma);
 
             rec->setContrast(defaultRenderingParams->GetEnhancedContrast());
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -494,21 +494,17 @@ gfxWindowsPlatform::HandleDeviceReset()
 static const BackendType SOFTWARE_BACKEND = BackendType::CAIRO;
 
 void
 gfxWindowsPlatform::UpdateBackendPrefs()
 {
   uint32_t canvasMask = BackendTypeBit(SOFTWARE_BACKEND);
   uint32_t contentMask = BackendTypeBit(SOFTWARE_BACKEND);
   BackendType defaultBackend = SOFTWARE_BACKEND;
-  if (gfxConfig::IsEnabled(Feature::DIRECT2D)) {
-    MOZ_RELEASE_ASSERT(Factory::GetDirect3D11Device());
-    MOZ_RELEASE_ASSERT(Factory::GetD2D1Device());
-    MOZ_RELEASE_ASSERT(Factory::SupportsD2D1());
-
+  if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::GetD2D1Device()) {
     contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     defaultBackend = BackendType::DIRECT2D1_1;
   } else {
     canvasMask |= BackendTypeBit(BackendType::SKIA);
   }
   contentMask |= BackendTypeBit(BackendType::SKIA);
   InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend);
@@ -1966,16 +1962,21 @@ gfxWindowsPlatform::InitializeConfig()
 
 void
 gfxWindowsPlatform::InitializeD3D9Config()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   FeatureState& d3d9 = gfxConfig::GetFeature(Feature::D3D9_COMPOSITING);
 
+  if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+    d3d9.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled");
+    return;
+  }
+
   if (!IsVistaOrLater()) {
     d3d9.EnableByDefault();
   } else {
     d3d9.SetDefaultFromPref(
       gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
       true,
       gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
 
@@ -2523,25 +2524,28 @@ gfxWindowsPlatform::ResetD3D11Devices()
 
 void
 gfxWindowsPlatform::InitializeD2DConfig()
 {
   FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
 
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D requires Direct3D 11 compositing");
-  } else if (!IsVistaOrLater()) {
+    return;
+  }
+  if (!IsVistaOrLater()) {
     d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D is not available on Windows XP");
-  } else {
-    d2d1.SetDefaultFromPref(
-      gfxPrefs::GetDirect2DDisabledPrefName(),
-      false,
-      gfxPrefs::GetDirect2DDisabledPrefDefault());
+    return;
   }
 
+  d2d1.SetDefaultFromPref(
+    gfxPrefs::GetDirect2DDisabledPrefName(),
+    false,
+    gfxPrefs::GetDirect2DDisabledPrefDefault());
+
   nsCString message;
   if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message)) {
     d2d1.Disable(FeatureStatus::Blacklisted, message.get());
   }
 
   if (!d2d1.IsEnabled() && gfxPrefs::Direct2DForceEnabled()) {
     d2d1.UserForceEnable("Force-enabled via user-preference");
   }
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -19,25 +19,38 @@
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 
 #if defined(MOZ_CRASHREPORTER) && defined(XP_WIN)
 #include "aclapi.h"
 #include "sddl.h"
+
+#include "mozilla/TypeTraits.h"
 #endif
 
 using namespace IPC;
 
 using base::GetCurrentProcId;
 using base::ProcessHandle;
 using base::ProcessId;
 
 namespace mozilla {
+
+#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN)
+// Generate RAII classes for LPTSTR and PSECURITY_DESCRIPTOR.
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedLPTStr, \
+                                          RemovePointer<LPTSTR>::Type, \
+                                          ::LocalFree)
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPSecurityDescriptor, \
+                                          RemovePointer<PSECURITY_DESCRIPTOR>::Type, \
+                                          ::LocalFree)
+#endif
+
 namespace ipc {
 
 ProtocolCloneContext::ProtocolCloneContext()
   : mNeckoParent(nullptr)
 {}
 
 ProtocolCloneContext::~ProtocolCloneContext()
 {}
@@ -288,18 +301,25 @@ bool DuplicateHandle(HANDLE aSourceHandl
                                                        aTargetHandle,
                                                        aDesiredAccess,
                                                        aOptions)) {
     return true;
   }
 #endif
 
   // Finally, see if we already have access to the process.
-  ScopedProcessHandle targetProcess;
-  if (!base::OpenProcessHandle(aTargetProcessId, &targetProcess.rwget())) {
+  ScopedProcessHandle targetProcess(OpenProcess(PROCESS_DUP_HANDLE,
+                                                FALSE,
+                                                aTargetProcessId));
+  if (!targetProcess) {
+#ifdef MOZ_CRASH_REPORTER
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCTransportFailureReason"),
+      NS_LITERAL_CSTRING("Failed to open target process."));
+#endif
     return false;
   }
 
   return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
                               targetProcess, aTargetHandle,
                               aDesiredAccess, FALSE, aOptions);
 }
 #endif
@@ -320,17 +340,18 @@ AnnotateSystemError()
       nsPrintfCString("%lld", error));
   }
 }
 
 void
 AnnotateProcessInformation(base::ProcessId aPid)
 {
 #ifdef XP_WIN
-  HANDLE processHandle = OpenProcess(READ_CONTROL|PROCESS_QUERY_INFORMATION, FALSE, aPid);
+  ScopedProcessHandle processHandle(
+    OpenProcess(READ_CONTROL|PROCESS_QUERY_INFORMATION, FALSE, aPid));
   if (!processHandle) {
     CrashReporter::AnnotateCrashReport(
       NS_LITERAL_CSTRING("IPCExtraSystemError"),
       nsPrintfCString("Failed to get information of process %d, error(%d)",
                       aPid,
                       ::GetLastError()));
     return;
   }
@@ -349,61 +370,57 @@ AnnotateProcessInformation(base::Process
     CrashReporter::AnnotateCrashReport(
       NS_LITERAL_CSTRING("IPCExtraSystemError"),
       nsPrintfCString("Process %d is not alive. Exit code: %d",
                       aPid,
                       exitCode));
     return;
   }
 
-  PSECURITY_DESCRIPTOR secDesc = nullptr;
+  ScopedPSecurityDescriptor secDesc(nullptr);
   PSID ownerSid = nullptr;
   DWORD rv = ::GetSecurityInfo(processHandle,
                                SE_KERNEL_OBJECT,
                                OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
                                &ownerSid,
                                nullptr,
                                nullptr,
                                nullptr,
-                               &secDesc);
+                               &secDesc.rwget());
   if (rv != ERROR_SUCCESS) {
     // GetSecurityInfo() failed.
     CrashReporter::AnnotateCrashReport(
       NS_LITERAL_CSTRING("IPCExtraSystemError"),
       nsPrintfCString("Failed to get security information of process %d,"
                       " error(%d)",
                       aPid,
                       rv));
     return;
   }
 
-  LPTSTR ownerSidStr = nullptr;
+  ScopedLPTStr ownerSidStr(nullptr);
   nsString annotation{};
   annotation.AppendLiteral("Owner: ");
-  if (::ConvertSidToStringSid(ownerSid, &ownerSidStr)) {
-    annotation.Append(ownerSidStr);
+  if (::ConvertSidToStringSid(ownerSid, &ownerSidStr.rwget())) {
+    annotation.Append(ownerSidStr.get());
   }
-  ::LocalFree(ownerSidStr);
 
-  LPTSTR secDescStr = nullptr;
+  ScopedLPTStr secDescStr(nullptr);
   annotation.AppendLiteral(", Security Descriptor: ");
   if (::ConvertSecurityDescriptorToStringSecurityDescriptor(secDesc,
                                                             SDDL_REVISION_1,
                                                             DACL_SECURITY_INFORMATION,
-                                                            &secDescStr,
+                                                            &secDescStr.rwget(),
                                                             nullptr)) {
-    annotation.Append(secDescStr);
+    annotation.Append(secDescStr.get());
   }
 
   CrashReporter::AnnotateCrashReport(
     NS_LITERAL_CSTRING("IPCExtraSystemError"),
     NS_ConvertUTF16toUTF8(annotation));
-
-  ::LocalFree(secDescStr);
-  ::LocalFree(secDesc);
 #endif
 }
 #endif
 
 void
 LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid,
                       const char* aContextDescription,
                       const char* aMessageDescription,
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -905,19 +905,25 @@ class NumLit
 {
   public:
     enum Which {
         Fixnum,
         NegativeInt,
         BigUnsigned,
         Double,
         Float,
+        Int8x16,
+        Int16x8,
         Int32x4,
+        Uint8x16,
+        Uint16x8,
         Uint32x4,
         Float32x4,
+        Bool8x16,
+        Bool16x8,
         Bool32x4,
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
     union {
         Value scalar_;
@@ -960,19 +966,22 @@ class NumLit
         return float(u.scalar_.toDouble());
     }
 
     Value scalarValue() const {
         MOZ_ASSERT(which_ != OutOfRangeInt);
         return u.scalar_;
     }
 
-    bool isSimd() const {
-        return which_ == Int32x4 || which_ == Uint32x4 || which_ == Float32x4 ||
-               which_ == Bool32x4;
+    bool isSimd() const
+    {
+        return which_ == Int8x16 || which_ == Uint8x16 || which_ == Int16x8 ||
+            which_ == Uint16x8 || which_ == Int32x4 || which_ == Uint32x4 ||
+            which_ == Float32x4 || which_ == Bool8x16 || which_ == Bool16x8 ||
+            which_ == Bool32x4;
     }
 
     const SimdConstant& simdValue() const {
         MOZ_ASSERT(isSimd());
         return u.simd_;
     }
 
     bool valid() const {
@@ -985,44 +994,61 @@ class NumLit
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
           case NumLit::BigUnsigned:
             return toInt32() == 0;
           case NumLit::Double:
             return toDouble() == 0.0 && !IsNegativeZero(toDouble());
           case NumLit::Float:
             return toFloat() == 0.f && !IsNegativeZero(toFloat());
+          case NumLit::Int8x16:
+          case NumLit::Uint8x16:
+          case NumLit::Bool8x16:
+            return simdValue() == SimdConstant::SplatX16(0);
+          case NumLit::Int16x8:
+          case NumLit::Uint16x8:
+          case NumLit::Bool16x8:
+            return simdValue() == SimdConstant::SplatX8(0);
           case NumLit::Int32x4:
           case NumLit::Uint32x4:
+          case NumLit::Bool32x4:
             return simdValue() == SimdConstant::SplatX4(0);
           case NumLit::Float32x4:
             return simdValue() == SimdConstant::SplatX4(0.f);
-          case NumLit::Bool32x4:
-            return simdValue() == SimdConstant::SplatX4(0);
           case NumLit::OutOfRangeInt:
             MOZ_CRASH("can't be here because of valid() check above");
         }
         return false;
     }
 
     Val value() const {
         switch (which_) {
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
           case NumLit::BigUnsigned:
             return Val(toUint32());
           case NumLit::Float:
             return Val(toFloat());
           case NumLit::Double:
             return Val(toDouble());
+          case NumLit::Int8x16:
+          case NumLit::Uint8x16:
+            return Val(simdValue().asInt8x16());
+          case NumLit::Int16x8:
+          case NumLit::Uint16x8:
+            return Val(simdValue().asInt16x8());
           case NumLit::Int32x4:
           case NumLit::Uint32x4:
             return Val(simdValue().asInt32x4());
           case NumLit::Float32x4:
             return Val(simdValue().asFloat32x4());
+          case NumLit::Bool8x16:
+            return Val(simdValue().asInt8x16(), ValType::B8x16);
+          case NumLit::Bool16x8:
+            return Val(simdValue().asInt16x8(), ValType::B16x8);
           case NumLit::Bool32x4:
             return Val(simdValue().asInt32x4(), ValType::B32x4);
           case NumLit::OutOfRangeInt:;
         }
         MOZ_CRASH("bad literal");
     }
 };
 
@@ -1044,19 +1070,25 @@ class Type
 {
   public:
     enum Which {
         Fixnum = NumLit::Fixnum,
         Signed = NumLit::NegativeInt,
         Unsigned = NumLit::BigUnsigned,
         DoubleLit = NumLit::Double,
         Float = NumLit::Float,
+        Int8x16 = NumLit::Int8x16,
+        Int16x8 = NumLit::Int16x8,
         Int32x4 = NumLit::Int32x4,
+        Uint8x16 = NumLit::Uint8x16,
+        Uint16x8 = NumLit::Uint16x8,
         Uint32x4 = NumLit::Uint32x4,
         Float32x4 = NumLit::Float32x4,
+        Bool8x16 = NumLit::Bool8x16,
+        Bool16x8 = NumLit::Bool16x8,
         Bool32x4 = NumLit::Bool32x4,
         Double,
         MaybeDouble,
         MaybeFloat,
         Floatish,
         Int,
         Intish,
         Void
@@ -1065,19 +1097,25 @@ class Type
   private:
     Which which_;
 
   public:
     Type() = default;
     MOZ_IMPLICIT Type(Which w) : which_(w) {}
     MOZ_IMPLICIT Type(SimdType type) {
         switch (type) {
+          case SimdType::Int8x16:   which_ = Int8x16;   return;
+          case SimdType::Int16x8:   which_ = Int16x8;   return;
           case SimdType::Int32x4:   which_ = Int32x4;   return;
+          case SimdType::Uint8x16:  which_ = Uint8x16;  return;
+          case SimdType::Uint16x8:  which_ = Uint16x8;  return;
           case SimdType::Uint32x4:  which_ = Uint32x4;  return;
           case SimdType::Float32x4: which_ = Float32x4; return;
+          case SimdType::Bool8x16:  which_ = Bool8x16;  return;
+          case SimdType::Bool16x8:  which_ = Bool16x8;  return;
           case SimdType::Bool32x4:  which_ = Bool32x4;  return;
           default:                  break;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad SimdType");
     }
 
     // Map an already canonicalized Type to the return type of a function call.
     static Type ret(Type t) {
@@ -1110,19 +1148,25 @@ class Type
 
           case DoubleLit:
           case Double:
             return Double;
 
           case Void:
             return Void;
 
+          case Int8x16:
+          case Int16x8:
           case Int32x4:
+          case Uint8x16:
+          case Uint16x8:
           case Uint32x4:
           case Float32x4:
+          case Bool8x16:
+          case Bool16x8:
           case Bool32x4:
             return t;
 
           case MaybeDouble:
           case MaybeFloat:
           case Floatish:
           case Intish:
             // These types need some kind of coercion, they can't be mapped
@@ -1139,19 +1183,25 @@ class Type
 
     bool operator<=(Type rhs) const {
         switch (rhs.which_) {
           case Signed:      return isSigned();
           case Unsigned:    return isUnsigned();
           case DoubleLit:   return isDoubleLit();
           case Double:      return isDouble();
           case Float:       return isFloat();
+          case Int8x16:     return isInt8x16();
+          case Int16x8:     return isInt16x8();
           case Int32x4:     return isInt32x4();
+          case Uint8x16:    return isUint8x16();
+          case Uint16x8:    return isUint16x8();
           case Uint32x4:    return isUint32x4();
           case Float32x4:   return isFloat32x4();
+          case Bool8x16:    return isBool8x16();
+          case Bool16x8:    return isBool16x8();
           case Bool32x4:    return isBool32x4();
           case MaybeDouble: return isMaybeDouble();
           case MaybeFloat:  return isMaybeFloat();
           case Floatish:    return isFloatish();
           case Int:         return isInt();
           case Intish:      return isIntish();
           case Fixnum:      return isFixnum();
           case Void:        return isVoid();
@@ -1206,38 +1256,63 @@ class Type
     bool isVoid() const {
         return which_ == Void;
     }
 
     bool isExtern() const {
         return isDouble() || isSigned();
     }
 
+    bool isInt8x16() const {
+        return which_ == Int8x16;
+    }
+
+    bool isInt16x8() const {
+        return which_ == Int16x8;
+    }
+
     bool isInt32x4() const {
         return which_ == Int32x4;
     }
 
+    bool isUint8x16() const {
+        return which_ == Uint8x16;
+    }
+
+    bool isUint16x8() const {
+        return which_ == Uint16x8;
+    }
+
     bool isUint32x4() const {
         return which_ == Uint32x4;
     }
 
     bool isFloat32x4() const {
         return which_ == Float32x4;
     }
 
+    bool isBool8x16() const {
+        return which_ == Bool8x16;
+    }
+
+    bool isBool16x8() const {
+        return which_ == Bool16x8;
+    }
+
     bool isBool32x4() const {
         return which_ == Bool32x4;
     }
 
     bool isSimd() const {
-        return isInt32x4() || isUint32x4() || isFloat32x4() || isBool32x4();
+        return isInt8x16() || isInt16x8() || isInt32x4() || isUint8x16() || isUint16x8() ||
+               isUint32x4() || isFloat32x4() || isBool8x16() || isBool16x8() || isBool32x4();
     }
 
     bool isUnsignedSimd() const {
-        return isUint32x4();
+        return isUint8x16() || isUint16x8() || isUint32x4();
     }
 
     // Check if this is one of the valid types for a function argument.
     bool isArgType() const {
         return isInt() || isFloat() || isDouble() || (isSimd() && !isUnsignedSimd());
     }
 
     // Check if this is one of the valid types for a function return value.
@@ -1272,19 +1347,25 @@ class Type
 
     // Convert this canonical type to a wasm::ExprType.
     ExprType canonicalToExprType() const {
         switch (which()) {
           case Int:       return ExprType::I32;
           case Float:     return ExprType::F32;
           case Double:    return ExprType::F64;
           case Void:      return ExprType::Void;
+          case Uint8x16:
+          case Int8x16:   return ExprType::I8x16;
+          case Uint16x8:
+          case Int16x8:   return ExprType::I16x8;
           case Uint32x4:
           case Int32x4:   return ExprType::I32x4;
           case Float32x4: return ExprType::F32x4;
+          case Bool8x16:  return ExprType::B8x16;
+          case Bool16x8:  return ExprType::B16x8;
           case Bool32x4:  return ExprType::B32x4;
           default:        MOZ_CRASH("Need canonical type");
         }
     }
 
     // Convert this canonical type to a wasm::ValType.
     ValType canonicalToValType() const {
         return NonVoidToValType(canonicalToExprType());
@@ -1298,19 +1379,25 @@ class Type
           case Float:       return "float";
           case Floatish:    return "floatish";
           case MaybeFloat:  return "float?";
           case Fixnum:      return "fixnum";
           case Int:         return "int";
           case Signed:      return "signed";
           case Unsigned:    return "unsigned";
           case Intish:      return "intish";
+          case Int8x16:     return "int8x16";
+          case Int16x8:     return "int16x8";
           case Int32x4:     return "int32x4";
+          case Uint8x16:    return "uint8x16";
+          case Uint16x8:    return "uint16x8";
           case Uint32x4:    return "uint32x4";
           case Float32x4:   return "float32x4";
+          case Bool8x16:    return "bool8x16";
+          case Bool16x8:    return "bool16x8";
           case Bool32x4:    return "bool32x4";
           case Void:        return "void";
         }
         MOZ_CRASH("Invalid Type");
     }
 };
 
 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
@@ -2355,18 +2442,24 @@ IsSimdLiteral(ModuleValidator& m, ParseN
     ParseNode* arg = CallArgList(pn);
     unsigned length = GetSimdLanes(type);
     for (unsigned i = 0; i < length; i++) {
         if (!IsNumericLiteral(m, arg))
             return false;
 
         uint32_t _;
         switch (type) {
+          case SimdType::Int8x16:
+          case SimdType::Int16x8:
           case SimdType::Int32x4:
+          case SimdType::Uint8x16:
+          case SimdType::Uint16x8:
           case SimdType::Uint32x4:
+          case SimdType::Bool8x16:
+          case SimdType::Bool16x8:
           case SimdType::Bool32x4:
             if (!IsLiteralInt(m, arg, &_))
                 return false;
             break;
           case SimdType::Float32x4:
             if (!IsNumericNonFloatLiteral(arg))
                 return false;
             break;
@@ -2410,40 +2503,89 @@ ExtractNumericNonFloatValue(ParseNode* p
 
 static NumLit
 ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
 {
     MOZ_ASSERT(IsSimdLiteral(m, pn));
 
     SimdType type;
     JS_ALWAYS_TRUE(IsSimdTuple(m, pn, &type));
+    MOZ_ASSERT(CallArgListLength(pn) == GetSimdLanes(type));
 
     ParseNode* arg = CallArgList(pn);
     switch (type) {
+      case SimdType::Int8x16:
+      case SimdType::Uint8x16: {
+        MOZ_ASSERT(GetSimdLanes(type) == 16);
+        int8_t val[16];
+        for (size_t i = 0; i < 16; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = int8_t(u32);
+        }
+        MOZ_ASSERT(arg == nullptr);
+        NumLit::Which w = type == SimdType::Uint8x16 ? NumLit::Uint8x16 : NumLit::Int8x16;
+        return NumLit(w, SimdConstant::CreateX16(val));
+      }
+      case SimdType::Int16x8:
+      case SimdType::Uint16x8: {
+        MOZ_ASSERT(GetSimdLanes(type) == 8);
+        int16_t val[8];
+        for (size_t i = 0; i < 8; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = int16_t(u32);
+        }
+        MOZ_ASSERT(arg == nullptr);
+        NumLit::Which w = type == SimdType::Uint16x8 ? NumLit::Uint16x8 : NumLit::Int16x8;
+        return NumLit(w, SimdConstant::CreateX8(val));
+      }
       case SimdType::Int32x4:
       case SimdType::Uint32x4: {
         MOZ_ASSERT(GetSimdLanes(type) == 4);
         int32_t val[4];
         for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
             uint32_t u32;
             JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
             val[i] = int32_t(u32);
         }
-        MOZ_ASSERT(arg== nullptr);
+        MOZ_ASSERT(arg == nullptr);
         NumLit::Which w = type == SimdType::Uint32x4 ? NumLit::Uint32x4 : NumLit::Int32x4;
         return NumLit(w, SimdConstant::CreateX4(val));
       }
       case SimdType::Float32x4: {
         MOZ_ASSERT(GetSimdLanes(type) == 4);
         float val[4];
         for (size_t i = 0; i < 4; i++, arg = NextNode(arg))
             val[i] = float(ExtractNumericNonFloatValue(arg));
         MOZ_ASSERT(arg == nullptr);
         return NumLit(NumLit::Float32x4, SimdConstant::CreateX4(val));
       }
+      case SimdType::Bool8x16: {
+        MOZ_ASSERT(GetSimdLanes(type) == 16);
+        int8_t val[16];
+        for (size_t i = 0; i < 16; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = u32 ? -1 : 0;
+        }
+        MOZ_ASSERT(arg == nullptr);
+        return NumLit(NumLit::Bool8x16, SimdConstant::CreateX16(val));
+      }
+      case SimdType::Bool16x8: {
+        MOZ_ASSERT(GetSimdLanes(type) == 8);
+        int16_t val[8];
+        for (size_t i = 0; i < 8; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = u32 ? -1 : 0;
+        }
+        MOZ_ASSERT(arg == nullptr);
+        return NumLit(NumLit::Bool16x8, SimdConstant::CreateX8(val));
+      }
       case SimdType::Bool32x4: {
         MOZ_ASSERT(GetSimdLanes(type) == 4);
         int32_t val[4];
         for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
             uint32_t u32;
             JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
             val[i] = u32 ? -1 : 0;
         }
@@ -2466,17 +2608,16 @@ ExtractNumericLiteral(ModuleValidator& m
         // Float literals are explicitly coerced and thus the coerced literal may be
         // any valid (non-float) numeric literal.
         if (CallArgListLength(pn) == 1) {
             pn = CallArgList(pn);
             double d = ExtractNumericNonFloatValue(pn);
             return NumLit(NumLit::Float, DoubleValue(d));
         }
 
-        MOZ_ASSERT(CallArgListLength(pn) == 4);
         return ExtractSimdValue(m, pn);
     }
 
     double d = ExtractNumericNonFloatValue(pn, &pn);
 
     // The asm.js spec syntactically distinguishes any literal containing a
     // decimal point or the literal -0 as having double type.
     if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
@@ -2513,19 +2654,25 @@ IsLiteralInt(NumLit lit, uint32_t* u32)
       case NumLit::Fixnum:
       case NumLit::BigUnsigned:
       case NumLit::NegativeInt:
         *u32 = lit.toUint32();
         return true;
       case NumLit::Double:
       case NumLit::Float:
       case NumLit::OutOfRangeInt:
+      case NumLit::Int8x16:
+      case NumLit::Uint8x16:
+      case NumLit::Int16x8:
+      case NumLit::Uint16x8:
       case NumLit::Int32x4:
       case NumLit::Uint32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool8x16:
+      case NumLit::Bool16x8:
       case NumLit::Bool32x4:
         return false;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
 }
 
 static inline bool
 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32)
@@ -2534,60 +2681,114 @@ IsLiteralInt(ModuleValidator& m, ParseNo
            IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
 }
 
 /*****************************************************************************/
 
 namespace {
 
 #define CASE(TYPE, OP) case SimdOperation::Fn_##OP: return Expr::TYPE##OP;
-#define I32CASE(OP) CASE(I32x4, OP)
-#define F32CASE(OP) CASE(F32x4, OP)
-#define B32CASE(OP) CASE(B32x4, OP)
+#define I8x16CASE(OP) CASE(I8x16, OP)
+#define I16x8CASE(OP) CASE(I16x8, OP)
+#define I32x4CASE(OP) CASE(I32x4, OP)
+#define F32x4CASE(OP) CASE(F32x4, OP)
+#define B8x16CASE(OP) CASE(B8x16, OP)
+#define B16x8CASE(OP) CASE(B16x8, OP)
+#define B32x4CASE(OP) CASE(B32x4, OP)
 #define ENUMERATE(TYPE, FOR_ALL, DO)                                     \
     switch(op) {                                                         \
         case SimdOperation::Constructor: return Expr::TYPE##Constructor; \
         FOR_ALL(DO)                                                      \
         default: break;                                                  \
     }
 
 static inline Expr
 SimdToExpr(SimdType type, SimdOperation op)
 {
     switch (type) {
+      case SimdType::Uint8x16:
+        // Handle the special unsigned opcodes, then fall through to Int8x16.
+        switch(op) {
+          case SimdOperation::Fn_addSaturate:        return Expr::I8x16addSaturateU;
+          case SimdOperation::Fn_subSaturate:        return Expr::I8x16subSaturateU;
+          case SimdOperation::Fn_extractLane:        return Expr::I8x16extractLaneU;
+          case SimdOperation::Fn_shiftRightByScalar: return Expr::I8x16shiftRightByScalarU;
+          case SimdOperation::Fn_lessThan:           return Expr::I8x16lessThanU;
+          case SimdOperation::Fn_lessThanOrEqual:    return Expr::I8x16lessThanOrEqualU;
+          case SimdOperation::Fn_greaterThan:        return Expr::I8x16greaterThanU;
+          case SimdOperation::Fn_greaterThanOrEqual: return Expr::I8x16greaterThanOrEqualU;
+          case SimdOperation::Fn_fromInt8x16Bits:    return Expr::Limit;
+          default: break;
+        }
+        MOZ_FALLTHROUGH;
+      case SimdType::Int8x16:
+        // Bitcasts Uint8x16 <--> Int8x16 become noops.
+        if (op == SimdOperation::Fn_fromUint8x16Bits) return Expr::Limit;
+        ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
+        break;
+
+      case SimdType::Uint16x8:
+        // Handle the special unsigned opcodes, then fall through to Int16x8.
+        switch(op) {
+          case SimdOperation::Fn_addSaturate:        return Expr::I16x8addSaturateU;
+          case SimdOperation::Fn_subSaturate:        return Expr::I16x8subSaturateU;
+          case SimdOperation::Fn_extractLane:        return Expr::I16x8extractLaneU;
+          case SimdOperation::Fn_shiftRightByScalar: return Expr::I16x8shiftRightByScalarU;
+          case SimdOperation::Fn_lessThan:           return Expr::I16x8lessThanU;
+          case SimdOperation::Fn_lessThanOrEqual:    return Expr::I16x8lessThanOrEqualU;
+          case SimdOperation::Fn_greaterThan:        return Expr::I16x8greaterThanU;
+          case SimdOperation::Fn_greaterThanOrEqual: return Expr::I16x8greaterThanOrEqualU;
+          case SimdOperation::Fn_fromInt16x8Bits:    return Expr::Limit;
+          default: break;
+        }
+        MOZ_FALLTHROUGH;
+      case SimdType::Int16x8:
+        // Bitcasts Uint16x8 <--> Int16x8 become noops.
+        if (op == SimdOperation::Fn_fromUint16x8Bits) return Expr::Limit;
+        ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
+        break;
+
       case SimdType::Uint32x4:
         // Handle the special unsigned opcodes, then fall through to Int32x4.
         switch(op) {
           case SimdOperation::Fn_shiftRightByScalar: return Expr::I32x4shiftRightByScalarU;
           case SimdOperation::Fn_lessThan:           return Expr::I32x4lessThanU;
           case SimdOperation::Fn_lessThanOrEqual:    return Expr::I32x4lessThanOrEqualU;
           case SimdOperation::Fn_greaterThan:        return Expr::I32x4greaterThanU;
           case SimdOperation::Fn_greaterThanOrEqual: return Expr::I32x4greaterThanOrEqualU;
           case SimdOperation::Fn_fromFloat32x4:      return Expr::I32x4fromFloat32x4U;
           case SimdOperation::Fn_fromInt32x4Bits:    return Expr::Limit;
           default: break;
         }
         MOZ_FALLTHROUGH;
-      case SimdType::Int32x4: {
+      case SimdType::Int32x4:
         // Bitcasts Uint32x4 <--> Int32x4 become noops.
         if (op == SimdOperation::Fn_fromUint32x4Bits) return Expr::Limit;
-        ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32CASE)
+        ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
         break;
-      }
-      case SimdType::Float32x4: {
-        ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32CASE)
+
+      case SimdType::Float32x4:
+        ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
+        break;
+
+      case SimdType::Bool8x16:
+        ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
         break;
-      }
-      case SimdType::Bool32x4: {
-        ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32CASE)
+
+      case SimdType::Bool16x8:
+        ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
         break;
-      }
+
+      case SimdType::Bool32x4:
+        ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32x4CASE)
+        break;
+
       default: break;
     }
-    MOZ_CRASH("unexpected SIMD type x operator combination");
+    MOZ_CRASH("unexpected SIMD (type, operator) combination");
 }
 
 #undef CASE
 #undef I32CASE
 #undef F32CASE
 #undef B32CASE
 #undef ENUMERATE
 
@@ -2851,23 +3052,39 @@ class MOZ_STACK_CLASS FunctionValidator
           case NumLit::BigUnsigned:
             return writeInt32Lit(lit.toInt32());
           case NumLit::Float:
             return encoder().writeExpr(Expr::F32Const) &&
                    encoder().writeFixedF32(lit.toFloat());
           case NumLit::Double:
             return encoder().writeExpr(Expr::F64Const) &&
                    encoder().writeFixedF64(lit.toDouble());
+          case NumLit::Int8x16:
+          case NumLit::Uint8x16:
+            return encoder().writeExpr(Expr::I8x16Const) &&
+                   encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
+          case NumLit::Int16x8:
+          case NumLit::Uint16x8:
+            return encoder().writeExpr(Expr::I16x8Const) &&
+                   encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
           case NumLit::Int32x4:
           case NumLit::Uint32x4:
             return encoder().writeExpr(Expr::I32x4Const) &&
                    encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
           case NumLit::Float32x4:
             return encoder().writeExpr(Expr::F32x4Const) &&
                    encoder().writeFixedF32x4(lit.simdValue().asFloat32x4());
+          case NumLit::Bool8x16:
+            // Boolean vectors use the Int8x16 memory representation.
+            return encoder().writeExpr(Expr::B8x16Const) &&
+                   encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
+          case NumLit::Bool16x8:
+            // Boolean vectors use the Int16x8 memory representation.
+            return encoder().writeExpr(Expr::B16x8Const) &&
+                   encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
           case NumLit::Bool32x4:
             // Boolean vectors use the Int32x4 memory representation.
             return encoder().writeExpr(Expr::B32x4Const) &&
                    encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
           case NumLit::OutOfRangeInt:
             break;
         }
         MOZ_CRASH("unexpected literal type");
@@ -4733,18 +4950,24 @@ class CheckArgIsSubtypeOf
         return true;
     }
 };
 
 static inline Type
 SimdToCoercedScalarType(SimdType t)
 {
     switch (t) {
+      case SimdType::Int8x16:
+      case SimdType::Int16x8:
       case SimdType::Int32x4:
+      case SimdType::Uint8x16:
+      case SimdType::Uint16x8:
       case SimdType::Uint32x4:
+      case SimdType::Bool8x16:
+      case SimdType::Bool16x8:
       case SimdType::Bool32x4:
         return Type::Intish;
       case SimdType::Float32x4:
         return Type::Floatish;
       default:
         break;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
@@ -4877,19 +5100,25 @@ CheckSimdBinary(FunctionValidator& f, Pa
     *type = opType;
     return true;
 }
 
 static bool
 CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
 {
     switch (opType) {
+      case SimdType::Int8x16:
+      case SimdType::Int16x8:
       case SimdType::Int32x4:   *type = Type::Signed; break;
+      case SimdType::Uint8x16:
+      case SimdType::Uint16x8:
       case SimdType::Uint32x4:  *type = Type::Unsigned; break;
       case SimdType::Float32x4: *type = Type::Float; break;
+      case SimdType::Bool8x16:
+      case SimdType::Bool16x8:
       case SimdType::Bool32x4:  *type = Type::Int; break;
       default:                  MOZ_CRASH("unhandled simd type");
     }
 
     unsigned numArgs = CallArgListLength(call);
     if (numArgs != 2)
         return f.failf(call, "expected 2 arguments to SIMD extract, got %u", numArgs);
 
@@ -5210,24 +5439,33 @@ CheckSimdOperationCall(FunctionValidator
         return CheckSimdBinaryShift(f, call, opType, op, type);
 
       FOREACH_COMP_SIMD_OP(_CASE)
         return CheckSimdBinaryComp(f, call, opType, op, type);
 
       FOREACH_NUMERIC_SIMD_BINOP(_CASE)
       FOREACH_FLOAT_SIMD_BINOP(_CASE)
       FOREACH_BITWISE_SIMD_BINOP(_CASE)
+      FOREACH_SMINT_SIMD_BINOP(_CASE)
         return CheckSimdBinary(f, call, opType, op, type);
 #undef _CASE
 
       case SimdOperation::Fn_extractLane:
         return CheckSimdExtractLane(f, call, opType, type);
       case SimdOperation::Fn_replaceLane:
         return CheckSimdReplaceLane(f, call, opType, type);
 
+      case SimdOperation::Fn_fromInt8x16Bits:
+        return CheckSimdCast(f, call, SimdType::Int8x16, opType, op, type);
+      case SimdOperation::Fn_fromUint8x16Bits:
+        return CheckSimdCast(f, call, SimdType::Uint8x16, opType, op, type);
+      case SimdOperation::Fn_fromInt16x8Bits:
+        return CheckSimdCast(f, call, SimdType::Int16x8, opType, op, type);
+      case SimdOperation::Fn_fromUint16x8Bits:
+        return CheckSimdCast(f, call, SimdType::Uint16x8, opType, op, type);
       case SimdOperation::Fn_fromInt32x4:
       case SimdOperation::Fn_fromInt32x4Bits:
         return CheckSimdCast(f, call, SimdType::Int32x4, opType, op, type);
       case SimdOperation::Fn_fromUint32x4:
       case SimdOperation::Fn_fromUint32x4Bits:
         return CheckSimdCast(f, call, SimdType::Uint32x4, opType, op, type);
       case SimdOperation::Fn_fromFloat32x4:
       case SimdOperation::Fn_fromFloat32x4Bits:
@@ -5265,20 +5503,16 @@ CheckSimdOperationCall(FunctionValidator
 
       case SimdOperation::Fn_allTrue:
         return CheckSimdAllTrue(f, call, opType, type);
       case SimdOperation::Fn_anyTrue:
         return CheckSimdAnyTrue(f, call, opType, type);
 
       case SimdOperation::Constructor:
         MOZ_CRASH("constructors are handled in CheckSimdCtorCall");
-      case SimdOperation::Fn_fromInt8x16Bits:
-      case SimdOperation::Fn_fromInt16x8Bits:
-      case SimdOperation::Fn_fromUint8x16Bits:
-      case SimdOperation::Fn_fromUint16x8Bits:
       case SimdOperation::Fn_fromFloat64x2Bits:
         MOZ_CRASH("NYI");
     }
     MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
 }
 
 static bool
 CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
@@ -5654,19 +5888,25 @@ IsValidIntMultiplyConstant(ModuleValidat
       case NumLit::NegativeInt:
         if (abs(lit.toInt32()) < (1<<20))
             return true;
         return false;
       case NumLit::BigUnsigned:
       case NumLit::Double:
       case NumLit::Float:
       case NumLit::OutOfRangeInt:
+      case NumLit::Int8x16:
+      case NumLit::Uint8x16:
+      case NumLit::Int16x8:
+      case NumLit::Uint16x8:
       case NumLit::Int32x4:
       case NumLit::Uint32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool8x16:
+      case NumLit::Bool16x8:
       case NumLit::Bool32x4:
         return false;
     }
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
@@ -6287,19 +6527,25 @@ CheckCaseExpr(FunctionValidator& f, Pars
       case NumLit::NegativeInt:
         *value = lit.toInt32();
         break;
       case NumLit::OutOfRangeInt:
       case NumLit::BigUnsigned:
         return f.fail(caseExpr, "switch case expression out of integer range");
       case NumLit::Double:
       case NumLit::Float:
+      case NumLit::Int8x16:
+      case NumLit::Uint8x16:
+      case NumLit::Int16x8:
+      case NumLit::Uint16x8:
       case NumLit::Int32x4:
       case NumLit::Uint32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool8x16:
+      case NumLit::Bool16x8: